private void toolStripSecretButton_Click(object sender, EventArgs e) { PrioritySelector ps = TreeRoot.Current.Root as PrioritySelector; int n = 0; Logging.Write("** BotBase **"); foreach (var p in ps.Children) { // add alternating amount of spaces to the end of log entries to prevent spam filter from blocking it n = (n + 1) % 2; Logging.Write("[{0}] {1}", p.GetType(), new string(' ', n)); } //Logging.Write("** Profile Settings **"); //foreach (var kv in PB.ProfileSettings.Settings) // Logging.Write("{0} {1}", kv.Key, kv.Value); Logging.Write("** ActionSelector **"); printComposite(PB.CurrentProfile.Branch, 0); //Logging.Write("** Material List **"); //foreach (var kv in PB.MaterialList) // Logging.Write("Ingredient ID: {0} Amount required:{1}", kv.Key, kv.Value); //Logging.Write("** DataStore **"); //foreach (var kv in PB.DataStore) // Logging.Write("item ID: {0} Amount in bag/bank/ah/alts:{1}", kv.Key, kv.Value); //if (PB.CsharpStringBuilder != null) // Logging.Write(PB.CsharpStringBuilder.ToString()); }
public Composite GenerateBehaviorTree() { if (!SingularSettings.Debug) { return(new PrioritySelector(blist.Select(b => b.behavior).ToArray())); } PrioritySelector pri = new PrioritySelector(); foreach (PrioritizedBehavior pb in blist) { if (!SingularSettings.TraceHeals) { pri.AddChild(pb.behavior); } else { CallTrace ct = new CallTrace(pb.Name, pb.behavior); ct.TraceActive = true; ct.TraceEnter = false; ct.TraceExit = true; pri.AddChild(ct); } } return(pri); }
//DynamicAddNode will allow the user to add any type of node to any composite node which exists inside of the treeNodeCollection list public void DynamicAddNode(Node nodeToAdd, string parentName = null) { //Check if the root exists, and a parent name for searching has been specified if (Root != null && parentName != null) { //Search for the name of the specified parent inside the list of all nodes inside of the tree Node parentToRecieve = treeNodeCollection.FirstOrDefault(o => o.NodeName == parentName); //Add the new node to the specified parent parentToRecieve.AddChild(nodeToAdd); //Add the new node to the list of all the nodes inside the tree AddToTreeNodeCollection(nodeToAdd); } //Else check if the root is null else if (Root == null) { //Debug that the user request is being overriden Debug.LogError("OVERRIDING REQUEST, ROOT IS NULL AND HAS TO BE ADDED"); //Override and add a new root Root = new PrioritySelector(1, "root", behaviorTreeHandler); //Add the new root to the full list of nodes inside of the tree AddToTreeNodeCollection(Root); } //Else check if the specified parent is null else if (parentName == null) { //Debug that the user has to add a parent name Debug.LogError("INVALID PARENT. PLEASE ADD A PARENT NAME FOR SEARCHING"); } }
private static SwitchArgument <RepairState> GetRepairStep(RepairContext context) { var closeAction = new Action(r => { Repair.Close(); context.State = BotManager.Current.Name != "Fate Bot" ? RepairState.MovingBack : RepairState.None; Logger.AgilMessage("Ended repair step, now on: {0}", context.State); }); var close = new Decorator(req => Equipment.GetCondition() >= context.MinimumDurability && Repair.IsOpen, closeAction); var dismount = new Decorator(req => Core.Me.IsMounted, new Action(r => Actionmanager.Dismount())); var selectYes = new Decorator(req => SelectYesno.IsOpen, new Action(r => SelectYesno.ClickYes())); var move = Movement.SprintMove(r => context.Location); var repairAll = new RepairAll(); var selectRepair = new SetIconString(2); var setTarget = new SetTarget(() => context.NpcId); var interact = new Decorator(req => !Repair.IsOpen && !SelectIconString.IsOpen && !SelectYesno.IsOpen, new Interact()); var repairStep = new PrioritySelector(move, dismount, close, selectYes, repairAll, selectRepair, setTarget, interact, new Action(r => RunStatus.Success)); return(new SwitchArgument <RepairState>(repairStep, RepairState.Repairing)); }
public void ReadXml(XmlReader reader) { int count; reader.MoveToContent(); int.TryParse(reader["ChildrenCount"], out count); reader.ReadStartElement("Professionbuddy"); PrioritySelector ps = (PrioritySelector)DecoratedChild; for (int i = 0; i < count; i++) { Type type = Type.GetType("HighVoltz.Composites." + reader.Name); if (type != null) { IPBComposite comp = (IPBComposite)Activator.CreateInstance(type); if (comp != null) { comp.ReadXml(reader); ps.AddChild((Composite)comp); } } else { Professionbuddy.Err("PB:Failed to load type {0}", type); } } if (reader.NodeType == XmlNodeType.EndElement) { reader.ReadEndElement(); } }
public void RebuildBehaviors() { Composite simcRoot = null; NameCount = 'A'; simcRoot = new PrioritySelector( new Action(context => { iterationCounter++; return(RunStatus.Failure); }), new Decorator(ret => IsPaused, new Action(ret => RunStatus.Success)), CallActionList(ActionProxy.ActionImpl.oocapl, ret => !StyxWoW.Me.Combat), new Decorator(ret => StyxWoW.Me.Combat, new LockSelector( new Decorator( ret => StyxWoW.Me.CurrentTarget != null && StyxWoW.Me.Combat, new PrioritySelector(CombatIteration(), actions.Selector, IterationEnd()))))); TreeHooks.Instance.ReplaceHook(SimcraftHookName, simcRoot); if (Me.Specialization != Specialisation) { SelectorWindow.ShowDialog(); } Specialisation = Me.Specialization; }
public void WhenAChildReturnsSuccessOrRunning_ReturnTheSameAndDoNotCallNextChildInSequence(BehaviourStatus status) { var behaviours = Enumerable.Range(0, 10) .Select(x => new MockBehaviour { ReturnStatus = BehaviourStatus.Failed }) .ToArray(); behaviours[4].ReturnStatus = status; var sut = new PrioritySelector <MockContext>(behaviours); var behaviourStatus = sut.Tick(new MockContext()); Assert.That(behaviourStatus, Is.EqualTo(status)); for (int i = 0; i < 4; i++) { var mockBehaviour = behaviours[i]; Assert.That(mockBehaviour.InitializeCallCount, Is.EqualTo(1)); Assert.That(mockBehaviour.UpdateCallCount, Is.EqualTo(1)); } for (int i = 5; i < behaviours.Length; i++) { var mockBehaviour = behaviours[i]; Assert.That(mockBehaviour.InitializeCallCount, Is.EqualTo(0)); Assert.That(mockBehaviour.UpdateCallCount, Is.EqualTo(0)); Assert.That(mockBehaviour.TerminateCallCount, Is.EqualTo(0)); } }
/// <summary> /// use Alchemist's Flask if no flask buff active. do over optimize since this is a precombatbuff behavior /// </summary> /// <returns></returns> public static Composite CreateUseXPBuffPotionsBehavior() { const int ACCELERATED_LEARNING = 178119; // (aura) const int EXCESS_POTION_OF_ACCELERATED_LEARNING = 120182; // (item) if (!SingularSettings.Instance.UseXPBuffPotions) { return(new ActionAlwaysFail()); } PrioritySelector pri = new PrioritySelector(); if (Me.Level.Between(91, 99)) { pri.AddChild( new Decorator( req => !Me.HasAura(ACCELERATED_LEARNING), UseItem(EXCESS_POTION_OF_ACCELERATED_LEARNING) ) ); } if (!pri.Children.Any()) { return(new ActionAlwaysFail()); } return(new ThrottlePasses( 1, TimeSpan.FromSeconds(15), RunStatus.Failure, pri )); }
// remove any occurance of IdentityComposite in the current BotBase, used on dispose or botbase change void BotBaseCleanUp(PrioritySelector bot) { PrioritySelector botbase = null; if (bot != null) { botbase = bot; } else if (TreeRoot.Current.Root is PrioritySelector) { botbase = TreeRoot.Current.Root as PrioritySelector; } // check if we already injected into the BotBase if (botbase != null) { bool isRunning = botbase.IsRunning; if (isRunning) { TreeRoot.Stop(); } for (int i = botbase.Children.Count - 1; i >= 0; i--) { //if (botbase.Children[i] is IdentityComposite ) // this will not work after a recompile because the types are now in different assemblies if (botbase.Children[i].GetType().Name.Contains("PBIdentityComposite")) { botbase.Children.RemoveAt(i); } } } }
/// <summary> /// Factory method to create new Red Guard /// </summary> /// <returns></returns> public static Entity Create() { ChaseBehavior ChasingHero; //ChaseBehavior ChasingCompanions; AlwaysTurnRightBehavior Turning; RandomWanderBehavior Wandering; AttackEnemyBehavior Attacking; var e = GameFactory.CreateThing(ThingType.RED_GUARD, true); var ai = new BTAIComp(); e.AddComponent(ai); var sub = new PrioritySelector(); ai.rootNode = sub; var tc = e.GetComponent <ThingComp>(); tc.IsCollisionFree = false; tc.Color = new Color(255, 10, 4); tc.Faction = Faction.EVIL; var rwc = new RandomWanderComp(); e.AddComponent(rwc); rwc.MinDirectionChangeTime = 2.7; rwc.MaxDirectionChangeTime = 11.3; // attack hero or companions Attacking = new AttackEnemyBehavior(attackString); // chase companions that are very close /* * ChasingCompanions = new ChaseBehavior(typeof(Companion)); * ChasingCompanions.DeltaTimeBetweenMoves = RandomMath.RandomBetween(0.43f, 0.65f); * ChasingCompanions.ChaseRange = 1.5f; // RandomMath.RandomBetween(12f, 40f); * sub.AddChild(ChasingCompanions); */ // chase hero ChasingHero = new ChaseBehavior(Level.Current.Hero); ChasingHero.DeltaTimeBetweenMoves = RandomMath.RandomBetween(0.47f, 0.75f); ChasingHero.ChaseRange = 15f; // RandomMath.RandomBetween(12f, 40f); sub.AddChild(ChasingHero); Turning = new AlwaysTurnRightBehavior(); // patrolling Turning.DeltaTimeBetweenMoves = ChasingHero.DeltaTimeBetweenMoves; //RandomMath.RandomBetween(0.57f, 1.05f); Turning.DeltaTimeBetweenMoves = 0.7f; sub.AddChild(Turning); Wandering = new RandomWanderBehavior(); Wandering.DeltaTimeBetweenMoves = 0.7f; sub.AddChild(Wandering); e.Refresh(); return(e); }
public static Composite GetComposite(WoWClass wowClass, WoWSpec spec, BehaviorType behavior, WoWContext context, out int behaviourCount) { behaviourCount = 0; if (_methods.Count <= 0) { Logger.Write("Building method list"); foreach (var type in Assembly.GetExecutingAssembly().GetTypes()) { // All behavior methods should not be generic, and should have zero parameters, with their return types being of type Composite. _methods.AddRange( type.GetMethods(BindingFlags.Static | BindingFlags.Public).Where( mi => !mi.IsGenericMethod && mi.GetParameters().Length == 0).Where( mi => mi.ReturnType.IsAssignableFrom(typeof (Composite)))); } Logger.Write("Added " + _methods.Count + " methods"); } var matchedMethods = new Dictionary<BehaviorAttribute, Composite>(); foreach (MethodInfo mi in _methods) { // If the behavior is set as ignore. Don't use it? Duh? if (mi.GetCustomAttributes(typeof(IgnoreBehaviorCountAttribute), false).Any()) continue; // If there's no behavior attrib, then move along. foreach (var a in mi.GetCustomAttributes(typeof(BehaviorAttribute), false)) { var attribute = a as BehaviorAttribute; if (attribute == null) continue; // Check if our behavior matches with what we want. If not, don't add it! if (IsMatchingMethod(attribute, wowClass, spec, behavior, context)) { Logger.Write(string.Format("Matched {0} to behavior {1} for {2} {3} with priority {4}", mi.Name, behavior, wowClass.ToString().CamelToSpaced(), spec.ToString().CamelToSpaced(), attribute.PriorityLevel)); // if it blows up here, you defined a method with the exact same attribute and priority as one already found matchedMethods.Add(attribute, mi.Invoke(null, null) as Composite); } } } // If we found no methods, rofls! if (matchedMethods.Count <= 0) { return null; } var result = new PrioritySelector(); foreach (var kvp in matchedMethods.OrderByDescending(mm => mm.Key.PriorityLevel)) { result.AddChild(kvp.Value); behaviourCount++; } return result; }
void BotEvents_OnBotChanged(BotEvents.BotChangedEventArgs args) { if (TreeRoot.Current.Root is PrioritySelector) { PrioritySelector botbase = TreeRoot.Current.Root as PrioritySelector; BotBaseCleanUp(botbase); botbase.InsertChild(0, Root); } }
protected override Composite CreateBehavior() { PrioritySelector p = new PrioritySelector(); foreach (var behavior in GetNodes().Select(b => b.Behavior)) { p.AddChild(new Decorator(ret => GetConditionExec(), behavior));// } return new Decorator(ret => !IsDone, p); }
public override void Initialize() { Settings.DefualtSettings(); Fight = new PrioritySelector(); BotMain.OnStart += OnStart; BotMain.OnStop += OnStop; BotMain.OnTick += Pulse; Log.Debug("Allrounder by xTenshiSanx has been loaded"); }
public static Composite CreateDispelBehavior() { PrioritySelector prio = new PrioritySelector(); switch (StyxWoW.Me.Class) { case WoWClass.Paladin: prio.AddChild(Spell.Cast("Cleanse", on => _unitDispel, ret => BossMechs.MechDispell())); break; case WoWClass.Monk: prio.AddChild(Spell.Cast("Detox", on => _unitDispel, ret => BossMechs.MechDispell())); break; case WoWClass.Priest: if (StyxWoW.Me.Specialization == WoWSpec.PriestHoly || StyxWoW.Me.Specialization == WoWSpec.PriestDiscipline) { prio.AddChild(Spell.Cast("Purify", on => _unitDispel, ret => BossMechs.MechDispell())); } break; case WoWClass.Druid: if (StyxWoW.Me.Specialization == WoWSpec.DruidRestoration) { prio.AddChild(Spell.Cast("Nature's Cure", on => _unitDispel, ret => BossMechs.MechDispell())); } else { prio.AddChild(Spell.Cast("Remove Corruption", on => _unitDispel, ret => BossMechs.MechDispell())); } break; case WoWClass.Shaman: if (StyxWoW.Me.Specialization == WoWSpec.ShamanRestoration) { prio.AddChild(Spell.Cast("Purify Spirit", on => _unitDispel, ret => BossMechs.MechDispell())); } else { prio.AddChild(Spell.Cast("Cleanse Spirit", on => _unitDispel, ret => BossMechs.MechDispell())); } break; case WoWClass.Mage: prio.AddChild(Spell.Cast("Remove Curse", on => _unitDispel, ret => BossMechs.MechDispell())); break; } return(new Sequence( new Action(r => _unitDispel = (from unit in ObjectManager.GetObjectsOfType <WoWPlayer>(false) where unit.IsAlive where CanDispel(unit) select unit).OrderByDescending(u => u.HealthPercent).LastOrDefault()), //HealerManager.Instance.TargetList.FirstOrDefault(u => u.IsAlive && CanDispel(u))), prio )); }
public static Composite GetBehavior(AvoidanceContext context) { var reportUnknown = new Action(r => context.Capture.CaptureScreenshot(context)); var update = new Action(r => context.Update()); var move = CommonBehaviors.MoveAndStop(r => context.SafeLocation, 1f, true); var avoidCheck = new PrioritySelector(new Decorator(req => context.IsAvoiding, move), new Decorator(req => context.IsWaiting, new Action(r => RunStatus.Success))); return(new Sequence(update, avoidCheck)); }
protected override Composite CreateBehavior() { var decorated = new PrioritySelector(new Composite[0]); foreach (var behavior in base.GetNodes()) { decorated.AddChild(behavior.Behavior); } return(new Decorator(CheckNotAlreadyDone, decorated)); }
protected override Composite CreateBehavior() { PrioritySelector decorated = new PrioritySelector(new Composite[0]); foreach (ProfileBehavior behavior in base.GetNodes()) { decorated.AddChild(behavior.Behavior); } return(new Zeta.TreeSharp.Decorator(new CanRunDecoratorDelegate(CheckNotAlreadyDone), decorated)); }
protected override Composite CreateBehavior() { PrioritySelector p = new PrioritySelector(); foreach (var behavior in GetNodes().Select(b => b.Behavior)) { p.AddChild(new Decorator(ret => GetConditionExec(), behavior));// } return(new Decorator(ret => !IsDone, p)); }
private IBehaviourNode CreateBehaviourTree() { // Shoot, Chase, Idle var ret = new PrioritySelector( BehaviourFactory.CreateShootTarget(), BehaviourFactory.CreateChaseTarget(), BehaviourFactory.CreateGuardPosition(() => GuardPosition)); return(ret); }
public static Node CreateBehaviorTree(Dorf d) { PrioritySelector root = new PrioritySelector(); root.AddChild(CreateFleeBehavior(d),Double.MaxValue); root.AddChild(CreateInteractionBehavior(d, d.Workplace),50.0); root.AddChild(CreateInteractionBehavior(d, d.Bed)); return root; }
private PrioritySelector CreateSpiderBT() { Node idleMotion = IdleMotion(); PrioritySelector attackSelector = AttackPattern(); //PlayerAround around = new PlayerAround(attackSelector, gameObject, enemy, playerDistance); return(new PrioritySelector(new List <Node> { attackSelector, idleMotion })); }
public static Composite CreateBehavior(EquipmentContext equipContext, RepairContext repairContext) { var repair = repairContext.RepairEnabled ? RepairBehavior.CreateBehavior(repairContext) : new Action(r => RunStatus.Failure); var optimize = equipContext.OptimizerEnabled ? GearOptimizer.GetBehavior(equipContext) : new Action(r => RunStatus.Failure); var behavior = new PrioritySelector(repair, optimize); return(new Decorator(req => CanAct, behavior)); }
public static Composite GetBehavior(EquipmentContext context) { var startup = new Decorator(req => !context.Optimizing, new Action(r => context.Optimizing = true)); var cleanup = new Action(r => context.Reset()); var optimize = new PrioritySelector(startup, EquipWeapon(context), EquipArmor(context, 2), EquipArmor(context, 7), EquipArmor(context, 3), EquipArmor(context, 4), EquipArmor(context, 5), EquipRing(context, 11, 12), EquipArmor(context, 6), EquipArmor(context, 8), EquipArmor(context, 9), EquipRing(context, 12, 11), EquipArmor(context, 10), EquipOffhand(context), cleanup); return(new Decorator(req => CanOptimize && NeedsOptimization(context), optimize)); }
/// <summary> /// This will replace the main BehaviorTree hooks for Combat, Vendoring, and Looting. /// </summary> private static void ReplaceTreeHooks() { // This is the do-all-be-all god-head all encompasing piece of trinity TreeHooks.Instance.ReplaceHook("Combat", new Decorator(ctx => CheckHasTarget(ctx), HandleTargetAction())); // We still want the main VendorRun logic, we're just going to take control of *when* this logic kicks in PrioritySelector VendorRunPrioritySelector = (TreeHooks.Instance.Hooks["VendorRun"][0] as Decorator).Children[0] as PrioritySelector; TreeHooks.Instance.ReplaceHook("VendorRun", new Decorator(ret => TownRun.TownRunCanRun(ret), VendorRunPrioritySelector)); // Loot tree is now empty and never runs (Loot is handled through combat) TreeHooks.Instance.ReplaceHook("Loot", new Decorator(ret => false, new Action())); }
// [Behavior(BehaviorType.Combat, priority: 999)] public static Composite CreateUseTrinketsBehaviour() { // Saving Settings via GUI will now force reinitialize so we can build the behaviors // basead upon the settings rather than continually checking the settings in the Btree // // if (SingularSettings.Instance.Trinket1Usage == TrinketUsage.Never && SingularSettings.Instance.Trinket2Usage == TrinketUsage.Never) { return(new Action(ret => { return RunStatus.Failure; })); } PrioritySelector ps = new PrioritySelector(); if (SingularSettings.IsTrinketUsageWanted(TrinketUsage.OnCooldown)) { ps.AddChild(Item.UseEquippedTrinket(TrinketUsage.OnCooldown)); } if (SingularSettings.IsTrinketUsageWanted(TrinketUsage.OnCooldownInCombat)) { ps.AddChild(Item.UseEquippedTrinket(TrinketUsage.OnCooldownInCombat)); } if (SingularSettings.IsTrinketUsageWanted(TrinketUsage.LowHealth)) { ps.AddChild(new Decorator(ret => StyxWoW.Me.HealthPercent < SingularSettings.Instance.PotionHealth, Item.UseEquippedTrinket(TrinketUsage.LowHealth))); } if (SingularSettings.IsTrinketUsageWanted(TrinketUsage.LowPower)) { ps.AddChild(new Decorator(ret => StyxWoW.Me.PowerPercent < SingularSettings.Instance.PotionMana, Item.UseEquippedTrinket(TrinketUsage.LowPower))); } if (SingularSettings.IsTrinketUsageWanted(TrinketUsage.CrowdControlled)) { ps.AddChild(new Decorator(ret => Unit.IsCrowdControlled(StyxWoW.Me), Item.UseEquippedTrinket(TrinketUsage.CrowdControlled))); } if (SingularSettings.IsTrinketUsageWanted(TrinketUsage.CrowdControlledSilenced)) { ps.AddChild(new Decorator(ret => StyxWoW.Me.Silenced && Unit.IsCrowdControlled(StyxWoW.Me), Item.UseEquippedTrinket(TrinketUsage.CrowdControlledSilenced))); } return(ps); }
public static Composite CreateUseTrinketsBehaviour() { // Saving Settings via GUI will now force reinitialize so we can build the behaviors // basead upon the settings rather than continually checking the settings in the Btree // // if (SingularSettings.Instance.Trinket1Usage == TrinketUsage.Never && SingularSettings.Instance.Trinket2Usage == TrinketUsage.Never) { return new Action(ret => { return RunStatus.Failure; }); } PrioritySelector ps = new PrioritySelector(); if (SingularSettings.IsTrinketUsageWanted(TrinketUsage.OnCooldown)) { ps.AddChild(Item.UseEquippedTrinket(TrinketUsage.OnCooldown)); } if (SingularSettings.IsTrinketUsageWanted(TrinketUsage.OnCooldownInCombat)) { ps.AddChild(Item.UseEquippedTrinket(TrinketUsage.OnCooldownInCombat)); } if (SingularSettings.IsTrinketUsageWanted(TrinketUsage.LowHealth)) { ps.AddChild( new Decorator( ret => StyxWoW.Me.HealthPercent < SingularSettings.Instance.PotionHealth, Item.UseEquippedTrinket( TrinketUsage.LowHealth))); } if (SingularSettings.IsTrinketUsageWanted(TrinketUsage.LowPower)) { ps.AddChild( new Decorator( ret => StyxWoW.Me.PowerPercent < SingularSettings.Instance.PotionMana, Item.UseEquippedTrinket(TrinketUsage.LowPower))); } if (SingularSettings.IsTrinketUsageWanted(TrinketUsage.CrowdControlled )) { ps.AddChild( new Decorator( ret => Unit.IsCrowdControlled( StyxWoW.Me), Item.UseEquippedTrinket( TrinketUsage.CrowdControlled ))); } if (SingularSettings.IsTrinketUsageWanted(TrinketUsage.CrowdControlledSilenced )) { ps.AddChild( new Decorator( ret => StyxWoW.Me.Silenced && Unit.IsCrowdControlled( StyxWoW.Me), Item.UseEquippedTrinket(TrinketUsage.CrowdControlledSilenced))); } return ps; }
private static SwitchArgument <RepairState> GetReturnStep(RepairContext context) { var finalize = new Action(r => { context.State = RepairState.None; Logger.AgilMessage("Finished moving back."); return(RunStatus.Success); }); var move = Movement.SprintMove(r => context.Location); var returnStep = new PrioritySelector(move, finalize, new Action(r => RunStatus.Success)); return(new SwitchArgument <RepairState>(returnStep, RepairState.MovingBack)); }
public void WriteXml(XmlWriter writer) { writer.WriteStartElement("Professionbuddy"); PrioritySelector ps = (PrioritySelector)DecoratedChild; writer.WriteStartAttribute("ChildrenCount"); writer.WriteValue(ps.Children.Count); writer.WriteEndAttribute(); foreach (IPBComposite comp in ps.Children) { writer.WriteStartElement(comp.GetType().Name); ((IXmlSerializable)comp).WriteXml(writer); writer.WriteEndElement(); } writer.WriteEndElement(); }
private static Composite CreateCombatRacialInRangeBehavior() { PrioritySelector priCombat = new PrioritySelector(); // not a racial, but best place to handle it if (SpellManager.HasSpell("Lifeblood")) { priCombat.AddChild( Spell.HandleOffGCD(Spell.BuffSelf("Lifeblood", ret => !PartyBuff.WeHaveBloodlust)) ); } if (SpellManager.HasSpell("Berserking")) { priCombat.AddChild( Spell.HandleOffGCD(Spell.BuffSelf("Berserking", ret => !PartyBuff.WeHaveBloodlust)) ); } if (SpellManager.HasSpell("Blood Fury")) { priCombat.AddChild( Spell.HandleOffGCD(Spell.BuffSelf("Blood Fury", ret => true)) ); } if (priCombat.Children.Any()) { return(new Decorator( req => { if (!StyxWoW.Me.Combat || !StyxWoW.Me.GotTarget()) { return false; } if (StyxWoW.Me.IsMelee()) { return StyxWoW.Me.CurrentTarget.IsWithinMeleeRange; } return !StyxWoW.Me.IsMoving && StyxWoW.Me.CurrentTarget.SpellDistance() < 40; }, priCombat )); } return(null); }
/// <summary> /// Blows your wad all over the floor, copied from Singular /// </summary> /// <returns>Nothing but win</returns> public static Composite UseTrinkets() { if (CLUSettings.Instance.Trinket1Usage == TrinketUsage.Never && CLUSettings.Instance.Trinket2Usage == TrinketUsage.Never) { return(new Action(ret => { return RunStatus.Failure; })); } PrioritySelector ps = new PrioritySelector(); if (CLUSettings.IsTrinketUsageWanted(TrinketUsage.OnCooldown)) { ps.AddChild(Item.UseEquippedTrinket(TrinketUsage.OnCooldown)); } if (CLUSettings.IsTrinketUsageWanted(TrinketUsage.OnCooldownInCombat)) { ps.AddChild(Item.UseEquippedTrinket(TrinketUsage.OnCooldownInCombat)); } if (CLUSettings.IsTrinketUsageWanted(TrinketUsage.LowHealth)) { ps.AddChild(new Decorator(ret => StyxWoW.Me.HealthPercent < CLUSettings.Instance.MinHealth, Item.UseEquippedTrinket(TrinketUsage.LowHealth))); } if (CLUSettings.IsTrinketUsageWanted(TrinketUsage.LowPower)) { ps.AddChild(new Decorator(ret => StyxWoW.Me.PowerPercent < CLUSettings.Instance.MinMana, Item.UseEquippedTrinket(TrinketUsage.LowPower))); } if (CLUSettings.IsTrinketUsageWanted(TrinketUsage.CrowdControlled)) { ps.AddChild(new Decorator(ret => Unit.UnitIsControlled(Me, false), Item.UseEquippedTrinket(TrinketUsage.CrowdControlled))); } if (CLUSettings.IsTrinketUsageWanted(TrinketUsage.CrowdControlledSilenced)) { ps.AddChild(new Decorator(ret => StyxWoW.Me.Silenced && Unit.UnitIsControlled(Me, false), Item.UseEquippedTrinket(TrinketUsage.CrowdControlledSilenced))); } return(ps); //return UseEquippedTrinket(); }
public void WhenAReevaluatedChildReturnsSuccess_ReturnSuccessAndResetChildren() { var behaviours = Enumerable.Range(0, 10) .Select(x => new MockBehaviour { ReturnStatus = BehaviourStatus.Failed }) .ToArray(); behaviours[4].ReturnStatus = BehaviourStatus.Running; var sut = new PrioritySelector <MockContext>(behaviours); sut.Tick(new MockContext()); behaviours[0].ReturnStatus = BehaviourStatus.Succeeded; sut.Tick(new MockContext()); Assert.That(behaviours[0].InitializeCallCount, Is.EqualTo(1)); Assert.That(behaviours[0].UpdateCallCount, Is.EqualTo(2)); Assert.That(behaviours[0].TerminateCallCount, Is.EqualTo(2)); Assert.That(behaviours[0].ResetCount, Is.EqualTo(1)); for (int i = 1; i < 4; i++) { var mockBehaviour = behaviours[i]; Assert.That(mockBehaviour.InitializeCallCount, Is.EqualTo(1)); Assert.That(mockBehaviour.UpdateCallCount, Is.EqualTo(1)); Assert.That(mockBehaviour.TerminateCallCount, Is.EqualTo(1)); Assert.That(mockBehaviour.ResetCount, Is.EqualTo(1)); } Assert.That(behaviours[4].InitializeCallCount, Is.EqualTo(1)); Assert.That(behaviours[4].UpdateCallCount, Is.EqualTo(1)); Assert.That(behaviours[4].TerminateCallCount, Is.EqualTo(0)); Assert.That(behaviours[4].ResetCount, Is.EqualTo(1)); for (int i = 5; i < behaviours.Length; i++) { var mockBehaviour = behaviours[i]; Assert.That(mockBehaviour.InitializeCallCount, Is.EqualTo(0)); Assert.That(mockBehaviour.UpdateCallCount, Is.EqualTo(0)); Assert.That(mockBehaviour.TerminateCallCount, Is.EqualTo(0)); Assert.That(mockBehaviour.ResetCount, Is.EqualTo(0)); } }
public void WhenAllChildrenReturnFailure_ReturnFailure() { var behaviours = Enumerable.Range(0, 10) .Select(x => new MockBehaviour { ReturnStatus = BehaviourStatus.Failed }) .ToArray(); var sut = new PrioritySelector <MockContext>(behaviours); var behaviourStatus = sut.Tick(new MockContext()); Assert.That(behaviourStatus, Is.EqualTo(BehaviourStatus.Failed)); Assert.That(behaviours.AllInitialized(), Is.True); Assert.That(behaviours.AllUpdated(), Is.True); Assert.That(behaviours.AllTerminated(), Is.True); }
//AddRoot will add the root to the tree. Incase the root already exists, it will notify the user to use the DynamicAddNode function instead. public void AddRoot() { //Check if the root is not null if (Root != null) { //If the root is not null then the root exists, so debug that the root exists Debug.LogError("ROOT EXISTS. USE DYNAMICADDNODE FUNCTION"); } else { //If it is null, add a new root to the tree Root = new PrioritySelector(1, "root", behaviorTreeHandler); //Add the new root to the full list of nodes inside of the tree AddToTreeNodeCollection(Root); } }
public static Composite CreateShamanEnhancementHealPvp() { Composite healBT = new PrioritySelector( new Decorator( ret => StyxWoW.Me.HasAura("Maelstrom Weapon", 5), new PrioritySelector( ctx => Unit.GroupMembers.Where( p => p.IsAlive && p.GetPredictedHealthPercent() < 50 && p.Distance < 40).FirstOrDefault(), Spell.Cast( "Healing Surge", ret => StyxWoW.Me, ret => StyxWoW.Me.GetPredictedHealthPercent() < 75 ), Spell.Cast( "Healing Surge", ret => (WoWPlayer) ret, ret => StyxWoW.Me.GetPredictedHealthPercent() < 50 ) ) ), new Decorator( ret => !StyxWoW.Me.Combat, Spell.Heal("Healing Surge", ret => StyxWoW.Me, ret => StyxWoW.Me.GetPredictedHealthPercent() <= 80) ) ); return healBT; }
// Use this for initialization void Start() { mate = buddy.transform; speed = 1.1f; direction = mate.position - transform.position; root = new SequenceSelector(); redSeq = new SequenceSelector(); colors = new PrioritySelector(); movement = new PrioritySelector(); close = new Condition(() => Mathf.Abs(Vector3.Distance(transform.position, mate.position)) < kindaClose); red = new Action(Red); green = new Action(Green); toward = new Action(Toward); away = new Action(Away); root.AddChild(colors, movement); colors.AddChild(redSeq,1.0); colors.AddChild(green); redSeq.AddChild(close,red); movement.AddChild(away,farAway); towardPrio = movement.AddChild(toward); toward.name = "toward"; }
public static Composite Cast(SpellFindDelegate ssd, SimpleBooleanDelegate checkMovement, UnitSelectionDelegate onUnit, SimpleBooleanDelegate requirements, SimpleBooleanDelegate cancel = null, LagTolerance allow = LagTolerance.Yes, bool skipWowCheck = false, CanCastDelegate canCast = null, HasGcd gcd = HasGcd.Yes) { // only need to check these at creation time if (ssd == null || checkMovement == null || onUnit == null || requirements == null) return new ActionAlwaysFail(); if (canCast == null) canCast = CanCastHack; Composite comp = new PrioritySelector( // create a CastContext object to save passed in context and other values ctx => new CastContext(ctx, ssd, onUnit, gcd), new Sequence( // cast the spell, saving state information including if we queued this cast new Action(ret => { CastContext cctx = ret.CastContext(); if (cctx.spell == null) return RunStatus.Failure; if (cctx.unit == null) return RunStatus.Failure; if (!requirements(cctx.context)) return RunStatus.Failure; if (checkMovement(cctx.context) && Me.IsMoving && !AllowMovingWhileCasting(cctx.spell)) { if (SingularSettings.DebugSpellCasting) Logger.WriteDebug("skipping Spell.Cast({0},[{1}]) because we are moving", cctx.unit.SafeName(), cctx.spell.Name); return RunStatus.Failure; } // check we can cast it on target without checking for movement // if (!Spell.CanCastHack(_spell, cctx.unit, true, false, allow == LagTolerance.Yes)) if (!canCast(cctx.sfr, cctx.unit, skipWowCheck)) { if (SingularSettings.DebugSpellCasting) Logger.WriteDebug("skipping Spell.Cast({0},[{1}]) because CanCastHack failed", cctx.unit.SafeName(), cctx.spell.Name); return RunStatus.Failure; } // save status of queueing spell (lag tolerance - the prior spell still completing) cctx.IsSpellBeingQueued = allow == LagTolerance.Yes && (Spell.GcdActive || StyxWoW.Me.IsCasting || StyxWoW.Me.IsChanneling); const int PENANCE = 047540; LogCast( cctx.spell.Name, cctx.unit, cctx.health, cctx.distance, cctx.spell.IsHeal() ? true : (cctx.spell.Id == PENANCE && cctx.unit.IsFriendly) ); if (SingularSettings.DebugSpellCasting) Logger.WriteDebug("Cast('{0}'): dist:{1:F3}, need({2}), hitbox:{3:F3}", cctx.spell.Name, cctx.unit.Distance, cctx.spell.IsMeleeSpell ? "Melee" : (!cctx.spell.HasRange ? "None" : string.Format("min={0:F3},max={1:F3}", cctx.spell.MinRange, cctx.spell.MaxRange)), cctx.unit.CombatReach ); if (!Spell.CastPrimative(cctx.spell, cctx.unit)) { Logger.Write(Color.LightPink, "cast of {0} on {1} failed!", cctx.spell.Name, cctx.unit.SafeName()); return RunStatus.Failure; } SingularRoutine.UpdateDiagnosticCastingState(); return RunStatus.Success; }), new Action(r => { if (SingularSettings.DebugSpellCasting) { CastContext cctx = r.CastContext(); Logger.WriteFile("Spell.Cast[{0}]: checking to ensure cast in progress", cctx.spell.Name); } return RunStatus.Success; }), // for instant spell, wait for GCD to start // for non-instant spell, wait for .IsCasting / .IsChanneling to start new PrioritySelector( new Wait( TimeSpan.FromMilliseconds(350), until => { CastContext cctx = until.CastContext(); if (gcd == HasGcd.No) { if (SingularSettings.DebugSpellCasting) Logger.WriteFile("Spell.Cast[{0}]: has no GCD, status GCD={1}, remains={2}", cctx.spell.Name, Spell.IsGlobalCooldown(allow).ToYN(), (long)Spell.GcdTimeLeft.TotalMilliseconds); return true; } if (cctx.spell.IsInstantCast() && Spell.GcdTimeLeft.TotalMilliseconds > 650) { if (SingularSettings.DebugSpellCasting) Logger.WriteFile("Spell.Cast[{0}]: is instant, status GCD={1}, remains={2}", cctx.spell.Name, Spell.IsGlobalCooldown(allow).ToYN(), (long)Spell.GcdTimeLeft.TotalMilliseconds); return true; } if (Me.CurrentCastTimeLeft.TotalMilliseconds > 750) { if (SingularSettings.DebugSpellCasting) Logger.WriteFile( "Spell.Cast[{0}]: cast time {1} left, iscasting={2}", cctx.spell.Name, (long)Me.CurrentCastTimeLeft.TotalMilliseconds, Spell.IsCasting(allow).ToYN()); return true; } if (Me.CurrentChannelTimeLeft.TotalMilliseconds > 750) { if (SingularSettings.DebugSpellCasting) Logger.WriteFile( "Spell.Cast[{0}]: channel spell and channel has {1} left, ischanneling={2}", cctx.spell.Name, (long)Me.CurrentChannelTimeLeft.TotalMilliseconds, Spell.IsChannelling(allow).ToYN()); return true; } return false; }, new ActionAlwaysSucceed() ), new Action( r => { if (SingularSettings.DebugSpellCasting) { CastContext cctx = r.CastContext(); Logger.WriteFile( "Spell.Cast[{0}]: timed out failing to detect spell in progress - gcdleft={1}/{2}, castleft={3}/{4}, chanleft={5}/{6}", cctx.spell.Name, Spell.IsGlobalCooldown(allow).ToYN(), (long)Spell.GcdTimeLeft.TotalMilliseconds, Spell.IsCasting(allow).ToYN(), (long)Me.CurrentCastTimeLeft.TotalMilliseconds, Spell.IsCasting(allow).ToYN(), (long)Me.CurrentChannelTimeLeft.TotalMilliseconds); } return RunStatus.Success; }) ), // now check for one of the possible done casting states new PrioritySelector( // for cast already ended, assume it has no Global Cooldown new Decorator( ret => !Spell.IsGlobalCooldown() && !Spell.IsCastingOrChannelling(), new Action(r => { CastContext cctx = r.CastContext(); if (SingularSettings.DebugSpellCasting) { if (!Spell.IsGlobalCooldown()) Logger.WriteFile("Spell.Cast(\"{0}\"): complete, no gcd active, lag={0} hasgcd={1}", cctx.spell.Name, allow, gcd); else Logger.WriteFile("Spell.Cast(\"{0}\"): complete, no cast in progress", cctx.spell.Name); } return RunStatus.Success; }) ), // for instant or no cancel method given, we are done new Decorator( ret => gcd == HasGcd.No || cancel == null || ret.CastContext().spell.IsInstantCast(), new Action(r => { CastContext cctx = r.CastContext(); if (SingularSettings.DebugSpellCasting) { if (gcd == HasGcd.No) Logger.WriteFile("Spell.Cast(\"{0}\"): complete, hasgcd=No", cctx.spell.Name); else if (r.CastContext().spell.IsInstantCast()) Logger.WriteFile("Spell.Cast(\"{0}\"): complete, is instant cast", cctx.spell.Name); else Logger.WriteFile("Spell.Cast(\"{0}\"): complete, no cancel delegate given", cctx.spell.Name); } return RunStatus.Success; }) ), // while casting/channeling call the cancel method to see if we should abort new Wait(12, until => { CastContext cctx = until.CastContext(); SingularRoutine.UpdateDiagnosticCastingState(); // Interrupted or finished casting. if (!Spell.IsCastingOrChannelling(allow)) { Logger.WriteDebug("Spell.Cast(\"{0}\"): complete, iscasting=false", cctx.spell.Name); return true; } // check cancel delegate if we are finished if (cancel(cctx.context)) { SpellManager.StopCasting(); Logger.Write(LogColor.Cancel, "/cancel {0} on {1} @ {2:F1}%", cctx.spell.Name, cctx.unit.SafeName(), cctx.unit.HealthPercent); return true; } // continue casting/channeling at this point return false; }, new ActionAlwaysSucceed() ), // if we are here, we timed out after 12 seconds (very odd) new Action(r => { CastContext cctx = r.CastContext(); Logger.WriteDebug("Spell.Cast(\"{0}\"): aborting, timed out waiting on cast, gcd={1} cast={2} chanl={3}", cctx.spell.Name, Spell.IsGlobalCooldown().ToYN(), Spell.IsCasting().ToYN(), Spell.IsChannelling().ToYN()); return RunStatus.Success; }) ), // made it this far then we are RunStatus.Success, so reset wowunit reference and return new Action(ret => { CastContext cctx = ret.CastContext(); cctx.unit = null; cctx.spell = null; return RunStatus.Success; }) ), // cast Sequence failed, so only thing left is to reset cached references and report failure new Action(ret => { CastContext cctx = ret.CastContext(); cctx.unit = null; cctx.spell = null; return RunStatus.Failure; }) ); // when no cancel method in place, we will return immediately so..... // .. throttle attempts at casting this spell. note: this only limits this // .. instance of the spell.cast behavior. in other words, if this is for a cast // .. of flame shock, it would only throttle this behavior tree instance, not any // .. other trees which also call Spell.Cast("flame shock") if (cancel == null) comp = new Throttle( TimeSpan.FromMilliseconds(SingularSettings.Instance.SameSpellThrottle), comp); return comp; }
protected override Composite CreateBehavior() { var decorated=new PrioritySelector(new Composite[0]); foreach (var behavior in base.GetNodes()) { decorated.AddChild(behavior.Behavior); } return new Decorator(CheckNotAlreadyDone, decorated); }
public void Start() { Logic = new PrioritySelector( //PAUSE new Decorator(ret => TheVariables.PAUSE, new Action(ret => RunStatus.Success) ), //reset TheVariables - works new Decorator(ret => LokiPoe.ObjectManager.Me.IsInTown, new Sequence( new Action(ret => TheVariables.takePortalFromAreaToTown = null), new Action(ret => TheVariables.makePortal = false), new Action(ret => RunStatus.Failure) ) ), new Decorator(ret => !LokiPoe.ObjectManager.Me.IsInTown, new Sequence( new Action(ret => TheVariables.takePortalFromTownToArea = null), new Action(ret => RunStatus.Failure) ) ), //Party stuff - works new Decorator(ret => TheVariables.acceptPartyInviteFrom.Length > 1, TheLogic.AcceptPartyInvite() ), new Decorator(ret => TheVariables.leaveParty, TheLogic.LeaveParty() ), //town-only-stuff new Decorator(ret => LokiPoe.ObjectManager.Me.IsInTown && TheVariables.moveToMiddleOfTown, TheLogic.MoveToMiddleOfTown() ), new Decorator(ret => LokiPoe.ObjectManager.Me.IsInTown && TheVariables.targetTown.Length > 1, TheLogic.SwitchTown(TheVariables.targetTown) ), //portal stuff - works new Decorator(ret => TheVariables.takePortalFromTownToArea != null || TheVariables.takePortalFromAreaToTown != null, TheLogic.TakeTP() ), new Decorator(ret => TheVariables.makePortal && !LokiPoe.ObjectManager.Me.IsInTown, TheLogic.MakeTP() ), //inArea or town stuff new Decorator(ret => TheVariables.takeNearestAreaTransition, TheLogic.MoveToAndTakeAreaTransition() //new Action(ret => ExileBoxer.Log.Debug("i want to take a transition now..")) ), //inArea #0 new Decorator(ret => TheVariables.takeNearestIslandTransition, TheLogic.MoveToAndTakeIslandTransition() ), //inArea stuff #1 - works //fight! return Success if we are fighting, so move does not get triggered pls! new Decorator(ret => !LokiPoe.ObjectManager.Me.IsInTown && TheVariables.checkBox2, TheLogic.Fight() ), //inArea stuff #2 - MOVE TO SHOULD BE THE VERY LAST THING PLEASE!!!!! - works new Decorator(ret => !LokiPoe.ObjectManager.Me.IsInTown && TheVariables.checkBox1, new PrioritySelector( new Decorator(ret => TheVariables.distanceLeader > TheVariables.numUpDown2, @CommonBehaviors.MoveTo(ret => TheVariables.posLeader, ret => "", TheVariables.numUpDown3) ), new Decorator(ret => TheVariables.distanceLeader <= TheVariables.numUpDown3 && LokiPoe.ObjectManager.Me.IsMoving, new Action(ret => Navigator.PlayerMover.Stop()) ) ) ), new Decorator(ret => !LokiPoe.ObjectManager.Me.IsInTown && !TheVariables.checkBox1, new Action(ret => Navigator.PlayerMover.Stop()) ) ); gui = new GUI(); gui.OnInit(); ResetVariables(); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... you really should!"); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... :-)"); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); Log.Debug("you should open the GUI by pressing 'Bot-Config' ... "); }
public Composite GenerateBehaviorTree() { if (!SingularSettings.Debug) return new PrioritySelector(blist.Select(b => b.behavior).ToArray()); PrioritySelector pri = new PrioritySelector(); foreach (PrioritizedBehavior pb in blist) { if (!SingularSettings.TraceHeals) pri.AddChild(pb.behavior); else { CallTrace ct = new CallTrace(pb.Name, pb.behavior); ct.TraceActive = true; ct.TraceEnter = false; ct.TraceExit = true; pri.AddChild( ct ); } } return pri; }
public Composite GenerateCombatBehavior(float range) { _context.CombatRange = range; var combatBehavior = new PrioritySelector(); combatBehavior.AddChild(GenerateCheckAvailableTargetsBehavior(range)); combatBehavior.AddChild(new Decorator(TargetAvailable(), GenerateAttackBehavior())); combatBehavior.AddChild(GenerateMoveToTargetBehavior()); return combatBehavior; }
/// <summary> /// creates a behavior that provides kiting support. intent is to move to closest safest spot possible away from target /// while optionally performing behaviors while moving there /// </summary> /// <param name="runawayAttack">behavior to use while running away (should not require facing target.) should be null if not needed</param> /// <param name="jumpturnAttack">behavior to use in middle of jump turn (while facing target.) should be null if not needed</param> /// <returns></returns> public static void CreateKitingBehavior(Composite slowAttack, Composite runawayAttack, Composite jumpturnAttack) { // _SlowAttackBehavior = slowAttack; Composite kitingBehavior = new PrioritySelector( new Decorator( ret => bstate == State.None, new Action(r => System.Diagnostics.Debug.Assert(false, "Kiting Failure: should never run with state == State.None")) ), new Decorator( ret => bstate != State.None, new PrioritySelector( new Decorator(ret => !StyxWoW.IsInGame, new Action(ret => EndKiting("not in game so cancelling"))), new Decorator(ret => !Me.IsAlive, new Action(ret => EndKiting("i am dead so cancelling"))), new Decorator(ret => jumpturnAttack != null && !Me.GotTarget(), new Action(ret => EndKiting("attack behavior but no target, cancelling"))), new PrioritySelector( ctx => SafeArea.NearestEnemyMobAttackingMe, new Decorator(ret => ret == null, new Action(ret => EndKiting("no non-trivial mobs attacking me, so cancelling"))), new Decorator( ret => (ret as WoWUnit).SpellDistance() > 20, new Action(ret => EndKiting(string.Format("closest non-trivial attacker {0} is {1:F1} yds away, so cancelling", (ret as WoWUnit).SafeName(), (ret as WoWUnit).SpellDistance()))) ), new Decorator( ret => (ret as WoWUnit).MovementInfo.CurrentSpeed >= Me.MovementInfo.CurrentSpeed && (ret as WoWUnit).IsMovingTowards(), new Action(ret => EndKiting(string.Format("closest non-trivial attacker {0} moving faster than me, so cancelling", (ret as WoWUnit).SafeName()))) ) ), new Decorator(ret => timeOut < DateTime.UtcNow, new Action(ret => EndKiting("taking too long, so cancelling"))), new Decorator(ret => Me.Stunned || Me.IsStunned(), new Action(ret => EndKiting("stunned, cancelling"))), new Decorator(ret => Me.Rooted || Me.IsRooted(), new Action(ret => EndKiting("rooted, cancelling"))), new Decorator(ret => Me.Location.Distance(locSafeSpot) < DISTANCE_CLOSE_ENOUGH_TO_DESTINATION, new Action(ret => EndKiting(string.Format("reached safe spot {0:F1} yds away", StyxWoW.Me.Location.Distance(locKiteBegin))))), new Decorator(ret => Me.Location.Distance(locSafeSpot) > DISTANCE_TOO_FAR_FROM_DESTINATION, new Action(ret => EndKiting(string.Format("too far from safe spot ( {0:F1} > {1:F1} yds), cancelling", Me.Location.Distance(locSafeSpot), DISTANCE_TOO_FAR_FROM_DESTINATION)))), new Decorator(ret => bstate == State.Slow, new Sequence( new SeqDbg(0f, Color.Cyan, s => "KITE: entering SlowAttack behavior"), new PrioritySelector( slowAttack ?? new SeqDbg( 0f, Color.Cyan, s => "KITE: no SlowAttack behavior so skipping"), new PriDbg(0f, Color.Cyan, s => "KITE: ended SlowAttack behavior with Failure") ), new Action(ret => { Logger.WriteDebug(Color.Cyan, "KITE: SlowAttack done, transition to Moving"); bstate = State.Moving; }) ) ), new Decorator(ret => bstate == State.Moving, new Sequence( new Action(d => Navigator.MoveTo(locSafeSpot)), // following 3 lines make sure we are facing and have started moving in the correct direction. it will force // .. a minimum wait of 250 ms after movement has started in the correct direction new WaitContinue(TimeSpan.FromMilliseconds(250), r => Me.IsDirectlyFacing(locSafeSpot), new ActionAlwaysSucceed()), new WaitContinue(TimeSpan.FromMilliseconds(500), r => Me.IsMoving, new ActionAlwaysSucceed()), // wait till we are moving (should be very quick) new DecoratorContinue( r => !Me.IsMoving, new Action(ret => { EndKiting("KITE: we stopped moving, so end kiting"); return RunStatus.Success; }) ), new Action(ret => { if (runawayAttack != null) { bstate = State.NonFaceAttack; Logger.WriteDebug(Color.Cyan, "KITE: transition from Moving to NonFaceAttack"); return RunStatus.Failure; } return RunStatus.Success; }), new Action(ret => { if (jumpturnAttack != null) { if (JumpTurn.IsJumpTurnInProgress()) { bstate = State.JumpTurnAndAttack; Logger.WriteDebug(Color.Cyan, "KITE: transition error - active jumpturn? forcing state JumpTurn"); return RunStatus.Failure; } if (Me.IsMoving && Me.IsSafelyFacing(Me.CurrentTarget, 120f)) { bstate = State.AttackWithoutJumpTurn; Logger.WriteDebug(Color.Cyan, "KITE: already facing so transition from Moving to AttackNoJumpTurn"); return RunStatus.Failure; } if (JumpTurn.IsJumpTurnNeeded()) { bstate = State.JumpTurnAndAttack; Logger.WriteDebug(Color.Cyan, "KITE: transition from Moving to JumpTurn"); return RunStatus.Failure; } } return RunStatus.Success; }) ) /* new Action( ret => { Navigator.MoveTo( safeSpot ); if (attackBehavior != null ) { if (WoWMathHelper.IsFacing( Me.Location, Me.RenderFacing, safeSpot, SafeArea.ONE_DEGREE_AS_RADIAN )) { if (JumpTurn.IsNeeded()) { bstate = State.JumpTurn; Logger.WriteDebug(Color.Cyan, "KITE: transition from Moving to JumpTurn"); } else if (JumpTurn.ActiveJumpTurn()) { bstate = State.JumpTurn; Logger.WriteDebug(Color.Cyan, "KITE: transition error - active jumpturn? forcing state JumpTurn"); } } } }) */ ), new Decorator(ret => bstate == State.NonFaceAttack, new PrioritySelector( new Sequence( new Action(ret => Logger.WriteDebug(Color.Cyan, "KITE: entering NonFaceAttack behavior")), runawayAttack ?? new Action(r => { return RunStatus.Failure; }), new Action(r => { return RunStatus.Failure; }) ), new Action(ret => { Logger.WriteDebug(Color.Cyan, "KITE: transition from NonFaceAttack to Moving"); bstate = State.Moving; }) ) ), new Decorator(ret => bstate == State.AttackWithoutJumpTurn, new PrioritySelector( new Sequence( new Action(ret => Logger.WriteDebug(Color.Cyan, "KITE: entering AttackNoJumpTurn behavior")), jumpturnAttack ?? new Action(r => { return RunStatus.Failure; }), new Action(r => { return RunStatus.Failure; }) ), new Action(ret => { Logger.WriteDebug(Color.Cyan, "KITE: transition from AttackNoJumpTurn to Moving"); bstate = State.Moving; }) ) ), new Decorator(ret => bstate == State.JumpTurnAndAttack, new PrioritySelector( JumpTurn.CreateBehavior(jumpturnAttack), new Decorator(ret => !JumpTurn.IsJumpTurnInProgress(), new Action(ret => { bstate = State.Moving; Logger.WriteDebug(Color.Cyan, "KITE: transition from JumpTurn to Moving"); }) ) ) ), new Action(ret => Logger.WriteDebug(Color.Cyan, "KITE: fell through with state {0}", bstate.ToString())) ) ) ); TreeHooks.Instance.ReplaceHook(SingularRoutine.HookName("KitingBehavior"), kitingBehavior ); }
/// <summary> /// Creates a behavior to start auto attacking to current target. /// </summary> /// <remarks> /// Created 23/05/2011 /// </remarks> /// <param name="includePet"> This will also toggle pet auto attack. </param> /// <returns></returns> public static Composite CreateAutoAttack(bool includePet) { PrioritySelector prio = new PrioritySelector(); // const int spellIdAutoShot = 75; bool autoAttack = Me.Class == WoWClass.DeathKnight || (Me.Class == WoWClass.Druid && Me.Specialization != WoWSpec.DruidRestoration) || Me.Class == WoWClass.Monk || (Me.Class == WoWClass.Paladin && Me.Specialization != WoWSpec.PaladinHoly) || Me.Class == WoWClass.Rogue || (Me.Class == WoWClass.Shaman && Me.Specialization != WoWSpec.ShamanRestoration) || Me.Class == WoWClass.Warrior; if (autoAttack) { prio.AddChild( new Throttle(TimeSpan.FromMilliseconds(500), new Decorator( ret => !StyxWoW.Me.IsAutoAttacking, // && StyxWoW.Me.AutoRepeatingSpellId != spellIdAutoShot, new Action(ret => { Lua.DoString("StartAttack()"); return RunStatus.Failure; }) ) ) ); } if ( includePet && (SingularRoutine.CurrentWoWContext != WoWContext.Normal || !SingularSettings.Instance.PetTankAdds )) { // pet assist: always keep pet on my target prio.AddChild( new ThrottlePasses(TimeSpan.FromMilliseconds(500), new Decorator( // check pet targeting same target as Me ret => Me.GotAlivePet && (!Me.Pet.GotTarget || Me.Pet.CurrentTargetGuid != Me.CurrentTargetGuid), new Action( delegate { PetManager.CastPetAction("Attack", Me.CurrentTarget); return RunStatus.Failure; }) ) ) ); } if ( includePet && SingularRoutine.CurrentWoWContext == WoWContext.Normal && SingularSettings.Instance.PetTankAdds ) { // pet tank: if pet's target isn't targeting Me, check if we should switch to one that is targeting Me prio.AddChild( new ThrottlePasses(TimeSpan.FromMilliseconds(500), new Decorator( ret => Me.GotAlivePet && (!Me.Pet.GotTarget || Me.Pet.CurrentTarget.CurrentTargetGuid != Me.Guid), new PrioritySelector( ctx => Unit.NearbyUnfriendlyUnits.Where(u => u.Combat && u.GotTarget && u.CurrentTarget.IsMe).FirstOrDefault() ?? Me.CurrentTarget, new Decorator( ret => ret != null && Me.Pet.CurrentTargetGuid != ((WoWUnit)ret).Guid, new Action(r => { PetManager.CastPetAction("Attack", (WoWUnit)r); return RunStatus.Failure; }) ) ) ) ) ); } return prio; }
public static Composite CreatePsychicScreamBehavior(int health = -1) { if (health < 0) health = PriestSettings.PsychicScreamHealth; PrioritySelector pri = new PrioritySelector(); if (PriestSettings.CrowdControlHealth > 0 && SpellManager.HasSpell("Psychic Horror")) pri.AddChild( Spell.Cast( "Psychic Horror", ret => { if (Me.HealthPercent <= PriestSettings.CrowdControlHealth) { int count = Unit.UnitsInCombatWithUsOrOurStuff(30).Count(u => !u.IsCrowdControlled() && !u.IsTrivial()); if (count == 0) return false; if (count == 1 || !PriestSettings.PsychicScreamAllow || Spell.GetSpellCooldown("Psychic Scream").TotalSeconds > 3) { return true; } } return false; }) ); if (PriestSettings.PsychicScreamAllow && SpellManager.HasSpell("Psychic Scream")) pri.AddChild( Spell.Cast( "Psychic Scream", ret => { if (!PriestSettings.PsychicScreamAllow) return false; int count = Unit.UnitsInCombatWithUsOrOurStuff(8).Count(u => !u.IsCrowdControlled() && !u.IsTrivial()); if (count == 0) return false; if (count >= PriestSettings.PsychicScreamAddCount) return true; if (count > 1 && TalentManager.HasGlyph("Psychic Scream")) return true; if (Me.HealthPercent <= health) return true; return false; }) ); return pri; }
public static Composite CreateDispelBehavior() { if (SingularSettings.Instance.DispelDebuffs == RelativePriority.None) return new ActionAlwaysFail(); PrioritySelector prio = new PrioritySelector(); switch ( StyxWoW.Me.Class) { case WoWClass.Paladin: prio.AddChild( Spell.Cast( "Cleanse", on => _unitDispel)); break; case WoWClass.Monk: prio.AddChild( Spell.Cast( "Detox", on => _unitDispel)); break; case WoWClass.Priest: if ( StyxWoW.Me.Specialization == WoWSpec.PriestHoly || StyxWoW.Me.Specialization == WoWSpec.PriestDiscipline ) prio.AddChild( Spell.Cast( "Purify", on => _unitDispel)); break; case WoWClass.Druid: if ( StyxWoW.Me.Specialization == WoWSpec.DruidRestoration ) prio.AddChild( Spell.Cast( "Nature's Cure", on => _unitDispel)); else prio.AddChild( Spell.Cast( "Remove Corruption", on => _unitDispel)); break; case WoWClass.Shaman: if ( StyxWoW.Me.Specialization == WoWSpec.ShamanRestoration ) prio.AddChild(Spell.Cast("Purify Spirit", on => _unitDispel)); else prio.AddChild(Spell.Cast("Cleanse Spirit", on => _unitDispel)); break; case WoWClass.Mage: prio.AddChild(Spell.Cast("Remove Curse", on => _unitDispel)); break; } return new Sequence( new Action(r => _unitDispel = HealerManager.Instance.TargetList.FirstOrDefault(u => u.IsAlive && CanDispel(u))), prio ); }
// [Behavior(BehaviorType.Combat, priority: 998)] public static Composite CreateRacialBehaviour() { PrioritySelector pri = new PrioritySelector(); if (SpellManager.HasSpell("Stoneform")) { pri.AddChild( new Decorator( ret => { if (!Spell.CanCastHack("Stoneform")) return false; if (StyxWoW.Me.GetAllAuras().Any(a => a.Spell.Mechanic == WoWSpellMechanic.Bleeding || a.Spell.DispelType == WoWDispelType.Disease || a.Spell.DispelType == WoWDispelType.Poison)) return true; if (Unit.NearbyUnitsInCombatWithMeOrMyStuff.Count() > 2) return true; if (StyxWoW.Me.GotTarget() && StyxWoW.Me.CurrentTarget.CurrentTargetGuid == StyxWoW.Me.Guid && StyxWoW.Me.CurrentTarget.MaxHealth > (StyxWoW.Me.MaxHealth * 2)) return true; return false; }, Spell.HandleOffGCD(Spell.BuffSelf("Stoneform")) ) ); } if (SpellManager.HasSpell("Escape Artist")) { pri.AddChild( Spell.HandleOffGCD(Spell.BuffSelf("Escape Artist", req => Unit.HasAuraWithMechanic(StyxWoW.Me, WoWSpellMechanic.Rooted, WoWSpellMechanic.Snared))) ); } if (SpellManager.HasSpell("Gift of the Naaru")) { pri.AddChild( Spell.HandleOffGCD(Spell.BuffSelf("Gift of the Naaru", req => StyxWoW.Me.HealthPercent < SingularSettings.Instance.GiftNaaruHP)) ); } if (SpellManager.HasSpell("Shadowmeld")) { pri.AddChild( // even though not on GCD, return success so we resume at top of tree new Sequence( Spell.BuffSelf("Shadowmeld", ret => NeedShadowmeld()), new Action( r => shadowMeldStart = DateTime.UtcNow ) ) ); } // add racials cast within range during Combat Composite combatRacials = CreateCombatRacialInRangeBehavior(); if (combatRacials != null) pri.AddChild(combatRacials); // just fail if no combat racials if (!SingularSettings.Instance.UseRacials || !pri.Children.Any() || SuppressGenericRacialBehavior) return new ActionAlwaysFail(); return new Throttle( TimeSpan.FromMilliseconds(250), new Decorator( req => !Spell.IsGlobalCooldown() && !Spell.IsCastingOrChannelling() && !SuppressGenericRacialBehavior, pri ) ); }
public Composite GenerateBehaviorTree() { if ( !SingularSettings.Debug ) return new PrioritySelector(blist.Select(b => b.behavior).ToArray()); PrioritySelector pri = new PrioritySelector(); foreach (PrioritizedBehavior pb in blist) { pri.AddChild(new CallTrace(pb.Name, pb.behavior)); } return pri; }
public static Composite CreateTotemsInstanceBehavior() { // create Fire Totems behavior first, then wrap if needed PrioritySelector fireTotemBehavior = new PrioritySelector(); fireTotemBehavior.AddChild( Spell.Buff( "Fire Elemental Totem", req => Me.CurrentTarget.IsBoss() && AllowElementalTotems ) ); if (TalentManager.CurrentSpec == WoWSpec.ShamanEnhancement) { fireTotemBehavior.AddChild( Spell.Cast("Magma Totem", on => Me.CurrentTarget ?? Me, ret => IsMagmaTotemNeeded()) ); } if (TalentManager.CurrentSpec == WoWSpec.ShamanRestoration) fireTotemBehavior = new PrioritySelector( new Decorator( ret => StyxWoW.Me.Combat && StyxWoW.Me.GotTarget() && !HealerManager.Instance.TargetList.Any(m => m.IsAlive), fireTotemBehavior ) ); else fireTotemBehavior.AddChild( Spell.Cast("Searing Totem", ret => IsSearingTotemNeeded()) ); // now return new PrioritySelector( Spell.BuffSelf(WoWTotem.Tremor.ToSpellId(), ret => Unit.GroupMembers.Any(f => f.Fleeing && f.Distance < Totems.GetTotemRange(WoWTotem.Tremor)) && !Exist(WoWTotem.StoneBulwark, WoWTotem.EarthElemental)), new Decorator( req => Totems.ExistInRange(Me.Location, WoWTotem.EarthElemental) && !Me.HasAura("Reinforce") && !Spell.DoubleCastContains(Me, "Reinforce"), new Sequence( PetManager.CastAction("Reinforce", on => Me), new Action(r => Spell.UpdateDoubleCast("Reinforce", Me)) ) ), new Decorator( req => Totems.ExistInRange(Me.Location, WoWTotem.FireElemental) && !Me.HasAura("Empower") && !Spell.DoubleCastContains(Me, "Empower"), new Sequence( PetManager.CastAction("Empower", on => Me), new Action(r => Spell.UpdateDoubleCast("Empower", Me)) ) ), new Decorator( ret => ShouldWeDropTotemsYet, new PrioritySelector( // earth totems Spell.Cast(WoWTotem.EarthElemental.ToSpellId(), on => Me.CurrentTarget ?? Me, req => { if (!Totems.AllowElementalTotems) return false; if (Exist(WoWTotem.StoneBulwark)) return false; if (Spell.IsSpellOnCooldown("Earth Elemental Totem")) return false; // no living tanks in range IEnumerable<WoWUnit> tanks = Group.Tanks.Where(u => u.DistanceSqr < 65 * 65); if (!tanks.Any(t => t.IsAlive) || tanks.Any( t => t.IsDead)) { // we are okay if another Earth Elemental active if ( ObjectManager.GetObjectsOfType<WoWUnit>(false, false).Any( o => o.Entry == 15352)) return false; // we are okay if nothing in combat with our group if (!Unit.NearbyUnitsInCombatWithUsOrOurStuff.Any()) return false; } // check we can cast it before messaging if (!Spell.CanCastHack("Earth Elemental Totem")) return false; Logger.Write(LogColor.Hilite, "^Earth Elemental Totem: tank is dead or not nearby"); return true; }), // Stone Bulwark handled in CombatBuffs with Astral Shift // fire totems fireTotemBehavior, // water totems Spell.BuffSelf("Mana Tide Totem", ret => { if (TalentManager.CurrentSpec != WoWSpec.ShamanRestoration) return false; // Logger.WriteDebug("Mana Tide Totem Check: current mana {0:F1}%", Me.ManaPercent); if (Me.ManaPercent > ShamanSettings.ManaTideTotemPercent) return false; if (Exist(WoWTotem.HealingTide, WoWTotem.HealingStream)) return false; return true; }), /* Healing...: handle within Helaing logic Spell.Cast("Healing Tide Totem", ret => ((bool)ret) && StyxWoW.Me.HealthPercent < 50 && !Exist(WoWTotem.ManaTide)), Spell.Cast("Healing Stream Totem", ret => ((bool)ret) && StyxWoW.Me.HealthPercent < 80 && !Exist( WoWTotemType.Water)), */ // air totems Spell.Cast("Stormlash Totem", ret => PartyBuff.WeHaveBloodlust && !Me.HasAura("Stormlash Totem")), new Decorator( ret => !Exist(WoWTotemType.Air), new PrioritySelector( Spell.Cast("Grounding Totem", on => Unit.NearbyUnfriendlyUnits.FirstOrDefault(u => u.SpellDistance() < 40 && u.IsTargetingMeOrPet && u.IsCasting)), Spell.Cast("Capacitor Totem", on => Unit.NearbyUnfriendlyUnits.FirstOrDefault(u => u.SpellDistance() < GetTotemRange(WoWTotem.Capacitor) && u.IsTargetingMeOrPet)), Spell.BuffSelf("Windwalk Totem", ret => Unit.HasAuraWithMechanic(StyxWoW.Me, WoWSpellMechanic.Rooted, WoWSpellMechanic.Snared)) ) ) ) ) ); }
public static Composite CreateTotemsNormalBehavior() { // create Fire Totems behavior first, then wrap if needed Composite fireTotemBehavior = new PrioritySelector( Spell.BuffSelf("Fire Elemental", ret => ((bool)ret) || (Unit.NearbyUnitsInCombatWithMe.Count() >= StressMobCount && !SpellManager.CanBuff(WoWTotem.EarthElemental.ToSpellId(), false))), /* Magma - handle within AoE DPS logic only Spell.BuffSelf("Magma Totem", ret => Unit.NearbyUnitsInCombatWithMe.Count(u => u.Distance <= GetTotemRange(WoWTotem.Magma)) >= StressMobCount && !Exist( WoWTotem.FireElemental)), */ Spell.BuffSelf("Searing Totem", ret => Me.GotTarget && Me.CurrentTarget.Distance < GetTotemRange(WoWTotem.Searing) - 2f && !Exist( WoWTotemType.Fire)) ); if ( Me.Specialization == WoWSpec.ShamanRestoration ) fireTotemBehavior = new Decorator(ret => StyxWoW.Me.Combat && StyxWoW.Me.GotTarget && Unit.NearbyFriendlyPlayers.Count(u => u.IsInMyPartyOrRaid) == 0, fireTotemBehavior); // now return new PrioritySelector( // check for stress - enemy player or elite within 8 levels nearby // .. dont use NearbyUnitsInCombatWithMe since it checks .Tagged and we only care if we are being attacked ctx => Unit.NearbyUnitsInCombatWithMe.Count() >= StressMobCount || Unit.NearbyUnfriendlyUnits.Any(u => u.IsTargetingMeOrPet && (u.IsPlayer || (u.Elite && u.Level + 8 > Me.Level))), // earth totems Spell.BuffSelf(WoWTotem.EarthElemental.ToSpellId(), ret => (bool) ret && !Exist( WoWTotem.StoneBulwark)), Spell.BuffSelf(WoWTotem.StoneBulwark.ToSpellId(), ret => Me.HealthPercent < SingularSettings.Instance.Shaman.StoneBulwarkTotemPercent && !Exist( WoWTotem.EarthElemental)), Spell.BuffSelf(WoWTotem.Tremor.ToSpellId(), ret => Unit.GroupMembers.Any( f=> f.Fleeing && f.Distance < Totems.GetTotemRange(WoWTotem.Tremor) && !Exist( WoWTotem.StoneBulwark, WoWTotem.EarthElemental ))), new PrioritySelector( ctx => Unit.NearbyUnfriendlyUnits.Any(u => u.IsTargetingMeOrPet && u.IsPlayer && u.Combat), Spell.BuffSelf(WoWTotem.Earthgrab.ToSpellId(), ret => (bool)ret && !Exist(WoWTotemType.Earth)), Spell.BuffSelf(WoWTotem.Earthbind.ToSpellId(), ret => (bool)ret && !Exist(WoWTotemType.Earth)) ), // fire totems fireTotemBehavior, // water totems Spell.Cast("Mana Tide Totem", ret => ((bool)ret) && StyxWoW.Me.ManaPercent < 80 && !Exist( WoWTotem.HealingTide )), /* Healing...: handle within Helaing logic Spell.Cast("Healing Tide Totem", ret => ((bool)ret) && StyxWoW.Me.HealthPercent < 50 && !Exist(WoWTotem.ManaTide)), Spell.Cast("Healing Stream Totem", ret => ((bool)ret) && StyxWoW.Me.HealthPercent < 80 && !Exist( WoWTotemType.Water)), */ // air totems Spell.Cast("Grounding Totem", ret => ((bool) ret) && Unit.NearbyUnfriendlyUnits.Any(u => u.Distance < 40 && u.IsTargetingMeOrPet && u.IsCasting) && !Exist(WoWTotemType.Air)), Spell.Cast("Capacitor Totem", ret => ((bool) ret) && Unit.NearbyUnfriendlyUnits.Any(u => u.Distance < GetTotemRange(WoWTotem.Capacitor)) && !Exist(WoWTotemType.Air)), Spell.Cast("Stormlash Totem", ret => ((bool)ret) && Me.HasAnyAura( Common.BloodlustName, "Timewarp", "Ancient Hysteria") && !Exist(WoWTotemType.Air)) ); }
protected override Composite CreateBehavior() { PrioritySelector decorated=new PrioritySelector(new Composite[0]); foreach (ProfileBehavior behavior in base.GetNodes()) { decorated.AddChild(behavior.Behavior); } // Logging.Write("Count: "); return new Zeta.TreeSharp.Decorator(new CanRunDecoratorDelegate(CheckNotAlreadyDone), decorated); }
public static Composite EnsureReadyToAttackFromMelee() { PrioritySelector prio = new PrioritySelector( Safers.EnsureTarget() , Movement.CreateMoveToLosBehavior(), Movement.CreateFaceTargetBehavior(), new Decorator( req => Me.GotTarget && Me.CurrentTarget.Distance < SingularSettings.Instance.MeleeDismountRange, Helpers.Common.CreateDismount( Dynamics.CompositeBuilder.CurrentBehaviorType.ToString()) // should be Pull or Combat 99% of the time ) ); if (Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Pull) { prio.AddChild( new PrioritySelector( ctx => Me.GotTarget && Me.CurrentTarget.IsAboveTheGround(), new Decorator( req => (bool)req, new PrioritySelector( Movement.CreateMoveToUnitBehavior(on => Me.CurrentTarget, 27, 22), Movement.CreateEnsureMovementStoppedBehavior(22) ) ), new Decorator( req => !(bool)req, new PrioritySelector( Movement.CreateMoveToMeleeBehavior(true), Movement.CreateEnsureMovementStoppedWithinMelee() ) ) ) ); } else { prio.AddChild( Movement.CreateMoveToMeleeBehavior(true)); prio.AddChild(Movement.CreateEnsureMovementStoppedWithinMelee()); } return prio; }
/// <summary>Creates an interrupt spell cast composite. This attempts to use spells in order of range (shortest to longest). /// behavior consists only of spells that apply to current toon based upon class, spec, and race /// </summary> public static Composite CreateInterruptBehavior() { if ( SingularSettings.Instance.InterruptTarget == CheckTargets.None ) return new ActionAlwaysFail(); Composite actionSelectTarget; if (SingularSettings.Instance.InterruptTarget == CheckTargets.Current) actionSelectTarget = new Action( ret => { WoWUnit u = Me.CurrentTarget; _unitInterrupt = IsInterruptTarget(u) ? u : null; if (_unitInterrupt != null && SingularSettings.Debug) Logger.WriteDebug("Possible Interrupt Target: {0} @ {1:F1} yds casting {2} #{3} for {4} ms", _unitInterrupt.SafeName(), _unitInterrupt.Distance, _unitInterrupt.CastingSpell.Name, _unitInterrupt.CastingSpell.Id, _unitInterrupt.CurrentCastTimeLeft.TotalMilliseconds ); } ); else // if ( SingularSettings.Instance.InterruptTarget == InterruptType.All ) { actionSelectTarget = new Action( ret => { _unitInterrupt = Unit.NearbyUnitsInCombatWithMeOrMyStuff.Where(u => IsInterruptTarget(u)).OrderBy( u => u.Distance ).FirstOrDefault(); if (_unitInterrupt != null && SingularSettings.Debug) Logger.WriteDebug("Possible Interrupt Target: {0} @ {1:F1} yds casting {2} #{3} for {4} ms", _unitInterrupt.SafeName(), _unitInterrupt.Distance, _unitInterrupt.CastingSpell.Name, _unitInterrupt.CastingSpell.Id, _unitInterrupt.CurrentCastTimeLeft.TotalMilliseconds); } ); } PrioritySelector prioSpell = new PrioritySelector(); #region Pet Spells First! if (Me.Class == WoWClass.Warlock) { // this will be either a Optical Blast or Spell Lock prioSpell.AddChild( Spell.Cast( "Command Demon", on => _unitInterrupt, ret => _unitInterrupt != null && _unitInterrupt.Distance < 40 && Singular.ClassSpecific.Warlock.Common.GetCurrentPet() == WarlockPet.Felhunter ) ); } #endregion #region Melee Range if ( Me.Class == WoWClass.Paladin ) prioSpell.AddChild( Spell.Cast("Rebuke", ctx => _unitInterrupt)); if ( Me.Class == WoWClass.Rogue) { prioSpell.AddChild( Spell.Cast("Kick", ctx => _unitInterrupt)); prioSpell.AddChild( Spell.Cast("Gouge", ctx => _unitInterrupt, ret => !_unitInterrupt.IsBoss() && Me.IsSafelyFacing(_unitInterrupt))); } if ( Me.Class == WoWClass.Warrior) prioSpell.AddChild( Spell.Cast("Pummel", ctx => _unitInterrupt)); if ( Me.Class == WoWClass.Monk ) prioSpell.AddChild( Spell.Cast("Spear Hand Strike", ctx => _unitInterrupt)); if ( Me.Class == WoWClass.Druid) { // Spell.Cast("Skull Bash (Cat)", ctx => _unitInterrupt, ret => StyxWoW.Me.Shapeshift == ShapeshiftForm.Cat)); // Spell.Cast("Skull Bash (Bear)", ctx => _unitInterrupt, ret => StyxWoW.Me.Shapeshift == ShapeshiftForm.Bear)); prioSpell.AddChild( Spell.Cast("Skull Bash", ctx => _unitInterrupt, ret => StyxWoW.Me.Shapeshift == ShapeshiftForm.Bear || StyxWoW.Me.Shapeshift == ShapeshiftForm.Cat)); prioSpell.AddChild( Spell.Cast("Mighty Bash", ctx => _unitInterrupt, ret => !_unitInterrupt.IsBoss() && _unitInterrupt.IsWithinMeleeRange)); } if ( Me.Class == WoWClass.DeathKnight) prioSpell.AddChild( Spell.Cast("Mind Freeze", ctx => _unitInterrupt)); if ( Me.Race == WoWRace.Pandaren ) prioSpell.AddChild( Spell.Cast("Quaking Palm", ctx => _unitInterrupt)); #endregion #region 8 Yard Range if ( Me.Race == WoWRace.BloodElf ) prioSpell.AddChild(Spell.Cast("Arcane Torrent", ctx => _unitInterrupt, req => _unitInterrupt.Distance < 8 && !Unit.NearbyUnfriendlyUnits.Any(u => u.IsSensitiveDamage( 8)))); if ( Me.Race == WoWRace.Tauren) prioSpell.AddChild(Spell.Cast("War Stomp", ctx => _unitInterrupt, ret => _unitInterrupt.Distance < 8 && !_unitInterrupt.IsBoss() && !Unit.NearbyUnfriendlyUnits.Any(u => u.IsSensitiveDamage( 8)))); #endregion #region 10 Yards if (Me.Class == WoWClass.Paladin) prioSpell.AddChild( Spell.Cast("Hammer of Justice", ctx => _unitInterrupt)); if (Me.Specialization == WoWSpec.DruidBalance ) prioSpell.AddChild( Spell.Cast("Hammer of Justice", ctx => _unitInterrupt)); if (Me.Class == WoWClass.Warrior) prioSpell.AddChild( Spell.Cast("Disrupting Shout", ctx => _unitInterrupt)); #endregion #region 25 yards if ( Me.Class == WoWClass.Shaman) prioSpell.AddChild( Spell.Cast("Wind Shear", ctx => _unitInterrupt, req => Me.IsSafelyFacing(_unitInterrupt))); #endregion #region 30 yards if (Me.Specialization == WoWSpec.PaladinProtection) prioSpell.AddChild( Spell.Cast("Avenger's Shield", ctx => _unitInterrupt)); if (Me.Class == WoWClass.Shaman) // Gag Order only works on non-bosses due to it being a silence, not an interrupt! prioSpell.AddChild( Spell.Cast("Heroic Throw", ctx => _unitInterrupt, ret => TalentManager.HasGlyph("Gag Order") && !_unitInterrupt.IsBoss())); if ( Me.Class == WoWClass.Priest ) prioSpell.AddChild( Spell.Cast("Silence", ctx => _unitInterrupt)); if (Me.Class == WoWClass.DeathKnight) prioSpell.AddChild(Spell.Cast("Strangulate", ctx => _unitInterrupt)); if (Me.Class == WoWClass.Mage) prioSpell.AddChild(Spell.Cast("Frostjaw", ctx => _unitInterrupt)); #endregion #region 40 yards if ( Me.Class == WoWClass.Mage) prioSpell.AddChild( Spell.Cast("Counterspell", ctx => _unitInterrupt)); if ( Me.Class == WoWClass.Hunter) prioSpell.AddChild( Spell.Cast("Silencing Shot", ctx => _unitInterrupt)); if ( Me.Class == WoWClass.Druid) prioSpell.AddChild( Spell.Cast("Solar Beam", ctx => _unitInterrupt, ret => StyxWoW.Me.Shapeshift == ShapeshiftForm.Moonkin)); if (Me.Specialization == WoWSpec.ShamanElemental || Me.Specialization == WoWSpec.ShamanEnhancement ) prioSpell.AddChild( Spell.Cast("Solar Beam", ctx => _unitInterrupt, ret => true)); #endregion return new Throttle( new Sequence( actionSelectTarget, new Decorator( ret => _unitInterrupt != null, // majority of these are off GCD, so throttle all to avoid most fail messages new Throttle( TimeSpan.FromMilliseconds(500), prioSpell ) ) ) ); }
public static Composite CreateTotemsNormalBehavior() { // create Fire Totems behavior first, then wrap if needed PrioritySelector fireTotemBehavior = new PrioritySelector(); fireTotemBehavior.AddChild( Spell.Buff(WoWTotem.FireElemental.ToSpellId(), req => Common.StressfulSituation && Totems.AllowElementalTotems && !Exist(WoWTotem.EarthElemental) && !Spell.CanCastHack("Earth Elemental Totem") ) ); if (TalentManager.CurrentSpec == WoWSpec.ShamanEnhancement) { fireTotemBehavior.AddChild( Spell.Cast("Magma Totem", on => Me.CurrentTarget ?? Me, ret => IsMagmaTotemNeeded()) ); } fireTotemBehavior.AddChild( Spell.BuffSelf("Searing Totem", ret => IsSearingTotemNeeded() ) ); if (TalentManager.CurrentSpec == WoWSpec.ShamanRestoration) { fireTotemBehavior = new PrioritySelector( new Decorator( ret => StyxWoW.Me.Combat && StyxWoW.Me.GotTarget() && !Unit.NearbyGroupMembers.Any(), fireTotemBehavior ) ); } // now return new PrioritySelector( new Throttle(1, new Action(r => { bool ccMechanic = Me.HasAuraWithMechanic(WoWSpellMechanic.Fleeing | WoWSpellMechanic.Polymorphed | WoWSpellMechanic.Asleep); bool ccEffect = Me.HasAuraWithEffect(WoWApplyAuraType.ModFear | WoWApplyAuraType.ModPacify | WoWApplyAuraType.ModPacifySilence); bool ccAttrib = Me.Fleeing; if (ccMechanic || ccEffect || ccAttrib) Logger.WriteDebug(Color.Pink, "... FEAR CHECKED OUT -- Mechanic={0} Effect={1} Attrib={2}", ccMechanic, ccEffect, ccAttrib); return RunStatus.Failure; }) ), Spell.BuffSelf(WoWTotem.Tremor.ToSpellId(), ret => Unit.GroupMembers.Any(f => f.Fleeing && f.Distance < Totems.GetTotemRange(WoWTotem.Tremor)) && !Exist(WoWTotem.StoneBulwark, WoWTotem.EarthElemental)), new Decorator( req => PetManager.NeedsPetSupport && Totems.ExistInRange(Me.Location, WoWTotem.EarthElemental) && !Me.HasAura("Reinforce") && !Spell.DoubleCastContains(Me, "Reinforce"), new Sequence( PetManager.CastAction("Reinforce", on => Me), new Action(r => Spell.UpdateDoubleCast("Reinforce", Me)) ) ), new Decorator( req => PetManager.NeedsPetSupport && Totems.ExistInRange(Me.Location, WoWTotem.FireElemental) && !Me.HasAura("Empower") && !Spell.DoubleCastContains(Me, "Empower"), new Sequence( PetManager.CastAction("Empower", on => Me), new Action(r => Spell.UpdateDoubleCast("Empower", Me)) ) ), new Decorator( ret => ShouldWeDropTotemsYet, new PrioritySelector( // check for stress - enemy player or elite within 8 levels nearby // .. dont use NearbyUnitsInCombatWithMe since it checks .Tagged and we only care if we are being attacked ctx => Common.StressfulSituation, // earth totems Spell.BuffSelf(WoWTotem.EarthElemental.ToSpellId(), ret => ((bool)ret || Group.Tanks.Any(t => t.IsDead && t.Distance < 40)) && Totems.AllowElementalTotems && !Exist(WoWTotem.StoneBulwark) ), Spell.BuffSelf(WoWTotem.StoneBulwark.ToSpellId(), ret => Me.Combat && Me.HealthPercent < ShamanSettings.StoneBulwarkTotemPercent && !Exist(WoWTotem.EarthElemental)), new PrioritySelector( ctx => Unit.NearbyUnfriendlyUnits.Any(u => u.IsTargetingMeOrPet && u.IsPlayer && u.Combat), Spell.BuffSelf(WoWTotem.Earthgrab.ToSpellId(), ret => (bool)ret && !Exist(WoWTotemType.Earth)), Spell.BuffSelf(WoWTotem.Earthbind.ToSpellId(), ret => (bool)ret && !Exist(WoWTotemType.Earth)) ), // fire totems fireTotemBehavior, // water totems Spell.BuffSelf("Mana Tide Totem", ret => { if (TalentManager.CurrentSpec != WoWSpec.ShamanRestoration) return false; // Logger.WriteDebug("Mana Tide Totem Check: current mana {0:F1}%", Me.ManaPercent); if (Me.ManaPercent > ShamanSettings.ManaTideTotemPercent ) return false; if (Exist(WoWTotem.HealingTide, WoWTotem.HealingStream)) return false; return true; }), /* Healing...: handle within Helaing logic Spell.Cast("Healing Tide Totem", ret => ((bool)ret) && StyxWoW.Me.HealthPercent < 50 && !Exist(WoWTotem.ManaTide)), Spell.Cast("Healing Stream Totem", ret => ((bool)ret) && StyxWoW.Me.HealthPercent < 80 && !Exist( WoWTotemType.Water)), */ // air totems Spell.Cast("Stormlash Totem", ret => PartyBuff.WeHaveBloodlust && !Me.HasAura("Stormlash Totem")), new Decorator( ret => !Exist(WoWTotemType.Air), new PrioritySelector( Spell.Cast("Grounding Totem", ret => Unit.NearbyUnfriendlyUnits.Any(u => u.SpellDistance() < 40 && u.IsTargetingMeOrPet && u.IsCasting)), Spell.Cast("Capacitor Totem", ret => ((bool)ret) && Unit.NearbyUnfriendlyUnits.Any(u => u.SpellDistance() < GetTotemRange(WoWTotem.Capacitor))), Spell.BuffSelf("Windwalk Totem", ret => Unit.HasAuraWithMechanic(StyxWoW.Me, WoWSpellMechanic.Rooted, WoWSpellMechanic.Snared)) ) ) ) ) ); }
public void RebuildBehaviors() { Composite simcRoot = null; NameCount = 'A'; simcRoot = new PrioritySelector( new Action(context => { iterationCounter++; return RunStatus.Failure; }), new Decorator(ret => IsPaused, new Action(ret => RunStatus.Success)), CallActionList(ActionProxy.ActionImpl.oocapl, ret => !StyxWoW.Me.Combat), new Decorator(ret => StyxWoW.Me.Combat, new LockSelector( new Decorator( ret => StyxWoW.Me.CurrentTarget != null && StyxWoW.Me.Combat, new PrioritySelector(CombatIteration(), actions.Selector, IterationEnd()))))); TreeHooks.Instance.ReplaceHook(SimcraftHookName, simcRoot); //if (Me.Specialization != Specialisation) //{ // SelectorWindow.ShowDialog(); //} Specialisation = Me.Specialization; }
public static Composite CreateTotemsNormalBehavior() { // create Fire Totems behavior first, then wrap if needed Composite fireTotemBehavior = new PrioritySelector( // Spell.CastSelfSpell("Fire Elemental", // ret => ((bool)ret) // || (Unit.EnemyUnits.Count() >= StressMobCount && !SpellManager.CanBuff(WoWTotem.EarthElemental.ToSpellId(), false)), "Fire Elemental"), // Magma - handle within AoE DPS logic only Spell.CastSpell("Magma Totem", ret => Unit.CountEnnemiesInRange(Me.CurrentTarget.Location, 20) >= 5 && !Exist(WoWTotemType.Fire) && !Totems.Exist(WoWTotem.FireElemental),"Magma Totem"), Spell.CastSpell("Searing Totem", ret => !Totems.Exist(WoWTotemType.Fire), "Searing Totem") ); if (Me.Specialization == WoWSpec.ShamanRestoration) fireTotemBehavior = new Decorator(ret => StyxWoW.Me.Combat && StyxWoW.Me.GotTarget && !Me.GroupInfo.IsInParty, fireTotemBehavior); // now return new PrioritySelector( // check for stress - enemy player or elite within 8 levels nearby // .. dont use NearbyUnitsInCombatWithMe since it checks .Tagged and we only care if we are being attacked ctx => Unit.EnemyMeleeUnits.Count() >= StressMobCount, // earth totems // Spell.CastSelfSpell(WoWTotem.EarthElemental.ToSpellId(), // ret => (bool)ret && !Exist(WoWTotem.StoneBulwark),"Earth Elemental"), Spell.CastSelfSpell(WoWTotem.StoneBulwark.ToSpellId(), ret => Me.HealthPercent < 50 && !Exist(WoWTotem.EarthElemental),"Stone Bulwark"), new PrioritySelector( ctx => Unit.EnemyRangedUnits.Any(u => u.IsTargetingMeOrPet && u.IsPlayer && u.Combat), Spell.CastSelfSpell(WoWTotem.Earthgrab.ToSpellId(), ret => (bool)ret && !Exist(WoWTotem.StoneBulwark, WoWTotem.EarthElemental, WoWTotem.Earthbind),"Earth Grab"), Spell.CastSelfSpell(WoWTotem.Earthbind.ToSpellId(), ret => (bool)ret && !Exist(WoWTotem.StoneBulwark, WoWTotem.EarthElemental, WoWTotem.Earthgrab), "Earth Bind") ), Spell.CastSelfSpell(WoWTotem.Tremor.ToSpellId(), ret => Me.Fleeing && !Exist(WoWTotem.StoneBulwark, WoWTotem.EarthElemental), "Tremor"), // fire totems fireTotemBehavior, // water totems Spell.CastSpell("Mana Tide Totem", ret => ((bool)ret) && StyxWoW.Me.ManaPercent < 80 && !Exist(WoWTotem.HealingTide),"Mana Tide Totem"), /* Healing...: handle within Helaing logic Spell.Cast("Healing Tide Totem", ret => ((bool)ret) && StyxWoW.Me.HealthPercent < 50 && !Exist(WoWTotem.ManaTide)), Spell.Cast("Healing Stream Totem", ret => ((bool)ret) && StyxWoW.Me.HealthPercent < 80 && !Exist( WoWTotemType.Water)), */ // air totems Spell.CastSpell("Grounding Totem", ret => ((bool)ret) && Unit.EnemyRangedUnits.Any(u => u.IsCasting) && !Exist(WoWTotemType.Air), "Grounding Totem"), Spell.CastSpell("Capacitor Totem", ret => ((bool)ret) && Unit.EnemyRangedUnits.Any(u => u.DistanceSqr < GetTotemRange(WoWTotem.Capacitor) * GetTotemRange(WoWTotem.Capacitor)) && !Exist(WoWTotemType.Air),"Capacitor Totem"), Spell.CastSpell("Stormlash Totem", ret => ((bool)ret) && ((CLUSettings.Instance.Shaman.UseStormlashTotem == StormlashTotem.OnHaste && Me.HasAnyAura(Me.IsHorde ? "Bloodlust" : "Heroism", "Timewarp", "Ancient Hysteria") || CLUSettings.Instance.Shaman.UseStormlashTotem == StormlashTotem.OnCooldown) && !Exist(WoWTotemType.Air)),"Stormlash Totem") ); }
/// <summary> /// creates a Druid specific avoidance behavior based upon settings. will check for safe landing /// zones before using WildCharge or rocket jump. will additionally do a running away or jump turn /// attack while moving away from attacking mob if behaviors provided /// </summary> /// <param name="nonfacingAttack">behavior while running away (back to target - instants only)</param> /// <param name="jumpturnAttack">behavior while facing target during jump turn (instants only)</param> /// <returns></returns> public static Composite CreateMageAvoidanceBehavior(Composite nonfacingAttack = null, Composite jumpturnAttack = null) { Kite.CreateKitingBehavior(CreateSlowMeleeBehavior(), nonfacingAttack, jumpturnAttack); PrioritySelector pri = new PrioritySelector(); if (SingularSettings.Instance.DisengageAllowed) { pri.AddChild( new Decorator( ret => Kite.IsDisengageWantedByUserSettings(), new PrioritySelector( Disengage.CreateDisengageBehavior("Blink", Disengage.Direction.Frontwards, 20, CreateSlowMeleeBehavior()), Disengage.CreateDisengageBehavior("Rocket Jump", Disengage.Direction.Frontwards, 20, CreateSlowMeleeBehavior()) ) ) ); } if (SingularSettings.Instance.KiteAllow) { pri.AddChild( new Decorator( ret => Kite.IsKitingWantedByUserSettings(), new Sequence( new Action( r => Logger.WriteDebug("MageAvoidance: requesting KITING!!!")), Kite.BeginKitingBehavior() ) ) ); } if (!pri.Children.Any()) { pri.AddChild(new ActionAlwaysFail()); } return new Decorator( req => MovementManager.IsClassMovementAllowed, pri ); }
private static Composite CreateCombatRacialInRangeBehavior() { PrioritySelector priCombat = new PrioritySelector(); // not a racial, but best place to handle it if (SpellManager.HasSpell("Lifeblood")) { priCombat.AddChild( Spell.HandleOffGCD(Spell.BuffSelf("Lifeblood", ret => !PartyBuff.WeHaveBloodlust)) ); } if (SpellManager.HasSpell("Berserking")) { priCombat.AddChild( Spell.HandleOffGCD(Spell.BuffSelf("Berserking", ret => !PartyBuff.WeHaveBloodlust)) ); } if (SpellManager.HasSpell("Blood Fury")) { priCombat.AddChild( Spell.HandleOffGCD(Spell.BuffSelf("Blood Fury", ret => true)) ); } if (priCombat.Children.Any()) { return new Decorator( req => { if (!StyxWoW.Me.Combat || !StyxWoW.Me.GotTarget()) return false; if (StyxWoW.Me.IsMelee()) return StyxWoW.Me.CurrentTarget.IsWithinMeleeRange; return !StyxWoW.Me.IsMoving && StyxWoW.Me.CurrentTarget.SpellDistance() < 40; }, priCombat ); } return null; }
internal static void HookBehaviorTree() { HookHandler.StoreHook(HookHandler.HookType.VendorRun); Logger.DBLog.InfoFormat("[FunkyTownRun] Treehooks.."); #region VendorRun // Wipe the vendorrun and loot behavior trees, since we no longer want them Logger.DBLog.DebugFormat("[FunkyTownRun] VendorRun..."); Decorator GilesDecorator = HookHandler.ReturnHookValue(HookHandler.HookType.VendorRun)[0] as Decorator; //PrioritySelector GilesReplacement = GilesDecorator.Children[0] as PrioritySelector; PrioritySelector GilesReplacement = GilesDecorator.Children[0] as PrioritySelector; //HookHandler.PrintChildrenTypes(GilesReplacement.Children); CanRunDecoratorDelegate canRunDelegateReturnToTown = TownPortalBehavior.FunkyTPOverlord; ActionDelegate actionDelegateReturnTown = TownPortalBehavior.FunkyTPBehavior; ActionDelegate actionDelegateTownPortalFinish = TownPortalBehavior.FunkyTownPortalTownRun; Sequence sequenceReturnTown = new Sequence( new Zeta.TreeSharp.Action(actionDelegateReturnTown), new Zeta.TreeSharp.Action(actionDelegateTownPortalFinish) ); GilesReplacement.Children[1] = new Decorator(canRunDelegateReturnToTown, sequenceReturnTown); Logger.DBLog.DebugFormat("[FunkyTownRun] Town Portal - hooked..."); ActionDelegate actionDelegatePrePause = TownRunManager.GilesStashPrePause; ActionDelegate actionDelegatePause = TownRunManager.GilesStashPause; #region Idenify CanRunDecoratorDelegate canRunDelegateFunkyIDManual = TownRunManager.IdenifyItemManualOverlord; ActionDelegate actionDelegateIDManual = TownRunManager.IdenifyItemManualBehavior; ActionDelegate actionDelegateIDFinish = TownRunManager.IdenifyItemManualFinishBehavior; Sequence sequenceIDManual = new Sequence( new Zeta.TreeSharp.Action(actionDelegateIDManual), new Zeta.TreeSharp.Action(actionDelegateIDFinish) ); CanRunDecoratorDelegate canRunDelegateFunkyIDBookOfCain = TownRunManager.IdenifyItemBookOfCainOverlord; ActionDelegate actionDelegateIDBookOfCainMovement = TownRunManager.IdenifyItemBookOfCainMovementBehavior; ActionDelegate actionDelegateIDBookOfCainInteraction = TownRunManager.IdenifyItemBookOfCainInteractionBehavior; Sequence sequenceIDBookOfCain = new Sequence( new Zeta.TreeSharp.Action(actionDelegateIDBookOfCainMovement), new Zeta.TreeSharp.Action(actionDelegateIDBookOfCainInteraction), new Zeta.TreeSharp.Action(actionDelegateIDFinish) ); PrioritySelector priorityIDItems = new PrioritySelector( new Decorator(canRunDelegateFunkyIDManual, sequenceIDManual), new Decorator(canRunDelegateFunkyIDBookOfCain, sequenceIDBookOfCain) ); CanRunDecoratorDelegate canRunDelegateFunkyIDOverlord = TownRunManager.IdenifyItemOverlord; GilesReplacement.Children[2] = new Decorator(canRunDelegateFunkyIDOverlord, priorityIDItems); Logger.DBLog.DebugFormat("[FunkyTownRun] Idenify Items - hooked..."); #endregion // Replace the pause just after identify stuff to ensure we wait before trying to run to vendor etc. CanRunDecoratorDelegate canRunDelegateEvaluateAction = TownRunManager.ActionsEvaluatedOverlord; ActionDelegate actionDelegateEvaluateAction = TownRunManager.ActionsEvaluatedBehavior; Sequence sequenceEvaluate = new Sequence( new Zeta.TreeSharp.Action(actionDelegatePrePause), new Zeta.TreeSharp.Action(actionDelegatePause), new Zeta.TreeSharp.Action(actionDelegateEvaluateAction) ); GilesReplacement.Children[3] = new Decorator(canRunDelegateEvaluateAction, sequenceEvaluate); #region Salvage // Replace DB salvaging behavior tree with my optimized & "one-at-a-time" version CanRunDecoratorDelegate canRunDelegateSalvageGilesOverlord = TownRunManager.GilesSalvageOverlord; ActionDelegate actionDelegatePreSalvage = TownRunManager.GilesOptimisedPreSalvage; ActionDelegate actionDelegateSalvage = TownRunManager.GilesOptimisedSalvage; ActionDelegate actionDelegatePostSalvage = TownRunManager.GilesOptimisedPostSalvage; Sequence sequenceSalvage = new Sequence( new Zeta.TreeSharp.Action(actionDelegatePreSalvage), new Zeta.TreeSharp.Action(actionDelegateSalvage), new Zeta.TreeSharp.Action(actionDelegatePostSalvage), new Sequence( new Zeta.TreeSharp.Action(actionDelegatePrePause), new Zeta.TreeSharp.Action(actionDelegatePause) ) ); GilesReplacement.Children[4] = new Decorator(canRunDelegateSalvageGilesOverlord, sequenceSalvage); Logger.DBLog.DebugFormat("[FunkyTownRun] Salvage - hooked..."); #endregion #region Stash // Replace DB stashing behavior tree with my optimized version with loot rule replacement CanRunDecoratorDelegate canRunDelegateStashGilesOverlord = TownRunManager.StashOverlord; ActionDelegate actionDelegatePreStash = TownRunManager.PreStash; ActionDelegate actionDelegatePostStash = TownRunManager.PostStash; ActionDelegate actionDelegateStashMovement = TownRunManager.StashMovement; ActionDelegate actionDelegateStashUpdate = TownRunManager.StashUpdate; ActionDelegate actionDelegateStashItems = TownRunManager.StashItems; Sequence sequencestash = new Sequence( new Zeta.TreeSharp.Action(actionDelegatePreStash), new Zeta.TreeSharp.Action(actionDelegateStashMovement), new Zeta.TreeSharp.Action(actionDelegateStashUpdate), new Zeta.TreeSharp.Action(actionDelegateStashItems), new Zeta.TreeSharp.Action(actionDelegatePostStash), new Sequence( new Zeta.TreeSharp.Action(actionDelegatePrePause), new Zeta.TreeSharp.Action(actionDelegatePause) ) ); GilesReplacement.Children[5] = new Decorator(canRunDelegateStashGilesOverlord, sequencestash); Logger.DBLog.DebugFormat("[FunkyTownRun] Stash - hooked..."); #endregion #region Vendor // Replace DB vendoring behavior tree with my optimized & "one-at-a-time" version CanRunDecoratorDelegate canRunDelegateSellGilesOverlord = TownRunManager.GilesSellOverlord; ActionDelegate actionDelegatePreSell = TownRunManager.GilesOptimisedPreSell; ActionDelegate actionDelegateMovement = TownRunManager.VendorMovement; ActionDelegate actionDelegateSell = TownRunManager.GilesOptimisedSell; ActionDelegate actionDelegatePostSell = TownRunManager.GilesOptimisedPostSell; Sequence sequenceSell = new Sequence( new Zeta.TreeSharp.Action(actionDelegatePreSell), new Zeta.TreeSharp.Action(actionDelegateMovement), new Zeta.TreeSharp.Action(actionDelegateSell), new Zeta.TreeSharp.Action(actionDelegatePostSell), new Sequence( new Zeta.TreeSharp.Action(actionDelegatePrePause), new Zeta.TreeSharp.Action(actionDelegatePause) ) ); GilesReplacement.Children[6] = new Decorator(canRunDelegateSellGilesOverlord, sequenceSell); Logger.DBLog.DebugFormat("[FunkyTownRun] Vendor - hooked..."); #endregion #region Interaction Behavior CanRunDecoratorDelegate canRunDelegateInteraction = TownRunManager.InteractionOverlord; ActionDelegate actionDelegateInteractionMovementhBehavior = TownRunManager.InteractionMovement; ActionDelegate actionDelegateInteractionClickBehaviorBehavior = TownRunManager.InteractionClickBehavior; ActionDelegate actionDelegateInteractionLootingBehaviorBehavior = TownRunManager.InteractionLootingBehavior; ActionDelegate actionDelegateInteractionFinishBehaviorBehavior = TownRunManager.InteractionFinishBehavior; Sequence sequenceInteraction = new Sequence( new Zeta.TreeSharp.Action(actionDelegateInteractionFinishBehaviorBehavior), new Zeta.TreeSharp.Action(actionDelegateInteractionMovementhBehavior), new Zeta.TreeSharp.Action(actionDelegateInteractionClickBehaviorBehavior), new Zeta.TreeSharp.Action(actionDelegateInteractionLootingBehaviorBehavior), new Zeta.TreeSharp.Action(actionDelegateInteractionFinishBehaviorBehavior) ); GilesReplacement.InsertChild(7, new Decorator(canRunDelegateInteraction, sequenceInteraction)); Logger.DBLog.DebugFormat("[FunkyTownRun] Interaction Behavior - Inserted..."); #endregion #region Gambling Behavior CanRunDecoratorDelegate canRunDelegateGambling = TownRunManager.GamblingRunOverlord; ActionDelegate actionDelegateGamblingMovementBehavior = TownRunManager.GamblingMovement; ActionDelegate actionDelegateGamblingInteractionBehavior = TownRunManager.GamblingInteraction; ActionDelegate actionDelegateGamblingStartBehavior = TownRunManager.GamblingStart; ActionDelegate actionDelegateGamblingFinishBehavior = TownRunManager.GamblingFinish; Sequence sequenceGambling = new Sequence( new Zeta.TreeSharp.Action(actionDelegateGamblingStartBehavior), new Zeta.TreeSharp.Action(actionDelegateGamblingMovementBehavior), new Zeta.TreeSharp.Action(actionDelegateGamblingInteractionBehavior), new Zeta.TreeSharp.Action(actionDelegateGamblingFinishBehavior) ); GilesReplacement.InsertChild(8, new Decorator(canRunDelegateGambling, sequenceGambling)); Logger.DBLog.DebugFormat("[FunkyTownRun] Gambling Behavior - Inserted..."); #endregion #region Finish Behavior int childrenCount = GilesReplacement.Children.Count; ActionDelegate actionFinishReset = TownRunManager.ActionsEvaluatedEndingBehavior; ActionDelegate actionDelegateFinishBehavior = TownRunManager.FinishBehavior; var finish = GilesReplacement.Children[childrenCount - 1]; Sequence FinishSequence = new Sequence ( finish, new Zeta.TreeSharp.Action(actionFinishReset), new Zeta.TreeSharp.Action(actionDelegateFinishBehavior) ); GilesReplacement.Children[childrenCount - 1] = FinishSequence; Logger.DBLog.DebugFormat("[FunkyTownRun] Created Sequence Finish Behavior."); #endregion CanRunDecoratorDelegate canRunDelegateGilesTownRunCheck = TownRunManager.TownRunCheckOverlord; VendorCanRunDelegate = canRunDelegateGilesTownRunCheck; VendorPrioritySelector = GilesReplacement; var VendorComposite = new Decorator(canRunDelegateGilesTownRunCheck, GilesReplacement); VendorGUID = VendorComposite.Guid; HookHandler.SetHookValue(HookHandler.HookType.VendorRun, 0, VendorComposite); Logger.DBLog.DebugFormat("[FunkyTownRun] Vendor Run tree hooking finished."); #endregion initTreeHooks = true; }
public Composite GenerateMoveToTargetBehavior() { var Behavior = new PrioritySelector(); var MoveToMob = new Action(delegate(object context) { if (_context.CurrentTarget == null) return; if (_context.CurrentTarget.GetNativeObject() == null) return; Vector3 position = _context.CurrentTarget.GetPosition(); BotManager.Legs.Walk(position.X, position.Y); }); Behavior.AddChild(MoveToMob); Behavior.AddChild(new Sleep(200)); return Behavior; }
/// <summary> /// create standard Avoidance behavior (disengage and/or kiting) /// </summary> /// <param name="disengageSpell">spellName to use for disengage</param> /// <param name="disengageDist">distance spellName will jump</param> /// <param name="disengageDir">direction spellName jumps</param> /// <param name="crowdControl">behavior called to crowd control melee enemies or halt use of disengage</param> /// <param name="needDisengage">delegate to check for using disgenage. defaults checking settings for health and # attackers</param> /// <param name="needKiting">delegate to check for using kiting. defaults to checking settings for health and # attackers</param> /// <param name="cancelKiting">delegate to check if kiting should be cancelled. defaults to checking no attackers that aren't crowd controlled</param> /// <returns></returns> public static Composite CreateAvoidanceBehavior( string disengageSpell, int disengageDist, Disengage.Direction disengageDir, Composite crowdControl, SimpleBooleanDelegate needDisengage = null, SimpleBooleanDelegate needKiting = null, SimpleBooleanDelegate cancelKiting = null ) { PrioritySelector pri = new PrioritySelector(); // build default check for whether disengage is needed if (needDisengage == null) { needDisengage = req => { if (disengageSpell == null || disengageDir == Disengage.Direction.None) return false; if (!Kite.IsDisengageWantedByUserSettings() || !MovementManager.IsClassMovementAllowed || !SingularRoutine.IsAllowed(CapabilityFlags.Kiting)) return false; if (Spell.IsSpellOnCooldown(disengageSpell) && (!SingularSettings.Instance.UseRacials || Spell.IsSpellOnCooldown("Rocket Jump"))) return false; bool useDisengage = false; if (SingularRoutine.CurrentWoWContext == WoWContext.Normal) { int countMelee = Unit.UnitsInCombatWithUsOrOurStuff(7).Sum( u => !u.IsPlayer || !u.IsMelee() ? 1 : SingularSettings.Instance.DisengageMobCount); if (countMelee >= SingularSettings.Instance.DisengageMobCount) useDisengage = true; else if (Me.HealthPercent <= SingularSettings.Instance.DisengageHealth && countMelee > 0) useDisengage = true; } else if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) { useDisengage = Unit.UnfriendlyUnits(7).Any(u => u.IsMelee()); } return useDisengage; }; } // standard disengage behavior, with class specific check to make sure parties are crowd controlled if (SingularSettings.Instance.DisengageAllowed) { Composite behavRocketJump = new ActionAlwaysFail(); if (SingularSettings.Instance.UseRacials && SpellManager.HasSpell("Rocket Jump")) behavRocketJump = Disengage.CreateDisengageBehavior("Rocket Jump", 20, Disengage.Direction.Frontwards, null); pri.AddChild( new Decorator( ret => needDisengage(ret), new Sequence( crowdControl, // return Failure if shouldnt disengage, otherwise Success to allow new PrioritySelector( // new Action(r => { Logger.Write( LogColor.Hilite, "^Avoidance: disengaging away from mobs!"); return RunStatus.Failure; } ), Disengage.CreateDisengageBehavior(disengageSpell, disengageDist, disengageDir, null), behavRocketJump ) ) ) ); } // build default needKiting check if (needKiting == null) { needKiting = req => { if (!Kite.IsKitingWantedByUserSettings() || !MovementManager.IsClassMovementAllowed || !SingularRoutine.IsAllowed(CapabilityFlags.Kiting)) return false; bool useKiting = false; int countMelee = Unit.UnitsInCombatWithUsOrOurStuff(7).Count(); if (countMelee >= SingularSettings.Instance.KiteMobCount) useKiting = true; else if (Me.HealthPercent <= SingularSettings.Instance.KiteHealth && countMelee > 0) useKiting = true; return useKiting; }; } if (cancelKiting == null) { cancelKiting = req => { int countAttackers = Unit.UnitsInCombatWithUsOrOurStuff(7).Count(); return (countAttackers == 0); }; } // add check to initiate kiting behavior if (SingularSettings.Instance.KiteAllow) { pri.AddChild( new Decorator( ret => needKiting(ret), new Sequence( crowdControl, Kite.BeginKitingBehavior() ) ) ); } if (!pri.Children.Any()) { return new ActionAlwaysFail(); } return new Decorator(req => MovementManager.IsClassMovementAllowed, pri); }