WIP-broken
This commit is contained in:
parent
380f40890c
commit
bb2999d3ab
8 changed files with 63 additions and 73 deletions
|
|
@ -13,8 +13,8 @@ namespace Server.Mobiles.AI
|
|||
public class BehaviorTreeAI : BaseAI
|
||||
{
|
||||
private Blackboard blackboard;
|
||||
private BehaviorTree behaviorTree;
|
||||
private BehaviorTreeContext behaviorTreeContext;
|
||||
private readonly BehaviorTree behaviorTree;
|
||||
private readonly BehaviorTreeContext behaviorTreeContext;
|
||||
public BehaviorTreeAI(BaseCreature m) : base(m)
|
||||
{
|
||||
// blackboard = new Blackboard();
|
||||
|
|
@ -25,13 +25,13 @@ namespace Server.Mobiles.AI
|
|||
new Selector(behaviorTree)
|
||||
.AddChild(new MageCombat(behaviorTree))
|
||||
.AddChild(new MagePassive(behaviorTree))
|
||||
/*
|
||||
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))
|
||||
*/
|
||||
/*
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -40,16 +40,20 @@ namespace Server.Mobiles.BT
|
|||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(spell.Mantra))
|
||||
{
|
||||
mob.PublicOverheadMessage(Network.MessageType.Spell, 0, false, spell.Mantra, false);
|
||||
}
|
||||
|
||||
return Result.Running;
|
||||
}
|
||||
else if (mob.Spell.IsCasting)
|
||||
|
||||
if (mob.Spell.IsCasting)
|
||||
{
|
||||
mob.DebugSay("Still casting...");
|
||||
return Result.Running;
|
||||
}
|
||||
else if (!mob.Spell.IsCasting)
|
||||
|
||||
if (!mob.Spell.IsCasting)
|
||||
{
|
||||
mob.DebugSay("Finished casting...");
|
||||
return Result.Success;
|
||||
|
|
|
|||
|
|
@ -73,14 +73,18 @@ namespace Server.Mobiles.BT
|
|||
}
|
||||
|
||||
if (mob.Combatant.Meditating)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
private static bool shouldContinueCombo(BaseCreature mob, Blackboard board)
|
||||
{
|
||||
if (mob.Combatant == null || mob.Combatant.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mob.Combatant.HitsMax - mob.Combatant.Hits >= 70)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,13 +7,15 @@ namespace Server.Mobiles.BehaviorAI
|
|||
{
|
||||
Success,
|
||||
Failure,
|
||||
Running
|
||||
Running,
|
||||
Terminated
|
||||
}
|
||||
public abstract class Behavior
|
||||
{
|
||||
public BehaviorTree Tree { get; protected set; }
|
||||
public BehaviorObserver Observer { get; protected set; }
|
||||
private Dictionary<BaseCreature, Result> lastResultCache;
|
||||
private readonly Dictionary<BaseCreature, Result> lastResultCache;
|
||||
|
||||
public virtual bool IsRunning(BehaviorTreeContext context) => GetResult(context) == Result.Running;
|
||||
public Behavior(BehaviorTree tree)
|
||||
{
|
||||
|
|
@ -30,14 +32,15 @@ namespace Server.Mobiles.BehaviorAI
|
|||
{
|
||||
if (!lastResultCache.TryGetValue(context.Mobile, out Result result))
|
||||
{
|
||||
result = Result.Failure;
|
||||
result = Result.Terminated;
|
||||
lastResultCache.Add(context.Mobile, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
public void SetResult(BehaviorTreeContext context, Result result)
|
||||
{
|
||||
if (!lastResultCache.TryGetValue(context.Mobile, out Result old))
|
||||
if (!lastResultCache.TryGetValue(context.Mobile, out _))
|
||||
{
|
||||
lastResultCache.Add(context.Mobile, Result.Failure);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ namespace Server.Mobiles.BehaviorAI
|
|||
public class BehaviorTree
|
||||
{
|
||||
public Behavior Root { get; private set; }
|
||||
private Dictionary<BaseCreature, Queue<BehaviorQueueEntry>> behaviorQueueCache;
|
||||
private Dictionary<BaseCreature, bool> executingCache;
|
||||
private readonly Dictionary<BaseCreature, Queue<BehaviorQueueEntry>> _behaviorQueueCache;
|
||||
private readonly Dictionary<BaseCreature, bool> _executingCache;
|
||||
public BehaviorTree()
|
||||
{
|
||||
behaviorQueueCache = new Dictionary<BaseCreature, Queue<BehaviorQueueEntry>>();
|
||||
executingCache = new Dictionary<BaseCreature, bool>();
|
||||
_behaviorQueueCache = new Dictionary<BaseCreature, Queue<BehaviorQueueEntry>>();
|
||||
_executingCache = new Dictionary<BaseCreature, bool>();
|
||||
}
|
||||
public bool TryAddRoot(Composite behavior)
|
||||
{
|
||||
|
|
@ -38,30 +38,29 @@ namespace Server.Mobiles.BehaviorAI
|
|||
}
|
||||
public virtual void Tick(BehaviorTreeContext context)
|
||||
{
|
||||
if (!executingCache.TryGetValue(context.Mobile, out bool executing))
|
||||
if (!_executingCache.TryGetValue(context.Mobile, out bool executing))
|
||||
{
|
||||
executing = false;
|
||||
executingCache[context.Mobile] = executing;
|
||||
_executingCache[context.Mobile] = executing;
|
||||
}
|
||||
|
||||
if (!executing)
|
||||
{
|
||||
executingCache[context.Mobile] = true;
|
||||
_executingCache[context.Mobile] = true;
|
||||
Queue<BehaviorQueueEntry> queue = getQueue(context);
|
||||
queue.Enqueue(null);
|
||||
while (Step(context))
|
||||
{
|
||||
}
|
||||
executingCache[context.Mobile] = false;
|
||||
_executingCache[context.Mobile] = false;
|
||||
}
|
||||
}
|
||||
public virtual bool Step(BehaviorTreeContext context)
|
||||
{
|
||||
Queue<BehaviorQueueEntry> queue = getQueue(context);
|
||||
|
||||
BehaviorQueueEntry current = queue.Dequeue();
|
||||
|
||||
if (current == null || current.Behavior == null || current.Context == null)
|
||||
if (current?.Behavior == null || current.Context == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -70,10 +69,8 @@ namespace Server.Mobiles.BehaviorAI
|
|||
|
||||
if (!current.Behavior.IsRunning(current.Context))
|
||||
{
|
||||
if (current.Observer != null)
|
||||
{
|
||||
current.Observer(current.Context, current.Behavior.GetResult(current.Context));
|
||||
}
|
||||
current.Observer?.Invoke(current.Context, current.Behavior.GetResult(current.Context));
|
||||
current.Behavior.SetResult(context, Result.Terminated);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -86,20 +83,20 @@ namespace Server.Mobiles.BehaviorAI
|
|||
}
|
||||
public void Enqueue(BehaviorTreeContext context, Behavior behavior, BehaviorObserver observer)
|
||||
{
|
||||
if (!behaviorQueueCache.TryGetValue(context.Mobile, out Queue<BehaviorQueueEntry> queue))
|
||||
if (!_behaviorQueueCache.TryGetValue(context.Mobile, out Queue<BehaviorQueueEntry> queue))
|
||||
{
|
||||
queue = new Queue<BehaviorQueueEntry>();
|
||||
behaviorQueueCache.Add(context.Mobile, queue);
|
||||
_behaviorQueueCache.Add(context.Mobile, queue);
|
||||
}
|
||||
|
||||
queue.Enqueue(new BehaviorQueueEntry(context, behavior, observer));
|
||||
}
|
||||
private Queue<BehaviorQueueEntry> getQueue(BehaviorTreeContext context)
|
||||
{
|
||||
if (!behaviorQueueCache.TryGetValue(context.Mobile, out Queue<BehaviorQueueEntry> queue))
|
||||
if (!_behaviorQueueCache.TryGetValue(context.Mobile, out Queue<BehaviorQueueEntry> queue))
|
||||
{
|
||||
queue = new Queue<BehaviorQueueEntry>();
|
||||
behaviorQueueCache.Add(context.Mobile, queue);
|
||||
_behaviorQueueCache.Add(context.Mobile, queue);
|
||||
}
|
||||
|
||||
return queue;
|
||||
|
|
@ -195,38 +192,18 @@ namespace Server.Mobiles.BehaviorAI
|
|||
{
|
||||
var random = Utility.Random(0, 32);
|
||||
|
||||
Direction direction;
|
||||
|
||||
switch (random)
|
||||
Direction direction = random switch
|
||||
{
|
||||
case 0:
|
||||
direction = Direction.Up;
|
||||
break;
|
||||
case 1:
|
||||
direction = Direction.North;
|
||||
break;
|
||||
case 2:
|
||||
direction = Direction.Left;
|
||||
break;
|
||||
case 3:
|
||||
direction = Direction.West;
|
||||
break;
|
||||
case 5:
|
||||
direction = Direction.Down;
|
||||
break;
|
||||
case 6:
|
||||
direction = Direction.South;
|
||||
break;
|
||||
case 7:
|
||||
direction = Direction.Right;
|
||||
break;
|
||||
case 8:
|
||||
direction = Direction.East;
|
||||
break;
|
||||
default:
|
||||
direction = mob.Direction;
|
||||
break;
|
||||
}
|
||||
0 => Direction.Up,
|
||||
1 => Direction.North,
|
||||
2 => Direction.Left,
|
||||
3 => Direction.West,
|
||||
5 => Direction.Down,
|
||||
6 => Direction.South,
|
||||
7 => Direction.Right,
|
||||
8 => Direction.East,
|
||||
_ => mob.Direction
|
||||
};
|
||||
|
||||
DoMove(context, direction, run);
|
||||
}
|
||||
|
|
@ -327,7 +304,9 @@ namespace Server.Mobiles.BehaviorAI
|
|||
}
|
||||
|
||||
if (obstacles.Count > 0)
|
||||
{
|
||||
blocked = true;
|
||||
}
|
||||
|
||||
while (obstacles.Count > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace Server.Mobiles.BehaviorAI
|
|||
currentChildCache.Add(context.Mobile, currentChild);
|
||||
}
|
||||
|
||||
if (GetResult(context) != Result.Running)
|
||||
if (GetResult(context) != Result.Terminated)
|
||||
{
|
||||
currentChild = 0;
|
||||
currentChildCache[context.Mobile] = currentChild;
|
||||
|
|
|
|||
|
|
@ -12,19 +12,15 @@ namespace Server.Mobiles.BehaviorAI
|
|||
}
|
||||
public virtual Decorator AddChild(Behavior child)
|
||||
{
|
||||
if (Child == null)
|
||||
{
|
||||
Child = child;
|
||||
}
|
||||
Child ??= child;
|
||||
return this;
|
||||
}
|
||||
public override void Tick(BehaviorTreeContext context)
|
||||
{
|
||||
if (Child != null && GetResult(context) != Result.Running)
|
||||
if (Child != null && GetResult(context) != Result.Terminated)
|
||||
{
|
||||
Tree.Enqueue(context, Child, OnChildComplete);
|
||||
SetResult(context, Result.Running);
|
||||
return;
|
||||
}
|
||||
}
|
||||
public virtual void OnChildComplete(BehaviorTreeContext context, Result lastResult)
|
||||
|
|
|
|||
|
|
@ -41,18 +41,22 @@ namespace Server.Mobiles.BehaviorAI
|
|||
|
||||
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)
|
||||
|
||||
if (owner.Spell.IsCasting)
|
||||
{
|
||||
SetResult(context, Result.Running);
|
||||
owner.DebugSay("Already casting {0}...", ((Spell)owner.Spell).Name);
|
||||
return;
|
||||
}
|
||||
else if (!owner.Spell.IsCasting && ((Spell)owner.Spell).State == SpellState.Sequencing)
|
||||
|
||||
if (!owner.Spell.IsCasting && ((Spell)owner.Spell).State == SpellState.Sequencing)
|
||||
{
|
||||
SetResult(context, Result.Success);
|
||||
owner.DebugSay("Finished casting {0}...", ((Spell)owner.Spell).Name);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue