behavior tree initial commit
This commit is contained in:
parent
e7e0151b38
commit
53f51d6bde
25 changed files with 1273 additions and 3 deletions
|
|
@ -13,6 +13,7 @@ using Server.Spells;
|
|||
using Server.Spells.Spellweaving;
|
||||
using Server.Targets;
|
||||
using MoveImpl = Server.Movement.MovementImpl;
|
||||
using Server.Mobiles.BehaviorTreeAI;
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
|
|
@ -27,7 +28,8 @@ namespace Server.Mobiles
|
|||
AI_Mage,
|
||||
AI_Berserk,
|
||||
AI_Predator,
|
||||
AI_Thief
|
||||
AI_Thief,
|
||||
AI_BehaviorTree
|
||||
}
|
||||
|
||||
public enum ActionType
|
||||
|
|
@ -136,6 +138,21 @@ namespace Server.Mobiles
|
|||
}
|
||||
|
||||
Action = ActionType.Wander;
|
||||
|
||||
/*
|
||||
behaviorTree = new BehaviorTree(this.m_Mobile);
|
||||
|
||||
behaviorTree.AddRoot(
|
||||
new RepeaterNode(behaviorTree,
|
||||
new SelectorNode(behaviorTree, new BehaviorTreeNode[1]
|
||||
{
|
||||
new SelfHealerNode(behaviorTree),
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
behaviorTree.Start();
|
||||
*/
|
||||
}
|
||||
|
||||
public ActionType Action
|
||||
|
|
@ -2019,7 +2036,7 @@ namespace Server.Mobiles
|
|||
|
||||
if (canOpenDoors || canDestroyObstacles)
|
||||
{
|
||||
m_Mobile.DebugSay("My movement was blocked, I will try to clear some obstacles.");
|
||||
// m_Mobile.DebugSay("My movement was blocked, I will try to clear some obstacles.");
|
||||
|
||||
var map = m_Mobile.Map;
|
||||
|
||||
|
|
@ -2445,7 +2462,7 @@ namespace Server.Mobiles
|
|||
|
||||
m_Mobile.NextReacquireTime = Core.TickCount + (int)m_Mobile.ReacquireDelay.TotalMilliseconds;
|
||||
|
||||
m_Mobile.DebugSay("Acquiring...");
|
||||
// m_Mobile.DebugSay("Acquiring...");
|
||||
|
||||
var map = m_Mobile.Map;
|
||||
|
||||
|
|
@ -3090,6 +3107,7 @@ namespace Server.Mobiles
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (m_Owner.CanDetectHidden && Core.TickCount - m_Owner.m_NextDetectHidden >= 0)
|
||||
{
|
||||
m_Owner.DetectHidden();
|
||||
|
|
@ -3103,6 +3121,7 @@ namespace Server.Mobiles
|
|||
m_Owner.m_NextDetectHidden = Core.TickCount +
|
||||
(int)TimeSpan.FromSeconds(Utility.RandomMinMax(min, max)).TotalMilliseconds;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
45
Projects/UOContent/Mobiles/AI/BehaviorTreeAI.cs
Normal file
45
Projects/UOContent/Mobiles/AI/BehaviorTreeAI.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Server.Mobiles.BehaviorTreeAI;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Mobiles.AI
|
||||
{
|
||||
public class BehaviorTreeAI : BaseAI
|
||||
{
|
||||
private BehaviorTree behaviorTree;
|
||||
public BehaviorTreeAI(BaseCreature m) : base(m)
|
||||
{
|
||||
behaviorTree = new BehaviorTree(m);
|
||||
|
||||
behaviorTree.AddRoot(
|
||||
new SelectorNode(behaviorTree, new BehaviorTreeNode[2]
|
||||
{
|
||||
new SelfHealerNode(behaviorTree),
|
||||
// new MageComboNode(behaviorTree),
|
||||
new TapNode(
|
||||
behaviorTree,
|
||||
(mob) => mob.DebugSay("I am taking no action")
|
||||
)
|
||||
})
|
||||
);
|
||||
|
||||
behaviorTree.Start();
|
||||
}
|
||||
|
||||
public override bool Think()
|
||||
{
|
||||
if (m_Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// behaviorTree.RunBehavior();
|
||||
|
||||
return base.Think();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -106,12 +106,14 @@ namespace Server.Mobiles
|
|||
|
||||
base.DoActionWander();
|
||||
|
||||
/*
|
||||
if (Utility.RandomDouble() < 0.05)
|
||||
{
|
||||
var spell = CheckCastHealingSpell();
|
||||
|
||||
spell?.Cast();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -120,10 +122,12 @@ namespace Server.Mobiles
|
|||
private Spell CheckCastHealingSpell()
|
||||
{
|
||||
// If I'm poisoned, always attempt to cure.
|
||||
/*
|
||||
if (m_Mobile.Poisoned)
|
||||
{
|
||||
return new CureSpell(m_Mobile);
|
||||
}
|
||||
*/
|
||||
|
||||
// Summoned creatures never heal themselves.
|
||||
if (m_Mobile.Summoned)
|
||||
|
|
@ -156,6 +160,7 @@ namespace Server.Mobiles
|
|||
|
||||
Spell spell = null;
|
||||
|
||||
/*
|
||||
if (m_Mobile.Hits < m_Mobile.HitsMax - 50)
|
||||
{
|
||||
if (UseNecromancy())
|
||||
|
|
@ -171,6 +176,7 @@ namespace Server.Mobiles
|
|||
{
|
||||
spell = new HealSpell(m_Mobile);
|
||||
}
|
||||
*/
|
||||
|
||||
double delay;
|
||||
|
||||
|
|
@ -775,6 +781,7 @@ namespace Server.Mobiles
|
|||
Spell spell;
|
||||
var toDispel = FindDispelTarget(true);
|
||||
|
||||
/*
|
||||
if (m_Mobile.Poisoned) // Top cast priority is cure
|
||||
{
|
||||
m_Mobile.DebugSay("I am going to cure myself");
|
||||
|
|
@ -782,6 +789,8 @@ namespace Server.Mobiles
|
|||
spell = new CureSpell(m_Mobile);
|
||||
}
|
||||
else if (toDispel != null) // Something dispellable is attacking us
|
||||
*/
|
||||
if (toDispel != null)
|
||||
{
|
||||
m_Mobile.DebugSay("I am going to dispel {0}", toDispel);
|
||||
|
||||
|
|
@ -901,10 +910,12 @@ namespace Server.Mobiles
|
|||
RunFrom(m_Mobile.FocusMob);
|
||||
m_Mobile.FocusMob = null;
|
||||
|
||||
/*
|
||||
if (m_Mobile.Poisoned && Utility.Random(0, 5) == 0)
|
||||
{
|
||||
new CureSpell(m_Mobile).Cast();
|
||||
}
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2187,6 +2187,7 @@ namespace Server.Mobiles
|
|||
// m_AI = new PredatorAI(this);
|
||||
new MeleeAI(this),
|
||||
AIType.AI_Thief => new ThiefAI(this),
|
||||
AIType.AI_BehaviorTree => new AI.BehaviorTreeAI(this),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
|
|
|||
15
Projects/UOContent/Mobiles/BehaviorTreeAI/ActionNode.cs
Normal file
15
Projects/UOContent/Mobiles/BehaviorTreeAI/ActionNode.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public abstract class ActionNode : LeafNode
|
||||
{
|
||||
public ActionNode(BehaviorTree tree) : base(tree)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Server.Spells;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public class CastSpellActionNode : ActionNode
|
||||
{
|
||||
private Func<BaseCreature, Spell> getSpellCallback;
|
||||
public CastSpellActionNode(BehaviorTree tree, Func<BaseCreature, Spell> callback) : base(tree)
|
||||
{
|
||||
object nextCastTime;
|
||||
if (!Tree.Blackboard.TryGetValue("nextCastTime", out nextCastTime))
|
||||
{
|
||||
tree.Blackboard.Add("nextCastTime", DateTime.Now);
|
||||
}
|
||||
getSpellCallback = callback;
|
||||
}
|
||||
|
||||
public override Result Execute()
|
||||
{
|
||||
object nextCastTime;
|
||||
|
||||
if (Tree.Blackboard.TryGetValue("nextCastTime", out nextCastTime) && DateTime.Now < (DateTime)nextCastTime)
|
||||
{
|
||||
Tree.Mobile.DebugSay("Spells are on cooldown...");
|
||||
return Result.Failure;
|
||||
}
|
||||
|
||||
if (Tree.Mobile.Spell == null)
|
||||
{
|
||||
Spell spell = getSpellCallback(Tree.Mobile);
|
||||
|
||||
Tree.Mobile.DebugSay("Casting...");
|
||||
if (!spell.Cast())
|
||||
{
|
||||
Tree.Mobile.DebugSay("Failed to cast...");
|
||||
return Result.Failure;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(spell.Mantra))
|
||||
Tree.Mobile.PublicOverheadMessage(Network.MessageType.Spell, 0, false, spell.Mantra, false);
|
||||
|
||||
return Result.Running;
|
||||
}
|
||||
else if (Tree.Mobile.Spell.IsCasting)
|
||||
{
|
||||
Tree.Mobile.DebugSay("Still casting...");
|
||||
return Result.Running;
|
||||
}
|
||||
else if (!Tree.Mobile.Spell.IsCasting)
|
||||
{
|
||||
Tree.Mobile.DebugSay("Finished casting...");
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return Result.Failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using Server.Spells;
|
||||
using Server.Spells.Second;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public class CureSelfNode : SequenceNode
|
||||
{
|
||||
public CureSelfNode(BehaviorTree tree) : base(tree)
|
||||
{
|
||||
Children = new List<BehaviorTreeNode>(new BehaviorTreeNode[5]
|
||||
{
|
||||
new ConditionNode(
|
||||
tree,
|
||||
(mob) =>
|
||||
{
|
||||
if (mob.Poison == null)
|
||||
{
|
||||
mob.DebugSay("I am not poisoned");
|
||||
return false;
|
||||
}
|
||||
|
||||
object nextHealTime;
|
||||
if (Tree.Blackboard.TryGetValue("nextHealTime", out nextHealTime))
|
||||
{
|
||||
if (DateTime.Now >= (DateTime)nextHealTime)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
mob.DebugSay("My heal cooldown is active");
|
||||
}
|
||||
|
||||
mob.DebugSay("My heal cooldown is not set");
|
||||
|
||||
return false;
|
||||
}
|
||||
),
|
||||
new ConditionNode(
|
||||
tree,
|
||||
(mob) =>
|
||||
{
|
||||
return mob.Spell == null || !mob.Spell.IsCasting;
|
||||
}
|
||||
),
|
||||
new CastSpellActionNode(
|
||||
tree,
|
||||
(mob) => new CureSpell(mob)
|
||||
),
|
||||
new AlwaysSucceedNode(
|
||||
tree,
|
||||
new TapNode(
|
||||
tree,
|
||||
(mob) =>
|
||||
{
|
||||
Tree.Blackboard.Remove("nextHealTime");
|
||||
Tree.Blackboard.Add("nextHealTime", DateTime.Now + TimeSpan.FromSeconds(10.0));
|
||||
}
|
||||
)
|
||||
),
|
||||
new TargetActionNode(tree),
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
/*
|
||||
public class CureSelfActionNode : ActionNode
|
||||
{
|
||||
public TimeSpan Cooldown { get; private set; }
|
||||
public CureSelfActionNode(BehaviorTree tree, TimeSpan cooldown) : base(tree)
|
||||
{
|
||||
object nextHealTime;
|
||||
if (!Tree.Blackboard.TryGetValue("nextHealTime", out nextHealTime))
|
||||
{
|
||||
tree.Blackboard.Add("nextHealTime", DateTime.Now);
|
||||
}
|
||||
Cooldown = cooldown;
|
||||
}
|
||||
|
||||
public override Result Execute()
|
||||
{
|
||||
object nextHealTime;
|
||||
if (Tree.Mobile.Poison != null && Tree.Blackboard.TryGetValue("nextHealTime", out nextHealTime))
|
||||
{
|
||||
if (DateTime.Now >= (DateTime)nextHealTime)
|
||||
{
|
||||
new CureSpell(Tree.Mobile).Cast();
|
||||
Tree.Mobile.DebugSay("Casting cure spell for myself");
|
||||
Tree.Blackboard.Remove("nextHealTime");
|
||||
Tree.Blackboard.Add("nextHealTime", DateTime.Now + Cooldown);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
Tree.Mobile.DebugSay("I'm poisoned, but my healing is on cooldown");
|
||||
|
||||
return Result.Failure;
|
||||
}
|
||||
|
||||
Tree.Mobile.DebugSay("I'm not poisoned");
|
||||
|
||||
return Result.Failure;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
class DetectHiddenActionNode : BehaviorTreeNode
|
||||
{
|
||||
public TimeSpan Cooldown { get; private set; }
|
||||
public DetectHiddenActionNode(BehaviorTree tree, TimeSpan cooldown) : base(tree)
|
||||
{
|
||||
object nextDetectTime;
|
||||
if (!Tree.Blackboard.TryGetValue("nextDetectTime", out nextDetectTime))
|
||||
{
|
||||
tree.Blackboard.Add("nextDetectTime", DateTime.Now);
|
||||
}
|
||||
Cooldown = cooldown;
|
||||
}
|
||||
public override Result Execute()
|
||||
{
|
||||
/*
|
||||
if (m_Mobile.Deleted || m_Mobile.Map == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_Mobile.DebugSay("Checking for hidden players");
|
||||
|
||||
var srcSkill = m_Mobile.Skills.DetectHidden.Value;
|
||||
|
||||
if (srcSkill <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var eable = m_Mobile.GetMobilesInRange(m_Mobile.RangePerception);
|
||||
|
||||
foreach (var trg in eable)
|
||||
{
|
||||
if (trg != m_Mobile && trg.Player && trg.Alive && trg.Hidden && trg.AccessLevel == AccessLevel.Player &&
|
||||
m_Mobile.InLOS(trg))
|
||||
{
|
||||
m_Mobile.DebugSay("Trying to detect {0}", trg.Name);
|
||||
|
||||
var trgHiding = trg.Skills.Hiding.Value / 2.9;
|
||||
var trgStealth = trg.Skills.Stealth.Value / 1.8;
|
||||
|
||||
var chance = srcSkill / 1.2 - Math.Min(trgHiding, trgStealth);
|
||||
|
||||
if (chance < srcSkill / 10)
|
||||
{
|
||||
chance = srcSkill / 10;
|
||||
}
|
||||
|
||||
chance /= 100;
|
||||
|
||||
if (chance > Utility.RandomDouble())
|
||||
{
|
||||
trg.RevealingAction();
|
||||
trg.SendLocalizedMessage(500814); // You have been revealed!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
*/
|
||||
|
||||
Tree.Mobile.DebugSay("DetectHiddenActionNode");
|
||||
|
||||
object nextDetectTime;
|
||||
|
||||
if (Tree.Blackboard.TryGetValue("nextDetectTime", out nextDetectTime))
|
||||
{
|
||||
if (DateTime.Now >= (DateTime)nextDetectTime)
|
||||
{
|
||||
var srcSkill = Tree.Mobile.Skills.DetectHidden.Value;
|
||||
|
||||
var eable = Tree.Mobile.GetMobilesInRange(Tree.Mobile.RangePerception);
|
||||
|
||||
foreach (var trg in eable)
|
||||
{
|
||||
if (trg != Tree.Mobile && trg.Player && trg.Alive && trg.Hidden && trg.AccessLevel == AccessLevel.Player &&
|
||||
Tree.Mobile.InLOS(trg))
|
||||
{
|
||||
Tree.Mobile.DebugSay("DetectHiddenActionNode {0}", trg.Name);
|
||||
|
||||
var trgHiding = trg.Skills.Hiding.Value / 2.9;
|
||||
var trgStealth = trg.Skills.Stealth.Value / 1.8;
|
||||
|
||||
var chance = srcSkill / 1.2 - Math.Min(trgHiding, trgStealth);
|
||||
|
||||
if (chance < srcSkill / 10)
|
||||
{
|
||||
chance = srcSkill / 10;
|
||||
}
|
||||
|
||||
chance /= 100;
|
||||
|
||||
if (chance > Utility.RandomDouble())
|
||||
{
|
||||
trg.RevealingAction();
|
||||
trg.SendLocalizedMessage(500814); // You have been revealed!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
Tree.Mobile.DebugSay("DetectHiddenActionNode Cooldown");
|
||||
}
|
||||
|
||||
return Result.Failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Server.Spells.First;
|
||||
using Server.Spells.Fourth;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
|
||||
public class HealSelfActionNode : BehaviorTreeNode
|
||||
{
|
||||
public TimeSpan Cooldown { get; private set; }
|
||||
public HealSelfActionNode(BehaviorTree tree, TimeSpan cooldown) : base(tree)
|
||||
{
|
||||
object nextHealTime;
|
||||
if (!Tree.Blackboard.TryGetValue("nextHealTime", out nextHealTime))
|
||||
{
|
||||
tree.Blackboard.Add("nextHealTime", DateTime.Now);
|
||||
}
|
||||
Cooldown = cooldown;
|
||||
}
|
||||
public override Result Execute()
|
||||
{
|
||||
object nextHealTime;
|
||||
if (Tree.Mobile.Hits < Tree.Mobile.HitsMax && Tree.Blackboard.TryGetValue("nextHealTime", out nextHealTime))
|
||||
{
|
||||
if (DateTime.Now >= (DateTime)nextHealTime)
|
||||
{
|
||||
if (Tree.Mobile.Hits < Tree.Mobile.HitsMax - 50)
|
||||
{
|
||||
new GreaterHealSpell(Tree.Mobile).Cast();
|
||||
Tree.Mobile.DebugSay("Casting greater heal for myself");
|
||||
}
|
||||
else
|
||||
{
|
||||
new HealSpell(Tree.Mobile).Cast();
|
||||
Tree.Mobile.DebugSay("Casting heal spell for myself");
|
||||
}
|
||||
|
||||
Tree.Blackboard.Remove("nextHealTime");
|
||||
Tree.Blackboard.Add("nextHealTime", DateTime.Now + Cooldown);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
Tree.Mobile.DebugSay("I'm hurt, but my healing is on cooldown {0}s", ((DateTime)nextHealTime - DateTime.Now).TotalSeconds);
|
||||
|
||||
return Result.Failure;
|
||||
}
|
||||
|
||||
Tree.Mobile.DebugSay("I'm at full health");
|
||||
|
||||
return Result.Failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Server.Spells.Sixth;
|
||||
using Server.Spells.Fifth;
|
||||
using Server.Spells.First;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public class MageComboNode : SequenceNode
|
||||
{
|
||||
public MageComboNode(BehaviorTree tree) : base(tree)
|
||||
{
|
||||
/*
|
||||
Children = new List<BehaviorTreeNode>(new BehaviorTreeNode[1]
|
||||
{
|
||||
// if we have a combatant
|
||||
new ConditionNode(
|
||||
tree,
|
||||
mob => mob.Combatant != null,
|
||||
)
|
||||
new ConditionNode(
|
||||
tree,
|
||||
mob => mob.Combatant != null,
|
||||
// do the following in sequence
|
||||
new SelectorNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[2]
|
||||
{
|
||||
// do the first appropriate debuff until all are done
|
||||
new SelectorNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[3]
|
||||
{
|
||||
// weaken if necessary
|
||||
new ConditionNode(
|
||||
tree,
|
||||
mob => mob.Combatant.Str == mob.Combatant.RawStr,
|
||||
new SequenceNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[2]
|
||||
{
|
||||
new CastSpellActionNode(
|
||||
tree,
|
||||
mob => new WeakenSpell(mob)
|
||||
),
|
||||
new TargetActionNode(tree)
|
||||
}
|
||||
)
|
||||
),
|
||||
// clumbsy if necessary
|
||||
new ConditionNode(
|
||||
tree,
|
||||
mob => mob.Combatant.Dex == mob.Combatant.RawDex,
|
||||
new SequenceNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[2]
|
||||
{
|
||||
new CastSpellActionNode(
|
||||
tree,
|
||||
mob => new ClumsySpell(mob)
|
||||
),
|
||||
new TargetActionNode(tree)
|
||||
}
|
||||
)
|
||||
),
|
||||
// feeblemind if necessary
|
||||
new ConditionNode(
|
||||
tree,
|
||||
mob => mob.Combatant.Int == mob.Combatant.RawInt,
|
||||
new SequenceNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[2]
|
||||
{
|
||||
new CastSpellActionNode(
|
||||
tree,
|
||||
mob => new FeeblemindSpell(mob)
|
||||
),
|
||||
new TargetActionNode(tree)
|
||||
}
|
||||
)
|
||||
),
|
||||
}
|
||||
),
|
||||
// do explosion ebolt combo
|
||||
new SequenceNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[2]
|
||||
{
|
||||
// explosion
|
||||
new SequenceNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[2]
|
||||
{
|
||||
new CastSpellActionNode(
|
||||
tree,
|
||||
mob => new ExplosionSpell(mob)
|
||||
),
|
||||
new TargetActionNode(tree)
|
||||
}
|
||||
),
|
||||
// ebolt
|
||||
new SequenceNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[2]
|
||||
{
|
||||
new CastSpellActionNode(
|
||||
tree,
|
||||
mob => new EnergyBoltSpell(mob)
|
||||
),
|
||||
new TargetActionNode(tree)
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
});
|
||||
*/
|
||||
|
||||
/*
|
||||
Children = new List<BehaviorTreeNode>(new BehaviorTreeNode[1] {
|
||||
new ConditionNode(
|
||||
tree,
|
||||
mob => mob.Combatant != null,
|
||||
new SequenceNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[3] {
|
||||
new AlwaysSucceedNode(
|
||||
tree,
|
||||
new ConditionNode(
|
||||
tree,
|
||||
mob => mob.Combatant != null && mob.Combatant.Str == mob.Combatant.RawStr,
|
||||
new SequenceNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[2]
|
||||
{
|
||||
new CastSpellActionNode(
|
||||
tree,
|
||||
mob => new WeakenSpell(mob)
|
||||
),
|
||||
new TargetActionNode(tree)
|
||||
}
|
||||
)
|
||||
)
|
||||
),
|
||||
new SequenceNode(tree, new BehaviorTreeNode[2]{
|
||||
new CastSpellActionNode(
|
||||
tree,
|
||||
mob => new ExplosionSpell(mob)
|
||||
),
|
||||
new TargetActionNode(tree)
|
||||
}),
|
||||
new SequenceNode(tree, new BehaviorTreeNode[2]{
|
||||
new SelectorNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[2]
|
||||
{
|
||||
new ConditionNode(
|
||||
tree,
|
||||
mob => Utility.RandomDouble() < 0.3,
|
||||
new CastSpellActionNode(
|
||||
tree,
|
||||
mob => new MindBlastSpell(mob)
|
||||
)
|
||||
),
|
||||
new CastSpellActionNode(
|
||||
tree,
|
||||
mob => new EnergyBoltSpell(mob)
|
||||
)
|
||||
}
|
||||
),
|
||||
new TargetActionNode(tree)
|
||||
}),
|
||||
}
|
||||
)
|
||||
)
|
||||
});
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
class SelfHealerNode : SelectorNode
|
||||
{
|
||||
public SelfHealerNode(BehaviorTree tree) : base(tree)
|
||||
{
|
||||
Children = new List<BehaviorTreeNode>(new BehaviorTreeNode[1]
|
||||
{
|
||||
new SequenceNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[2]
|
||||
{
|
||||
new ConditionNode(
|
||||
tree,
|
||||
mob => true // Utility.RandomDouble() < 0.5
|
||||
),
|
||||
new CureSelfNode(tree),
|
||||
}
|
||||
)
|
||||
/*
|
||||
new SequenceNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[2]
|
||||
{
|
||||
new ConditionNode(
|
||||
tree,
|
||||
mob => mob.Hits < mob.HitsMax - 20
|
||||
),
|
||||
new SequenceNode(
|
||||
tree,
|
||||
new BehaviorTreeNode[3]
|
||||
{
|
||||
new ConditionNode(
|
||||
tree,
|
||||
mob => true // Utility.RandomDouble() < 0.2
|
||||
),
|
||||
new HealSelfActionNode(tree, TimeSpan.FromSeconds(10.0)),
|
||||
new TargetActionNode(tree)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
*/
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Server.Spells;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public class TargetActionNode : ActionNode
|
||||
{
|
||||
public TargetActionNode(BehaviorTree tree) : base(tree)
|
||||
{
|
||||
}
|
||||
|
||||
public override Result Execute()
|
||||
{
|
||||
Target target = Tree.Mobile.Target;
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
Tree.Mobile.DebugSay("Waiting for target...");
|
||||
return Result.Running;
|
||||
}
|
||||
|
||||
Mobile mobTarget = Tree.Mobile.Combatant;
|
||||
|
||||
if ((target.Flags & TargetFlags.Harmful) != 0 && mobTarget != null)
|
||||
{
|
||||
if (mobTarget.Deleted || !mobTarget.Alive)
|
||||
{
|
||||
Tree.Mobile.DebugSay("Canceling my target because my target is dead or does not exist");
|
||||
target.Cancel(Tree.Mobile, TargetCancelType.Canceled);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
if ((target.Range == -1 ||
|
||||
Tree.Mobile.InRange(mobTarget, target.Range)) &&
|
||||
Tree.Mobile.CanSee(mobTarget) &&
|
||||
Tree.Mobile.InLOS(mobTarget)
|
||||
)
|
||||
{
|
||||
Tree.Mobile.DebugSay("Targeting my combatant");
|
||||
target.Invoke(Tree.Mobile, mobTarget);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
else if ((target.Flags & TargetFlags.Beneficial) != 0)
|
||||
{
|
||||
Tree.Mobile.DebugSay("Targeting myself");
|
||||
target.Invoke(Tree.Mobile, Tree.Mobile);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return Result.Failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public class AlwaysSucceedNode : DecoratorNode
|
||||
{
|
||||
public AlwaysSucceedNode(BehaviorTree tree, BehaviorTreeNode child) : base(tree, child)
|
||||
{
|
||||
}
|
||||
public AlwaysSucceedNode(BehaviorTree tree) : base(tree)
|
||||
{
|
||||
}
|
||||
|
||||
public override Result Execute()
|
||||
{
|
||||
if (Child != null)
|
||||
{
|
||||
Child.Execute();
|
||||
}
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
207
Projects/UOContent/Mobiles/BehaviorTreeAI/BehaviorTree.cs
Normal file
207
Projects/UOContent/Mobiles/BehaviorTreeAI/BehaviorTree.cs
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public class BehaviorTree
|
||||
{
|
||||
/*
|
||||
public static BehaviorTree Instance;
|
||||
private static BehaviorTreeTimer timer;
|
||||
public static void Initialize()
|
||||
{
|
||||
Instance = new BehaviorTree();
|
||||
|
||||
var seq = new SequenceNode(Instance, new BehaviorTreeNode[3]{
|
||||
new ActionNode(Instance, () => { Console.WriteLine("ActionNode1"); return BehaviorTreeNode.Result.Success; }),
|
||||
new ActionNode(Instance, () => { Console.WriteLine("ActionNode2"); return BehaviorTreeNode.Result.Success; }),
|
||||
new SequenceNode(Instance, new BehaviorTreeNode[4]{
|
||||
new ActionNode(Instance, () => { Console.WriteLine("ActionNode3"); return BehaviorTreeNode.Result.Success; }),
|
||||
new ActionNode(Instance, () => { Console.WriteLine("ActionNode4"); return BehaviorTreeNode.Result.Success; }),
|
||||
new ActionNode(Instance, () => { Console.WriteLine("ActionNode5"); return BehaviorTreeNode.Result.Success; }),
|
||||
new SelectorNode(Instance, new BehaviorTreeNode[3]
|
||||
{
|
||||
new ActionNode(Instance, () => { Console.WriteLine("ActionNode6"); return BehaviorTreeNode.Result.Failure; }),
|
||||
new ActionNode(Instance, () => { Console.WriteLine("ActionNode7"); return BehaviorTreeNode.Result.Success; }),
|
||||
new ActionNode(Instance, () => { Console.WriteLine("ActionNode8"); return BehaviorTreeNode.Result.Success; })
|
||||
})
|
||||
}),
|
||||
});
|
||||
|
||||
Instance.AddRoot(
|
||||
new RepeaterNode(Instance, seq)
|
||||
);
|
||||
|
||||
Instance.Start();
|
||||
}
|
||||
*/
|
||||
|
||||
private BehaviorTreeNode root;
|
||||
public Dictionary<string, object> Blackboard { get; set; }
|
||||
public BehaviorTreeNode Root { get { return root; } }
|
||||
|
||||
private BehaviorTreeTimer treeTimer;
|
||||
|
||||
public BaseCreature Mobile { get; private set; }
|
||||
|
||||
public BehaviorTree(BaseCreature mob)
|
||||
{
|
||||
Blackboard = new Dictionary<string, object>();
|
||||
treeTimer = new BehaviorTreeTimer(this);
|
||||
Mobile = mob;
|
||||
}
|
||||
|
||||
public void AddRoot(BehaviorTreeNode rootNode)
|
||||
{
|
||||
if (root == null)
|
||||
{
|
||||
root = rootNode;
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
treeTimer.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
treeTimer.Stop();
|
||||
}
|
||||
|
||||
public virtual void Tick()
|
||||
{
|
||||
RunBehavior();
|
||||
}
|
||||
|
||||
public virtual void RunBehavior()
|
||||
{
|
||||
Root.Execute();
|
||||
}
|
||||
|
||||
private class BehaviorTreeTimer : Timer
|
||||
{
|
||||
private BehaviorTree behaviorTree;
|
||||
public BehaviorTreeTimer(BehaviorTree tree) : base(TimeSpan.Zero, TimeSpan.FromSeconds(1.0))
|
||||
{
|
||||
behaviorTree = tree;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (behaviorTree != null)
|
||||
{
|
||||
behaviorTree.Tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
public enum ControlFlowNodeType
|
||||
{
|
||||
Sequence,
|
||||
Fallback,
|
||||
Parallel,
|
||||
Decorator
|
||||
}
|
||||
|
||||
public enum ExecutionNodeType
|
||||
{
|
||||
Action,
|
||||
Condition
|
||||
}
|
||||
|
||||
public class BehaviorTreeNode
|
||||
{
|
||||
private readonly List<BehaviorTreeNode> _children = new List<BehaviorTreeNode>();
|
||||
public bool Root { get { return Parent == null; } }
|
||||
|
||||
public BehaviorTreeNode() { }
|
||||
public BehaviorTreeNode(BehaviorTreeNode parent) : this()
|
||||
{
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
public BehaviorTreeNode this[int i]
|
||||
{
|
||||
get { return _children[i]; }
|
||||
}
|
||||
|
||||
public BehaviorTreeNode Parent { get; private set; }
|
||||
|
||||
public ReadOnlyCollection<BehaviorTreeNode> Children
|
||||
{
|
||||
get { return _children.AsReadOnly(); }
|
||||
}
|
||||
|
||||
public virtual BehaviorTreeNode AddChild(BehaviorTreeNode node)
|
||||
{
|
||||
_children.Add(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public virtual bool Evaluate(BaseCreature creature)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool Traverse(BaseCreature creature)
|
||||
{
|
||||
foreach (var child in _children)
|
||||
child.Traverse(creature);
|
||||
}
|
||||
}
|
||||
|
||||
public class SequenceNode : BehaviorTreeNode {
|
||||
public override bool Evaluate(BaseCreature creature)
|
||||
{
|
||||
return Children.All((BehaviorTreeNode node) => node.Evaluate(creature));
|
||||
}
|
||||
|
||||
public override bool Traverse(BaseCreature creature)
|
||||
{
|
||||
if (Evaluate(creature)) return true;
|
||||
|
||||
foreach (var child in Children)
|
||||
child.Traverse(creature);
|
||||
}
|
||||
}
|
||||
|
||||
public class FallbackNode : BehaviorTreeNode
|
||||
{
|
||||
public override bool Evaluate(BaseCreature creature)
|
||||
{
|
||||
return Children.Any((BehaviorTreeNode node) => node.Evaluate(creature));
|
||||
}
|
||||
|
||||
public override bool Traverse(BaseCreature creature)
|
||||
{
|
||||
return Evaluate(creature);
|
||||
}
|
||||
}
|
||||
|
||||
public class ParallelNode : BehaviorTreeNode
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class DecoratorNode : BehaviorTreeNode
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class ActionNode : BehaviorTreeNode
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class ConditionNode : BehaviorTreeNode
|
||||
{
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public abstract class BehaviorTreeNode
|
||||
{
|
||||
public enum Result { Running, Failure, Success }
|
||||
|
||||
public BehaviorTree Tree { get; private set; }
|
||||
|
||||
public BehaviorTreeNode(BehaviorTree tree)
|
||||
{
|
||||
Tree = tree;
|
||||
}
|
||||
public abstract Result Execute();
|
||||
}
|
||||
}
|
||||
25
Projects/UOContent/Mobiles/BehaviorTreeAI/ConditionNode.cs
Normal file
25
Projects/UOContent/Mobiles/BehaviorTreeAI/ConditionNode.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public class ConditionNode : LeafNode
|
||||
{
|
||||
private Predicate<BaseCreature> predicate;
|
||||
public ConditionNode(BehaviorTree tree, Predicate<BaseCreature> fn) : base(tree)
|
||||
{
|
||||
predicate = fn;
|
||||
}
|
||||
public override Result Execute()
|
||||
{
|
||||
if (predicate(Tree.Mobile))
|
||||
{
|
||||
return Result.Success;
|
||||
}
|
||||
return Result.Failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Projects/UOContent/Mobiles/BehaviorTreeAI/DecoratorNode.cs
Normal file
21
Projects/UOContent/Mobiles/BehaviorTreeAI/DecoratorNode.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public abstract class DecoratorNode : BehaviorTreeNode
|
||||
{
|
||||
public BehaviorTreeNode Child { get; set; }
|
||||
|
||||
public DecoratorNode(BehaviorTree tree) : this(tree, null)
|
||||
{
|
||||
}
|
||||
public DecoratorNode(BehaviorTree tree, BehaviorTreeNode child) : base(tree)
|
||||
{
|
||||
Child = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
35
Projects/UOContent/Mobiles/BehaviorTreeAI/InverterNode.cs
Normal file
35
Projects/UOContent/Mobiles/BehaviorTreeAI/InverterNode.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public class InverterNode : DecoratorNode
|
||||
{
|
||||
public InverterNode(BehaviorTree tree) : base(tree)
|
||||
{
|
||||
}
|
||||
|
||||
public override Result Execute()
|
||||
{
|
||||
if (Child != null)
|
||||
{
|
||||
var result = Child.Execute();
|
||||
|
||||
if (result == Result.Success)
|
||||
{
|
||||
return Result.Failure;
|
||||
}
|
||||
else if (result == Result.Failure)
|
||||
{
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return Result.Running;
|
||||
}
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Projects/UOContent/Mobiles/BehaviorTreeAI/LeafNode.cs
Normal file
15
Projects/UOContent/Mobiles/BehaviorTreeAI/LeafNode.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public abstract class LeafNode : BehaviorTreeNode
|
||||
{
|
||||
public LeafNode(BehaviorTree tree) : base(tree)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Projects/UOContent/Mobiles/BehaviorTreeAI/RepeaterNode.cs
Normal file
28
Projects/UOContent/Mobiles/BehaviorTreeAI/RepeaterNode.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public class RepeaterNode : DecoratorNode
|
||||
{
|
||||
public RepeaterNode(BehaviorTree tree) : base(tree)
|
||||
{
|
||||
}
|
||||
|
||||
public RepeaterNode(BehaviorTree tree, BehaviorTreeNode child) : base(tree, child)
|
||||
{
|
||||
}
|
||||
|
||||
public override Result Execute()
|
||||
{
|
||||
if (Child != null)
|
||||
{
|
||||
Child.Execute();
|
||||
}
|
||||
return Result.Running;
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Projects/UOContent/Mobiles/BehaviorTreeAI/SelectorNode.cs
Normal file
48
Projects/UOContent/Mobiles/BehaviorTreeAI/SelectorNode.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
class SelectorNode : BehaviorTreeNode
|
||||
{
|
||||
int currentTask = 0;
|
||||
public List<BehaviorTreeNode> Children { get; protected set; }
|
||||
public SelectorNode(BehaviorTree tree) : this(tree, new BehaviorTreeNode[0]) { }
|
||||
public SelectorNode(BehaviorTree tree, BehaviorTreeNode[] children) : base(tree)
|
||||
{
|
||||
Children = new List<BehaviorTreeNode>(children);
|
||||
}
|
||||
|
||||
public override Result Execute()
|
||||
{
|
||||
if (currentTask < Children.Count)
|
||||
{
|
||||
var result = Children[currentTask].Execute();
|
||||
|
||||
if (result == Result.Success)
|
||||
{
|
||||
currentTask = 0;
|
||||
return Result.Success;
|
||||
}
|
||||
else if (result == Result.Running)
|
||||
{
|
||||
return Result.Running;
|
||||
}
|
||||
|
||||
currentTask++;
|
||||
if (currentTask == Children.Count)
|
||||
{
|
||||
currentTask = 0;
|
||||
return Result.Failure;
|
||||
}
|
||||
|
||||
return Result.Running;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Projects/UOContent/Mobiles/BehaviorTreeAI/SequenceNode.cs
Normal file
48
Projects/UOContent/Mobiles/BehaviorTreeAI/SequenceNode.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public class SequenceNode : BehaviorTreeNode
|
||||
{
|
||||
int currentTask = 0;
|
||||
public List<BehaviorTreeNode> Children { get; protected set; }
|
||||
public SequenceNode(BehaviorTree tree) : this(tree, new BehaviorTreeNode[0]) { }
|
||||
public SequenceNode(BehaviorTree tree, BehaviorTreeNode[] children) : base(tree)
|
||||
{
|
||||
Children = new List<BehaviorTreeNode>(children);
|
||||
}
|
||||
|
||||
public override Result Execute()
|
||||
{
|
||||
if(currentTask < Children.Count)
|
||||
{
|
||||
var result = Children[currentTask].Execute();
|
||||
|
||||
if (result == Result.Running)
|
||||
{
|
||||
return Result.Running;
|
||||
}
|
||||
else if (result == Result.Failure)
|
||||
{
|
||||
currentTask = 0;
|
||||
return Result.Failure;
|
||||
}
|
||||
|
||||
currentTask++;
|
||||
if(currentTask == Children.Count)
|
||||
{
|
||||
currentTask = 0;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return Result.Running;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Projects/UOContent/Mobiles/BehaviorTreeAI/TapNode.cs
Normal file
31
Projects/UOContent/Mobiles/BehaviorTreeAI/TapNode.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public class TapNode : DecoratorNode
|
||||
{
|
||||
public Action<BaseCreature> Action { get; private set; }
|
||||
public TapNode(BehaviorTree tree, Action<BaseCreature> action, BehaviorTreeNode child) : base(tree, child)
|
||||
{
|
||||
}
|
||||
public TapNode(BehaviorTree tree, Action<BaseCreature> action) : base(tree)
|
||||
{
|
||||
Action = action;
|
||||
}
|
||||
public override Result Execute()
|
||||
{
|
||||
Action(Tree.Mobile);
|
||||
|
||||
if (Child != null)
|
||||
{
|
||||
return Child.Execute();
|
||||
}
|
||||
|
||||
return Result.Failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Projects/UOContent/Mobiles/BehaviorTreeAI/UntilFailNode.cs
Normal file
29
Projects/UOContent/Mobiles/BehaviorTreeAI/UntilFailNode.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server.Mobiles.BehaviorTreeAI
|
||||
{
|
||||
public class UntilFailNode : DecoratorNode
|
||||
{
|
||||
public UntilFailNode(BehaviorTree tree) : base(tree)
|
||||
{
|
||||
}
|
||||
|
||||
public override Result Execute()
|
||||
{
|
||||
if (Child != null)
|
||||
{
|
||||
var result = Child.Execute();
|
||||
|
||||
if (result == Result.Failure)
|
||||
{
|
||||
return Result.Failure;
|
||||
}
|
||||
}
|
||||
return Result.Running;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,10 @@ namespace Server.Mobiles
|
|||
[Constructible]
|
||||
public EvilHealer()
|
||||
{
|
||||
AI = AIType.AI_BehaviorTree;
|
||||
|
||||
ChangeAIType(AI);
|
||||
|
||||
Title = "the healer";
|
||||
|
||||
Karma = -10000;
|
||||
|
|
@ -63,6 +67,8 @@ namespace Server.Mobiles
|
|||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
AI = AIType.AI_BehaviorTree;
|
||||
|
||||
var version = reader.ReadInt();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue