From 3f309edffbe28b9b8abe3717675ec1d8697fce64 Mon Sep 17 00:00:00 2001 From: Leath Cooper Date: Sat, 11 Sep 2021 20:12:32 -0400 Subject: [PATCH] asdfa --- .../UOContent/Mobiles/AI/BehaviorTreeAI.cs | 24 +++- .../Mobiles/BT/BehaviorMageCombatNodeSet.cs | 4 +- .../Mobiles/BehaviorAI/Composite/Selector.cs | 50 ++++++++ .../Mobiles/BehaviorAI/Composite/Sequence.cs | 50 ++++++++ .../Mobiles/BehaviorAI/Core/Behavior.cs | 52 +++++++++ .../BehaviorAI/Core/BehaviorQueueEntry.cs | 21 ++++ .../Mobiles/BehaviorAI/Core/BehaviorTree.cs | 108 ++++++++++++++++++ .../BehaviorAI/Core/BehaviorTreeContext.cs | 19 +++ .../Mobiles/BehaviorAI/Core/Blackboard.cs | 12 ++ .../Mobiles/BehaviorAI/Core/Composite.cs | 51 +++++++++ .../Mobiles/BehaviorAI/Core/Decorator.cs | 42 +++++++ .../BehaviorAI/Decorator/ConditionalLoop.cs | 41 +++++++ .../Mobiles/BehaviorAI/Decorator/Cooldown.cs | 57 +++++++++ .../BehaviorAI/Decorator/ForceSuccess.cs | 23 ++++ .../Mobiles/BehaviorAI/Decorator/Inverter.cs | 29 +++++ .../Mobiles/BehaviorAI/Decorator/Loop.cs | 57 +++++++++ .../BehaviorAI/Decorator/OwnerCondition.cs | 29 +++++ .../Mobiles/BehaviorAI/Decorator/UntilFail.cs | 30 +++++ .../Mobiles/BehaviorAI/Leaf/AutoTarget.cs | 56 +++++++++ .../Mobiles/BehaviorAI/Leaf/CastSpell.cs | 70 ++++++++++++ .../Mobiles/BehaviorAI/Leaf/Condition.cs | 29 +++++ .../Mobiles/BehaviorAI/Leaf/DynamicTarget.cs | 46 ++++++++ .../UOContent/Mobiles/BehaviorAI/Leaf/Tap.cs | 23 ++++ .../Mobiles/BehaviorAI/Leaf/WaitForTarget.cs | 24 ++++ Projects/UOContent/Mobiles/BehaviorMage.cs | 3 + 25 files changed, 947 insertions(+), 3 deletions(-) create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Composite/Selector.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Composite/Sequence.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Core/Behavior.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Core/BehaviorQueueEntry.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Core/BehaviorTree.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Core/BehaviorTreeContext.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Core/Blackboard.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Core/Composite.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Core/Decorator.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Decorator/ConditionalLoop.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Decorator/Cooldown.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Decorator/ForceSuccess.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Decorator/Inverter.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Decorator/Loop.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Decorator/OwnerCondition.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Decorator/UntilFail.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Leaf/AutoTarget.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Leaf/CastSpell.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Leaf/Condition.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Leaf/DynamicTarget.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Leaf/Tap.cs create mode 100644 Projects/UOContent/Mobiles/BehaviorAI/Leaf/WaitForTarget.cs diff --git a/Projects/UOContent/Mobiles/AI/BehaviorTreeAI.cs b/Projects/UOContent/Mobiles/AI/BehaviorTreeAI.cs index 0606b97bb..945604ce4 100644 --- a/Projects/UOContent/Mobiles/AI/BehaviorTreeAI.cs +++ b/Projects/UOContent/Mobiles/AI/BehaviorTreeAI.cs @@ -3,17 +3,34 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using Server.Mobiles.BT; +// using Server.Mobiles.BT; using Server.Targeting; +using Server.Mobiles.BehaviorAI; +using Server.Spells.Third; namespace Server.Mobiles.AI { public class BehaviorTreeAI : BaseAI { + // private Blackboard blackboard; private Blackboard blackboard; + private BehaviorTree behaviorTree; + private BehaviorTreeContext behaviorTreeContext; public BehaviorTreeAI(BaseCreature m) : base(m) { + // blackboard = new Blackboard(); blackboard = new Blackboard(); + behaviorTreeContext = new BehaviorTreeContext(m, blackboard); + behaviorTree = new BehaviorTree(); + behaviorTree.TryAddRoot( + new Sequence(behaviorTree) + .AddChild(new Condition(behaviorTree, (context) => context.Mobile.RawStr - context.Mobile.Str == 0)) + .AddChild(new CastSpell(behaviorTree, (context) => new BlessSpell(context.Mobile))) + .AddChild(new WaitForTarget(behaviorTree)) + .AddChild(new DynamicTarget(behaviorTree, (context) => context.Mobile)) + ); + + behaviorTree.Start(behaviorTreeContext); } public override bool Think() @@ -23,7 +40,10 @@ namespace Server.Mobiles.AI return false; } - MageBehaviorTree.Instance.RunBehavior(m_Mobile, blackboard); + // MageBehaviorTree.Instance.RunBehavior(m_Mobile, blackboard); + + m_Mobile.DebugSay("Thinking..."); + behaviorTree.Tick(behaviorTreeContext); return true; } diff --git a/Projects/UOContent/Mobiles/BT/BehaviorMageCombatNodeSet.cs b/Projects/UOContent/Mobiles/BT/BehaviorMageCombatNodeSet.cs index 1f9433943..f655226eb 100644 --- a/Projects/UOContent/Mobiles/BT/BehaviorMageCombatNodeSet.cs +++ b/Projects/UOContent/Mobiles/BT/BehaviorMageCombatNodeSet.cs @@ -11,12 +11,14 @@ namespace Server.Mobiles.BT AddChild( new SelectorNode(tree) .AddChild(new TapNode(tree, (mob, board) => { mob.CurrentSpeed = mob.ActiveSpeed; })) - .AddChild(new CooldownNode(tree, TimeSpan.FromSeconds(0.5), new InverterNode(tree, new KeepRangeNode(tree, 1, 2)))) + .AddChild(new CooldownNode(tree, TimeSpan.FromSeconds(0.5), new InverterNode(tree, new KeepRangeNode(tree, 1, 3)))) + /* .AddChild(new MageDebuffNode(tree)) .AddChild(new MageComboNode(tree)) .AddChild(new OwnerConditionNode(tree, (mob, board) => mob.Poison != null, new CureSelfNode(tree, TimeSpan.FromSeconds(6.0)))) .AddChild(new OwnerConditionNode(tree, (mob, board) => mob.HitsMax - mob.Hits >= 40, new HealSelfNode(tree, TimeSpan.FromSeconds(6.0)))) .AddChild(new OwnerConditionNode(tree, shouldMeditate, new MeditateNode(tree, 75))) + */ ); } diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Composite/Selector.cs b/Projects/UOContent/Mobiles/BehaviorAI/Composite/Selector.cs new file mode 100644 index 000000000..b57bfacdd --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Composite/Selector.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class Selector : Composite + { + public Selector(BehaviorTree tree) : base(tree) + { + } + public override void OnChildComplete(BehaviorTreeContext context, Result lastResult) + { + if (!currentChildCache.TryGetValue(context.Mobile, out int currentChild)) + { + currentChild = 0; + currentChildCache.Add(context.Mobile, currentChild); + } + + Behavior child = Children[currentChild]; + + if (lastResult == Result.Success) + { + currentChildCache[context.Mobile] = 0; + SetResult(context, Result.Success); + return; + } + + currentChild++; + if (currentChild >= Children.Count) + { + currentChildCache[context.Mobile] = 0; + SetResult(context, Result.Failure); + return; + } + else + { + currentChildCache[context.Mobile] = currentChild; + SetResult(context, Result.Running); + if (Children[currentChild] == null) + { + throw new NullReferenceException(); + } + Tree.Enqueue(context, Children[currentChild], OnChildComplete); + } + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Composite/Sequence.cs b/Projects/UOContent/Mobiles/BehaviorAI/Composite/Sequence.cs new file mode 100644 index 000000000..aa38262cf --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Composite/Sequence.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class Sequence : Composite + { + public Sequence(BehaviorTree tree) : base(tree) + { + } + public override void OnChildComplete(BehaviorTreeContext context, Result lastResult) + { + if (!currentChildCache.TryGetValue(context.Mobile, out int currentChild)) + { + currentChild = 0; + currentChildCache.Add(context.Mobile, currentChild); + } + + Behavior child = Children[currentChild]; + + if (lastResult == Result.Failure) + { + currentChildCache[context.Mobile] = 0; + SetResult(context, Result.Failure); + return; + } + + currentChild++; + if (currentChild >= Children.Count) + { + currentChildCache[context.Mobile] = 0; + SetResult(context, Result.Success); + return; + } + else + { + SetResult(context, Result.Running); + currentChildCache[context.Mobile] = currentChild; + if (Children[currentChild] == null) + { + throw new NullReferenceException(); + } + Tree.Enqueue(context, Children[currentChild], OnChildComplete); + } + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Core/Behavior.cs b/Projects/UOContent/Mobiles/BehaviorAI/Core/Behavior.cs new file mode 100644 index 000000000..bc6683c3e --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Core/Behavior.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public delegate void BehaviorObserver(BehaviorTreeContext context, Result lastResult); + public enum Result + { + Success, + Failure, + Running + } + public abstract class Behavior + { + public BehaviorTree Tree { get; protected set; } + public BehaviorObserver Observer { get; protected set; } + private Dictionary lastResultCache; + public virtual bool IsRunning(BehaviorTreeContext context) => GetResult(context) == Result.Running; + public Behavior(BehaviorTree tree) + { + Tree = tree; + lastResultCache = new Dictionary(); + } + public virtual void Tick(BehaviorTreeContext context) + { + } + public virtual void Execute(BehaviorTreeContext context) + { + } + public Result GetResult(BehaviorTreeContext context) + { + if (!lastResultCache.TryGetValue(context.Mobile, out Result result)) + { + result = Result.Failure; + lastResultCache.Add(context.Mobile, result); + } + return result; + } + public void SetResult(BehaviorTreeContext context, Result result) + { + if (!lastResultCache.TryGetValue(context.Mobile, out Result old)) + { + lastResultCache.Add(context.Mobile, Result.Failure); + } + + lastResultCache[context.Mobile] = result; + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Core/BehaviorQueueEntry.cs b/Projects/UOContent/Mobiles/BehaviorAI/Core/BehaviorQueueEntry.cs new file mode 100644 index 000000000..9b82b562d --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Core/BehaviorQueueEntry.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class BehaviorQueueEntry + { + public BehaviorTreeContext Context { get; } + public BehaviorObserver Observer { get; } + public Behavior Behavior { get; } + public BehaviorQueueEntry(BehaviorTreeContext context, Behavior behavior, BehaviorObserver observer) + { + Context = context; + Behavior = behavior; + Observer = observer; + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Core/BehaviorTree.cs b/Projects/UOContent/Mobiles/BehaviorAI/Core/BehaviorTree.cs new file mode 100644 index 000000000..b9136fa7e --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Core/BehaviorTree.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class BehaviorTree + { + public Behavior Root { get; private set; } + private Dictionary> behaviorQueueCache; + private Dictionary executingCache; + public BehaviorTree() + { + behaviorQueueCache = new Dictionary>(); + executingCache = new Dictionary(); + } + public bool TryAddRoot(Composite behavior) + { + if (Root == null) + { + Root = behavior; + return true; + } + return false; + } + public void Start(BehaviorTreeContext context) + { + if (Root != null) + { + Enqueue(context, Root, ExecutionFinished); + } + } + public void Stop(BehaviorTreeContext context) + { + getQueue(context).Clear(); + } + public virtual void Tick(BehaviorTreeContext context) + { + if (!executingCache.TryGetValue(context.Mobile, out bool executing)) + { + executing = false; + executingCache[context.Mobile] = executing; + } + + if (!executing) + { + executingCache[context.Mobile] = true; + Queue queue = getQueue(context); + queue.Enqueue(null); + while (Step(context)) + { + } + executingCache[context.Mobile] = false; + } + } + public virtual bool Step(BehaviorTreeContext context) + { + Queue queue = getQueue(context); + + BehaviorQueueEntry current = queue.Dequeue(); + + if (current == null || current.Behavior == null || current.Context == null) + { + return false; + } + + current.Behavior.Tick(current.Context); + + if (!current.Behavior.IsRunning(current.Context)) + { + if (current.Observer != null) + { + current.Observer(current.Context, current.Behavior.GetResult(current.Context)); + } + return true; + } + + queue.Enqueue(current); + + return false; + } + public virtual void ExecutionFinished(BehaviorTreeContext context, Result result) + { + } + public void Enqueue(BehaviorTreeContext context, Behavior behavior, BehaviorObserver observer) + { + if (!behaviorQueueCache.TryGetValue(context.Mobile, out Queue queue)) + { + queue = new Queue(); + behaviorQueueCache.Add(context.Mobile, queue); + } + + queue.Enqueue(new BehaviorQueueEntry(context, behavior, observer)); + } + private Queue getQueue(BehaviorTreeContext context) + { + if (!behaviorQueueCache.TryGetValue(context.Mobile, out Queue queue)) + { + queue = new Queue(); + behaviorQueueCache.Add(context.Mobile, queue); + } + + return queue; + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Core/BehaviorTreeContext.cs b/Projects/UOContent/Mobiles/BehaviorAI/Core/BehaviorTreeContext.cs new file mode 100644 index 000000000..440613499 --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Core/BehaviorTreeContext.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class BehaviorTreeContext + { + public BaseCreature Mobile { get; } + public Blackboard Blackboard { get; } + public BehaviorTreeContext(BaseCreature mob, Blackboard blackboard) + { + Mobile = mob; + Blackboard = blackboard; + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Core/Blackboard.cs b/Projects/UOContent/Mobiles/BehaviorAI/Core/Blackboard.cs new file mode 100644 index 000000000..c850af8c4 --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Core/Blackboard.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class Blackboard : Dictionary + { + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Core/Composite.cs b/Projects/UOContent/Mobiles/BehaviorAI/Core/Composite.cs new file mode 100644 index 000000000..f75dcc14f --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Core/Composite.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class Composite : Behavior + { + public List Children { get; private set; } + protected Dictionary currentChildCache; + public Composite(BehaviorTree tree) : base(tree) + { + Children = new List(); + currentChildCache = new Dictionary(); + } + public Composite AddChild(Behavior child) + { + if (Children != null && child != null) + { + Children.Add(child); + } + return this; + } + public override void Tick(BehaviorTreeContext context) + { + if (!currentChildCache.TryGetValue(context.Mobile, out int currentChild)) + { + currentChild = 0; + currentChildCache.Add(context.Mobile, currentChild); + } + + if (GetResult(context) != Result.Running) + { + currentChild = 0; + currentChildCache[context.Mobile] = currentChild; + SetResult(context, Result.Running); + if (Children[currentChild] == null) + { + throw new NullReferenceException(); + } + Tree.Enqueue(context, Children[currentChild], OnChildComplete); + } + } + public virtual void OnChildComplete(BehaviorTreeContext context, Result result) + { + SetResult(context, Result.Failure); + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Core/Decorator.cs b/Projects/UOContent/Mobiles/BehaviorAI/Core/Decorator.cs new file mode 100644 index 000000000..bedd27e61 --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Core/Decorator.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class Decorator : Behavior + { + public Behavior Child { get; private set; } + public Decorator(BehaviorTree tree) : base(tree) + { + } + public Decorator(BehaviorTree tree, Behavior child) : base(tree) + { + Child = child; + } + public virtual bool AddChild(Behavior child) + { + if (Child == null) + { + Child = child; + return true; + } + return false; + } + public override void Tick(BehaviorTreeContext context) + { + if (Child != null && GetResult(context) != Result.Running) + { + Tree.Enqueue(context, Child, OnChildComplete); + SetResult(context, Result.Running); + return; + } + } + public virtual void OnChildComplete(BehaviorTreeContext context, Result result) + { + SetResult(context, Child.GetResult(context)); + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Decorator/ConditionalLoop.cs b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/ConditionalLoop.cs new file mode 100644 index 000000000..0f92b43ee --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/ConditionalLoop.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public delegate bool ConditionalLoopPredicate(BehaviorTreeContext context); + public class ConditionalLoop : Decorator + { + public ConditionalLoopPredicate Predicate { get; private set; } + public ConditionalLoop(BehaviorTree tree, ConditionalLoopPredicate predicate) : base(tree) + { + Predicate = predicate; + } + public ConditionalLoop(BehaviorTree tree, ConditionalLoopPredicate predicate, Behavior child) : base(tree, child) + { + Predicate = predicate; + } + public override void Tick(BehaviorTreeContext context) + { + if (Predicate(context)) + { + base.Tick(context); + return; + } + SetResult(context, Result.Failure); + return; + } + public override void OnChildComplete(BehaviorTreeContext context, Result result) + { + if (Predicate(context)) + { + Tree.Enqueue(context, Child, OnChildComplete); + return; + } + SetResult(context, Result.Success); + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Decorator/Cooldown.cs b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/Cooldown.cs new file mode 100644 index 000000000..13edfda87 --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/Cooldown.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class Cooldown : Decorator + { + public TimeSpan Duration { get; } + private Dictionary nextCooldownCache; + public Cooldown(BehaviorTree tree, TimeSpan duration) : base(tree) + { + nextCooldownCache = new Dictionary(); + Duration = duration; + } + public Cooldown(BehaviorTree tree, TimeSpan duration, Behavior child) : base(tree, child) + { + nextCooldownCache = new Dictionary(); + Duration = duration; + } + public override void Tick(BehaviorTreeContext context) + { + if(!nextCooldownCache.TryGetValue(context.Mobile, out long nextCooldown)) + { + nextCooldown = Core.TickCount; + nextCooldownCache.Add(context.Mobile, nextCooldown); + } + + if (Core.TickCount > nextCooldown) + { + base.Tick(context); + } + } + public override void OnChildComplete(BehaviorTreeContext context, Result result) + { + if (!nextCooldownCache.TryGetValue(context.Mobile, out long nextCooldown)) + { + nextCooldown = Core.TickCount; + nextCooldownCache.Add(context.Mobile, nextCooldown); + } + + if (Child != null) + { + if (Child.GetResult(context) == Result.Success) + { + nextCooldownCache[context.Mobile] = Core.TickCount + Duration.Ticks; + } + SetResult(context, Child.GetResult(context)); + return; + } + + SetResult(context, Result.Failure); + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Decorator/ForceSuccess.cs b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/ForceSuccess.cs new file mode 100644 index 000000000..e6a8c9184 --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/ForceSuccess.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class ForceSuccess : Decorator + { + public ForceSuccess(BehaviorTree tree) : base(tree) + { + } + public ForceSuccess(BehaviorTree tree, Behavior child) : base(tree, child) + { + } + public override void OnChildComplete(BehaviorTreeContext context, Result result) + { + SetResult(context, Result.Success); + } + } +} + diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Decorator/Inverter.cs b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/Inverter.cs new file mode 100644 index 000000000..a3d622e0e --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/Inverter.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class Inverter : Decorator + { + public Inverter(BehaviorTree tree) : base(tree) + { + } + public Inverter(BehaviorTree tree, Behavior child) : base(tree, child) + { + } + public override void OnChildComplete(BehaviorTreeContext context, Result result) + { + if (result == Result.Success) + { + SetResult(context, Result.Failure); + } + else + { + SetResult(context, Result.Success); + } + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Decorator/Loop.cs b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/Loop.cs new file mode 100644 index 000000000..e93d623e5 --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/Loop.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class Loop : Decorator + { + private Dictionary currentIterationCache; + private int totalIterations; + public Loop(BehaviorTree tree, int n) : base(tree) + { + currentIterationCache = new Dictionary(); + totalIterations = n; + } + public override void Tick(BehaviorTreeContext context) + { + if (!currentIterationCache.TryGetValue(context.Mobile, out int iteration)) + { + iteration = 0; + currentIterationCache.Add(context.Mobile, iteration); + } + + base.Tick(context); + } + public override void OnChildComplete(BehaviorTreeContext context, Result result) + { + if (!currentIterationCache.TryGetValue(context.Mobile, out int iteration)) + { + iteration = 0; + currentIterationCache.Add(context.Mobile, iteration); + } + + iteration++; + + if (iteration >= totalIterations) + { + currentIterationCache[context.Mobile] = 0; + SetResult(context, Result.Success); + return; + } + + currentIterationCache[context.Mobile] = iteration; + + if (Child != null) + { + Tree.Enqueue(context, Child, OnChildComplete); + return; + } + + currentIterationCache[context.Mobile] = 0; + SetResult(context, Result.Failure); + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Decorator/OwnerCondition.cs b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/OwnerCondition.cs new file mode 100644 index 000000000..2ee6948d2 --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/OwnerCondition.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public delegate bool OwnerConditionPredicate(BehaviorTreeContext context); + public class OwnerCondition : Decorator + { + public OwnerConditionPredicate Predicate { get; } + public OwnerCondition(BehaviorTree tree, OwnerConditionPredicate predicate) : base(tree) + { + Predicate = predicate; + } + public OwnerCondition(BehaviorTree tree, OwnerConditionPredicate predicate, Behavior child) : base(tree, child) + { + Predicate = predicate; + } + public override void Tick(BehaviorTreeContext context) + { + if (Predicate(context)) + { + base.Tick(context); + } + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Decorator/UntilFail.cs b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/UntilFail.cs new file mode 100644 index 000000000..44397f72e --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Decorator/UntilFail.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class UntilFail : Decorator + { + public UntilFail(BehaviorTree tree): base(tree) + { + } + public UntilFail(BehaviorTree tree, Behavior child) : base(tree, child) + { + } + public override void OnChildComplete(BehaviorTreeContext context, Result result) + { + if (Child != null) + { + if (Child.GetResult(context) == Result.Failure) + { + SetResult(context, Result.Success); + return; + } + Tree.Enqueue(context, Child, OnChildComplete); + } + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Leaf/AutoTarget.cs b/Projects/UOContent/Mobiles/BehaviorAI/Leaf/AutoTarget.cs new file mode 100644 index 000000000..d00c84d83 --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Leaf/AutoTarget.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Server.Targeting; + +namespace Server.Mobiles.BehaviorAI +{ + public class AutoTarget : Behavior + { + public AutoTarget(BehaviorTree tree) : base(tree) + { + } + public override void Tick(BehaviorTreeContext context) + { + Target target = context.Mobile.Target; + + if (target == null) + { + SetResult(context, Result.Failure); + return; + } + + Mobile combatant = context.Mobile.Combatant; + + if ((target.Flags & TargetFlags.Harmful) != 0 && combatant != null) + { + if (combatant.Deleted) + { + target.Cancel(context.Mobile, TargetCancelType.Canceled); + SetResult(context, Result.Failure); + return; + } + + if ((target.Range == -1 || context.Mobile.InRange(combatant, target.Range)) && + context.Mobile.CanSee(combatant) && + context.Mobile.InLOS(combatant) + ) + { + target.Invoke(context.Mobile, combatant); + SetResult(context, Result.Success); + return; + } + } + else if ((target.Flags & TargetFlags.Beneficial) != 0) + { + target.Invoke(context.Mobile, context.Mobile); + SetResult(context, Result.Success); + return; + } + + SetResult(context, Result.Failure); + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Leaf/CastSpell.cs b/Projects/UOContent/Mobiles/BehaviorAI/Leaf/CastSpell.cs new file mode 100644 index 000000000..f0a9aba49 --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Leaf/CastSpell.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Server.Spells; + +namespace Server.Mobiles.BehaviorAI +{ + public delegate Spell CastSpellCallback(BehaviorTreeContext context); + public class CastSpell : Behavior + { + public CastSpellCallback Callback { get; } + public CastSpell(BehaviorTree tree, CastSpellCallback callback) : base(tree) + { + Callback = callback; + } + public override void Tick(BehaviorTreeContext context) + { + BaseCreature owner = context.Mobile; + + if (owner.Spell == null) + { + Spell spell = Callback(context); + + if (owner.Mana < spell.GetMana()) + { + SetResult(context, Result.Failure); + owner.DebugSay("Not enough mana..."); + return; + } + + if (Core.TickCount < owner.NextSpellTime) + { + SetResult(context, Result.Running); + owner.DebugSay("On cooldown..."); + return; + } + + if (!spell.Cast()) + { + SetResult(context, Result.Running); + owner.DebugSay("Failed to cast..."); + return; + } + + owner.DebugSay("Casting {0}...", spell.Name); + if (!string.IsNullOrEmpty(spell.Mantra)) + owner.PublicOverheadMessage(Network.MessageType.Spell, owner.SpeechHue, false, spell.Mantra, false); + + SetResult(context, Result.Running); + return; + } + else if (owner.Spell.IsCasting) + { + SetResult(context, Result.Running); + owner.DebugSay("Already casting {0}...", ((Spell)owner.Spell).Name); + return; + } + else if (!owner.Spell.IsCasting) + { + SetResult(context, Result.Success); + owner.DebugSay("Finished casting {0}...", ((Spell)owner.Spell).Name); + return; + } + + SetResult(context, Result.Failure); + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Leaf/Condition.cs b/Projects/UOContent/Mobiles/BehaviorAI/Leaf/Condition.cs new file mode 100644 index 000000000..bc0fea30c --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Leaf/Condition.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public delegate bool ConditionPredicate(BehaviorTreeContext context); + public class Condition : Behavior + { + public ConditionPredicate Predicate { get; } + public Condition(BehaviorTree tree, ConditionPredicate predicate) : base(tree) + { + Predicate = predicate; + } + public override void Tick(BehaviorTreeContext context) + { + if(Predicate(context)) + { + SetResult(context, Result.Success); + } + else + { + SetResult(context, Result.Failure); + } + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Leaf/DynamicTarget.cs b/Projects/UOContent/Mobiles/BehaviorAI/Leaf/DynamicTarget.cs new file mode 100644 index 000000000..28560db26 --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Leaf/DynamicTarget.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Server.Targeting; + +namespace Server.Mobiles.BehaviorAI +{ + public delegate object DynamicTargetCallback(BehaviorTreeContext context); + public class DynamicTarget : Behavior + { + public DynamicTargetCallback Transformation { get; private set; } + public DynamicTarget(BehaviorTree tree, DynamicTargetCallback transformation) : base(tree) + { + Transformation = transformation; + } + public override void Tick(BehaviorTreeContext context) + { + if (Transformation != null) + { + SetResult(context, Result.Failure); + return; + } + + Target target = context.Mobile.Target; + + if (target == null) + { + SetResult(context, Result.Failure); + return; + } + + object targeted = Transformation(context); + + if (targeted == null) + { + SetResult(context, Result.Failure); + return; + } + + target.Invoke(context.Mobile, targeted); + SetResult(context, Result.Success); + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Leaf/Tap.cs b/Projects/UOContent/Mobiles/BehaviorAI/Leaf/Tap.cs new file mode 100644 index 000000000..338d4bc28 --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Leaf/Tap.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public delegate void TapAction(BehaviorTreeContext context); + public class Tap : Behavior + { + public TapAction Action { get; private set; } + public Tap(BehaviorTree tree, TapAction action) : base(tree) + { + Action = action; + } + public override void Tick(BehaviorTreeContext context) + { + Action(context); + SetResult(context, Result.Failure); + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorAI/Leaf/WaitForTarget.cs b/Projects/UOContent/Mobiles/BehaviorAI/Leaf/WaitForTarget.cs new file mode 100644 index 000000000..e07d3a64e --- /dev/null +++ b/Projects/UOContent/Mobiles/BehaviorAI/Leaf/WaitForTarget.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Mobiles.BehaviorAI +{ + public class WaitForTarget : Behavior + { + public WaitForTarget(BehaviorTree tree) : base(tree) + { + } + public override void Tick(BehaviorTreeContext context) + { + if (context.Mobile.Target != null) + { + SetResult(context, Result.Success); + return; + } + SetResult(context, Result.Running); + } + } +} diff --git a/Projects/UOContent/Mobiles/BehaviorMage.cs b/Projects/UOContent/Mobiles/BehaviorMage.cs index 370b079b6..f89566483 100644 --- a/Projects/UOContent/Mobiles/BehaviorMage.cs +++ b/Projects/UOContent/Mobiles/BehaviorMage.cs @@ -13,6 +13,8 @@ namespace Server.Mobiles [Constructible] public BehaviorMage() : base(AIType.AI_BehaviorTree, FightMode.Evil, 2, 0, 0.01, 3) { + Debug = true; + InitBody(); SetStr(100, 100); @@ -32,6 +34,7 @@ namespace Server.Mobiles SetSkill(SkillName.MagicResist, 100.0); SetSkill(SkillName.Tactics, 100.0); SetSkill(SkillName.Meditation, 100.0); + SetSkill(SkillName.Wrestling, 100.0); AddItem(new Backpack() { Movable = false });