@@ -0,0 +1,175 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "Parallel.h"

#include "console/engineAPI.h"

using namespace BadBehavior;

//------------------------------------------------------------------------------
// Parallel node
//------------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(Parallel);

Parallel::Parallel()
: mReturnPolicy(REQUIRE_ALL)
{
}

ImplementEnumType( ParallelReturnPolicy,
"@brief The policy to use when determining the return status of a parallel node.\n\n"
"@ingroup AI\n\n")
{ Parallel::REQUIRE_ALL, "REQUIRE_ALL", "Will return success if all children succeed.\n"
"Will terminate and return failure if one child fails \n"},
{ Parallel::REQUIRE_NONE, "REQUIRE_NONE", "Will return success even if all children fail.\n" },
{ Parallel::REQUIRE_ONE, "REQUIRE_ONE", "Will terminate and return success when one child succeeds.\n" },
EndImplementEnumType;

void Parallel::initPersistFields()
{
addGroup( "Behavior" );

addField( "returnPolicy", TYPEID< Parallel::ParallelPolicy >(), Offset(mReturnPolicy, Parallel),
"@brief The policy to use when deciding the return status for the parallel sequence.");

endGroup( "Behavior" );

Parent::initPersistFields();
}

Task *Parallel::createTask(SimObject &owner, BehaviorTreeRunner &runner)
{
return new ParallelTask(*this, owner, runner);
}

//------------------------------------------------------------------------------
// Parallel Task
//------------------------------------------------------------------------------
ParallelTask::ParallelTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner)
: Parent(node, owner, runner),
mHasSuccess(false),
mHasFailure(false)
{
}

void ParallelTask::onInitialize()
{
Parent::onInitialize();

mHasSuccess = mHasFailure = false;

if(mBranches.empty())
{
for (VectorPtr<Task*>::iterator i = mChildren.begin(); i != mChildren.end(); ++i)
{
mBranches.push_back(BehaviorTreeBranch(*i));
}
}
else
{
for (Vector<BehaviorTreeBranch>::iterator it = mBranches.begin(); it != mBranches.end(); ++it)
{
it->reset();
}
}
}

Task* ParallelTask::update()
{
bool hasRunning = false, hasSuspended = false, hasResume = false;
for (Vector<BehaviorTreeBranch>::iterator it = mBranches.begin(); it != mBranches.end(); ++it)
{
Status s = it->getStatus();
if(s == INVALID || s == RUNNING || s == RESUME)
{
s = it->update();

switch(it->getStatus())
{
case SUCCESS:
mHasSuccess = true;
break;
case FAILURE:
mHasFailure = true;
break;
case RUNNING:
hasRunning = true;
break;
case SUSPENDED:
hasSuspended = true;
break;
case RESUME:
hasResume = true;
break;
}
}
}

switch(static_cast<Parallel *>(mNodeRep)->getReturnPolicy())
{
// REQUIRE_NONE
// returns SUCCESS when all children have finished irrespective of their return status.
case Parallel::REQUIRE_NONE:
mStatus = hasResume ? RESUME : ( hasRunning ? RUNNING : ( hasSuspended ? SUSPENDED : SUCCESS ) );
break;

// REQUIRE_ONE
// terminates and returns SUCCESS when any of its children succeed
// returns FAILURE if no children succeed
case Parallel::REQUIRE_ONE:
mStatus = mHasSuccess ? SUCCESS : ( hasResume ? RESUME : ( hasRunning ? RUNNING : ( hasSuspended ? SUSPENDED : FAILURE ) ) );
break;

// REQUIRE_ALL
// returns SUCCESS if all of its children succeed.
// terminates and returns failure if any of its children fail
case Parallel::REQUIRE_ALL:
mStatus = mHasFailure ? FAILURE : ( hasResume ? RESUME : ( hasRunning ? RUNNING : ( hasSuspended ? SUSPENDED : SUCCESS ) ) );
break;
}

mIsComplete = (mStatus != RUNNING && mStatus != SUSPENDED && mStatus != RESUME);

return NULL;
}


Status ParallelTask::getStatus()
{
if(mStatus == SUSPENDED)
{
// need to check if the parallel is still suspended.
// A parallel will only report SUSPENDED when all of its children are suspended
for(Vector<BehaviorTreeBranch>::iterator it = mBranches.begin(); it != mBranches.end(); ++it)
{
switch(it->getStatus())
{
case RUNNING:
return RUNNING;
case RESUME:
return RESUME;
}
}
}
return mStatus;
}
@@ -0,0 +1,97 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_PARALLEL_H_
#define _BB_PARALLEL_H_

#ifndef _BB_CORE_H_
#include "BadBehavior/core/Composite.h"
#endif

#ifndef _BB_BRANCH_H_
#include "BadBehavior/core/Branch.h"
#endif

namespace BadBehavior
{
//---------------------------------------------------------------------------
// Parallel sequence node
// Runs all of its children irrespective of their return status
// The final return status depends on the chosen policy
// (not a true parallel, as branches are actually evaluated sequentially)
//---------------------------------------------------------------------------
class Parallel: public CompositeNode
{
typedef CompositeNode Parent;

public:
// parallel return policies
enum ParallelPolicy
{
REQUIRE_NONE,
REQUIRE_ONE,
REQUIRE_ALL,
};

protected:
ParallelPolicy mReturnPolicy;

public:
Parallel();

virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner);

static void initPersistFields();

ParallelPolicy getReturnPolicy() const { return mReturnPolicy; }

DECLARE_CONOBJECT(Parallel);
};

//---------------------------------------------------------------------------
// Parallel Task
//---------------------------------------------------------------------------
class ParallelTask: public CompositeTask
{
typedef CompositeTask Parent;

protected:
Vector<BehaviorTreeBranch> mBranches;

bool mHasSuccess, mHasFailure;

virtual void onInitialize();
virtual Task* update();

public:
ParallelTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner);

virtual Status getStatus();
};

} // namespace BadBehavior

// make the return policy enum accessible from script
typedef BadBehavior::Parallel::ParallelPolicy ParallelReturnPolicy;
DefineEnumType( ParallelReturnPolicy );

#endif
@@ -0,0 +1,68 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "RandomSelector.h"

#include "math/mMathFn.h"

using namespace BadBehavior;

//------------------------------------------------------------------------------
// Random selector node
//------------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(RandomSelector);

Task *RandomSelector::createTask(SimObject &owner, BehaviorTreeRunner &runner)
{
return new RandomSelectorTask(*this, owner, runner);
}

//------------------------------------------------------------------------------
// Random selector task
//------------------------------------------------------------------------------
RandomSelectorTask::RandomSelectorTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner)
: Parent(node, owner, runner)
{
}

void RandomSelectorTask::onInitialize()
{
Parent::onInitialize();

// randomize the order of our child tasks
VectorPtr<Task *> randomChildren;

while(mChildren.size() > 0)
{
U32 index = mRandI(0, mChildren.size() - 1);
Task* child = mChildren[index];
randomChildren.push_back(child);
mChildren.erase_fast(index);
}

mChildren = randomChildren;

// normal init
mCurrentChild = mChildren.begin();
if(mCurrentChild != mChildren.end())
(*mCurrentChild)->reset();
}
@@ -0,0 +1,62 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_RANDOMSELECTOR_H_
#define _BB_RANDOMSELECTOR_H_

#ifndef _BB_SELECTOR_H_
#include "Selector.h"
#endif

namespace BadBehavior
{
//---------------------------------------------------------------------------
// Random selector node
// selects its children in a random order until one of them succeeds
//---------------------------------------------------------------------------
class RandomSelector : public Selector
{
typedef Selector Parent;

public:
virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner);

DECLARE_CONOBJECT(RandomSelector);
};

//---------------------------------------------------------------------------
// Random selector task
//---------------------------------------------------------------------------
class RandomSelectorTask : public SelectorTask
{
typedef SelectorTask Parent;

protected:
virtual void onInitialize();

public:
RandomSelectorTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner);
};

} // namespace BadBehavior

#endif
@@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "Selector.h"

using namespace BadBehavior;

//------------------------------------------------------------------------------
// Selector node
//------------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(Selector);

Task *Selector::createTask(SimObject &owner, BehaviorTreeRunner &runner)
{
return new SelectorTask(*this, owner, runner);
}

//------------------------------------------------------------------------------
// Selector task
//------------------------------------------------------------------------------
SelectorTask::SelectorTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner)
: Parent(node, owner, runner)
{
}

void SelectorTask::onChildComplete(Status s)
{
mStatus = s;

// if child failed, move on to the next child
if(mStatus == FAILURE)
++mCurrentChild;
else
mIsComplete = true;
}
@@ -0,0 +1,60 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_SELECTOR_H_
#define _BBSELECTOR_H_

#ifndef _BB_CORE_H_
#include "BadBehavior/core/Composite.h"
#endif

namespace BadBehavior
{
//---------------------------------------------------------------------------
// Selector Node
//---------------------------------------------------------------------------
class Selector : public CompositeNode
{
typedef CompositeNode Parent;

public:
virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner);

DECLARE_CONOBJECT(Selector);
};

//---------------------------------------------------------------------------
// Selector Task
//---------------------------------------------------------------------------
class SelectorTask : public CompositeTask
{
typedef CompositeTask Parent;

public:
SelectorTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner);

virtual void onChildComplete(Status);
};

} // namespace BadBehavior

#endif
@@ -0,0 +1,55 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "Sequence.h"

using namespace BadBehavior;

//------------------------------------------------------------------------------
// Sequence node
//------------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(Sequence);

Task *Sequence::createTask(SimObject &owner, BehaviorTreeRunner &runner)
{
return new SequenceTask(*this, owner, runner);
}

//------------------------------------------------------------------------------
// Sequence Task
//------------------------------------------------------------------------------
SequenceTask::SequenceTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner)
: Parent(node, owner, runner)
{
}

void SequenceTask::onChildComplete(Status s)
{
mStatus = s;

// if child succeeded, move on to the next child
if(mStatus == SUCCESS)
++mCurrentChild;
else
mIsComplete = true;
}

@@ -0,0 +1,60 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_SEQUENCE_H_
#define _BB_SEQUENCE_H_

#ifndef _BB_CORE_H_
#include "BadBehavior/core/Composite.h"
#endif

namespace BadBehavior
{
//---------------------------------------------------------------------------
// Sequence Node
//---------------------------------------------------------------------------
class Sequence : public CompositeNode
{
typedef CompositeNode Parent;

public:
virtual Task* createTask(SimObject &owner, BehaviorTreeRunner &runner);

DECLARE_CONOBJECT(Sequence);
};

//---------------------------------------------------------------------------
// Sequence Task
//---------------------------------------------------------------------------
class SequenceTask : public CompositeTask
{
typedef CompositeTask Parent;

public:
SequenceTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner);

virtual void onChildComplete(Status);
};

} // namespace BadBehavior

#endif
@@ -0,0 +1,66 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "Branch.h"
#include "Stepper.h"

using namespace BadBehavior;

BehaviorTreeBranch::BehaviorTreeBranch()
: mRootTask(NULL)
{
}

BehaviorTreeBranch::BehaviorTreeBranch(Task *root)
: mRootTask(root),
mStatus(INVALID)
{
}

Status BehaviorTreeBranch::getStatus()
{
if(!mTasks.empty())
return mTasks.back()->getStatus();

return mStatus;
}

Status BehaviorTreeBranch::update()
{
if(mRootTask)
{
if(mTasks.empty())
{
mRootTask->setup();
mTasks.push_back(mRootTask);
}
}
mStatus = BehaviorTreeStepper::stepThrough(mTasks);
return mStatus;
}

void BehaviorTreeBranch::reset()
{
mStatus = INVALID;
mRootTask->reset();
mTasks.clear();
}
@@ -0,0 +1,53 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_BRANCH_H_
#define _BB_BRANCH_H_

#ifndef _BB_CORE_H_
#include "Core.h"
#endif

namespace BadBehavior
{
// The branch class handles a single execution path in the tree
// Typically used for the tree root and for handling suspension and concurrency
class BehaviorTreeBranch
{
private:
Status mStatus;
Task *mRootTask;
VectorPtr<Task *> mTasks;

public:
BehaviorTreeBranch();
BehaviorTreeBranch(Task *root);

Status getStatus();
Status update();
void reset();
};


} // namespace BadBehavior

#endif
@@ -0,0 +1,110 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "Composite.h"

using namespace BadBehavior;

//------------------------------------------------------------------------------
// Base composite node
// override addObject to only allow behavior tree nodes to be added
//------------------------------------------------------------------------------
void CompositeNode::addObject(SimObject *object)
{
if(dynamic_cast<Node*>(object))
Parent::addObject(object);
}

bool CompositeNode::acceptsAsChild( SimObject *object ) const
{
return (dynamic_cast<Node*>(object));
}

//------------------------------------------------------------------------------
// Base composite task
//------------------------------------------------------------------------------
CompositeTask::CompositeTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner)
: Parent(node, owner, runner)
{
}

CompositeTask::~CompositeTask()
{
while(mChildren.size())
{
Task *child = mChildren.back();
mChildren.pop_back();
if(child)
delete child;
}
}

void CompositeTask::onInitialize()
{
if(mChildren.empty())
{
CompositeNode *node = static_cast<CompositeNode *>(mNodeRep);
for(SimSet::iterator i = node->begin(); i != node->end(); ++i)
{
Task *task = static_cast<Node*>(*i)->createTask(*mOwner, *mRunner);
if(task)
{
task->setParent(this);
mChildren.push_back(task);
}
}
}

mStatus = INVALID;
mCurrentChild = mChildren.begin();
if(mCurrentChild != mChildren.end())
(*mCurrentChild)->reset();
}

void CompositeTask::onTerminate()
{
mStatus = INVALID;
}

Task* CompositeTask::update()
{
// reached the end of child list, we are complete
if (mCurrentChild == mChildren.end())
mIsComplete = true;

// task has finished
if( mIsComplete )
{
// are we latent?
if(mStatus == RUNNING || mStatus == SUSPENDED)
mIsComplete = false;

return NULL;
}

// reset the child ready for the next tick
if(mStatus != RUNNING && mStatus != SUSPENDED)
(*mCurrentChild)->reset();

// return child
return (*mCurrentChild);
}
@@ -0,0 +1,70 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_COMPOSITE_H_
#define _BB_COMPOSITE_H_

#ifndef _BB_CORE_H_
#include "Core.h"
#endif

namespace BadBehavior
{
//---------------------------------------------------------------------------
// Composite node base class - for nodes with children
//---------------------------------------------------------------------------
class CompositeNode : public Node
{
typedef Node Parent;

public:
// override addObject and acceptsAsChild to only allow behavior tree nodes to be added as children
virtual void addObject(SimObject *obj);
virtual bool acceptsAsChild( SimObject *object ) const;
};

//---------------------------------------------------------------------------
// Composite task base class
//---------------------------------------------------------------------------
class CompositeTask : public Task
{
typedef Task Parent;

protected:
// vector of pointers to child tasks
VectorPtr<Task*> mChildren;

// the current child task
VectorPtr<Task*>::iterator mCurrentChild;

virtual void onInitialize();
virtual void onTerminate();
virtual Task* update();

public:
CompositeTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner);
virtual ~CompositeTask();
};

} // namespace BadBehavior

#endif
@@ -0,0 +1,161 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "Core.h"
#include "Runner.h"

#include "console/engineAPI.h"
#include "platform/profiler.h"

bool gInBtEditor = false;

using namespace BadBehavior;

//------------------------------------------------------------------------------
// script enum for return type
//------------------------------------------------------------------------------
ImplementEnumType( BehaviorReturnType,
"@brief The return status for a behavior.\n\n"
"@ingroup AI\n\n")
// not needed script side
//{ BadBehavior::INVALID, "INVALID", "The behavior could not be evaluated.\n" },
{ BadBehavior::SUCCESS, "SUCCESS", "The behavior succeeded.\n" },
{ BadBehavior::FAILURE, "FAILURE", "The behavior failed.\n" },
{ BadBehavior::RUNNING, "RUNNING", "The behavior is still running.\n" },
// not needed script side
//{ BadBehavior::SUSPENDED, "SUSPENDED", "The behavior has been suspended.\n" },
//{ BadBehavior::RESUME, "RESUME", "The behavior is resuming from suspended.\n" }
EndImplementEnumType;


//================================LeafNode======================================

//------------------------------------------------------------------------------
// don't allow objects to be added
//------------------------------------------------------------------------------
void LeafNode::addObject(SimObject *object)
{
}

bool LeafNode::acceptsAsChild( SimObject *object ) const
{
return false;
}


//==================================Task========================================

Task::Task(Node &node, SimObject &owner, BehaviorTreeRunner &runner)
: mStatus(INVALID),
mIsComplete(false),
mNodeRep(&node),
mOwner(&owner),
mRunner(&runner),
mParent(NULL)
{
}

Task::~Task()
{
}

void Task::onInitialize()
{
}

void Task::onTerminate()
{
}

Task* Task::tick()
{
PROFILE_SCOPE(Task_Tick);

return update();
}

void Task::setup()
{
PROFILE_SCOPE(Task_setup);

if(mStatus != RUNNING && mStatus != SUSPENDED)
onInitialize();

mIsComplete = false;
}

void Task::finish()
{
if(mIsComplete)
onTerminate();
}

void Task::reset()
{
mStatus = INVALID;
}

Status Task::getStatus()
{
return mStatus;
}

void Task::setStatus(Status newStatus)
{
mStatus = newStatus;
}

void Task::setParent(Task *parent)
{
mParent = parent;
}

Task *Task::getParent()
{
return mParent;
}

void Task::onChildComplete(Status)
{
}

void Task::onResume()
{
if(mStatus == SUSPENDED)
mStatus = RESUME;

//Con::warnf("onResume %s",
// mNodeRep->getIdString());
}

DefineEngineFunction(onBehaviorTreeEditorStart, void, (),,
"@brief Notify the engine that the behavior tree editor is active")
{
gInBtEditor = true;
}


DefineEngineFunction(onBehaviorTreeEditorStop, void, (),,
"@brief Notify the engine that the behavior tree editor has finished")
{
gInBtEditor = false;
}
@@ -0,0 +1,151 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BBCORE_H_
#define _BBCORE_H_

#ifndef _SIMSET_H_
#include "console/simSet.h"
#endif
#ifndef _SIMOBJECT_H_
#include "console/simObject.h"
#endif

extern bool gInBtEditor;

namespace BadBehavior
{
//---------------------------------------------------------------------------
// return status values
//---------------------------------------------------------------------------
enum Status
{
INVALID = -1,
FAILURE,
SUCCESS,
RUNNING,
SUSPENDED,
RESUME
};

class Task;
class BehaviorTreeRunner;

//---------------------------------------------------------------------------
// node base class
// derived from SimGroup for easy editor integration
//---------------------------------------------------------------------------
class Node : public SimGroup
{
typedef SimGroup Parent;

public:
// create a runtime task for this node
virtual Task* createTask(SimObject &owner, BehaviorTreeRunner &runner) = 0;
};

//---------------------------------------------------------------------------
// Leaf node base class - for nodes without children
//---------------------------------------------------------------------------
class LeafNode : public Node
{
typedef Node Parent;

public:
virtual void addObject(SimObject *obj);
virtual bool acceptsAsChild( SimObject *object ) const;
};


//---------------------------------------------------------------------------
// base class for all behavior tree tasks
//---------------------------------------------------------------------------
class Task
{
protected:

// the current status
Status mStatus;

// has the task finished
bool mIsComplete;

// the node associated with this task
Node *mNodeRep;

// the object that owns us
SimObjectPtr<SimObject> mOwner;

// the object running us
BehaviorTreeRunner *mRunner;

// the parent of this task
Task *mParent;

// update
virtual Task* update() = 0;

// initialize
virtual void onInitialize();

// terminate
virtual void onTerminate();

public:
// tasks are instantiated with a reference to their associated node
Task(Node &node, SimObject &owner, BehaviorTreeRunner &runner);
virtual ~Task();

// status sets and gets
virtual Status getStatus();
void setStatus(Status newStatus);

// parent sets and gets
void setParent(Task *parent);
Task *getParent();

// run the task
Task* tick();

// called when child task finishes
virtual void onChildComplete(Status);

// called when a suspended task becomes active
virtual void onResume();

// prepare the task
void setup();

// finish the task
void finish();

// reset the task
void reset();
};

} // namespace BadBehavior

// make the return status enum accessible from script
typedef BadBehavior::Status BehaviorReturnType;
DefineEnumType( BehaviorReturnType );

#endif
@@ -0,0 +1,106 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "Decorator.h"

using namespace BadBehavior;

//------------------------------------------------------------------------------
// Base decorator node
// overrides for decorators to only allow 1 child
//------------------------------------------------------------------------------
void DecoratorNode::addObject(SimObject *obj)
{
if(empty())
Parent::addObject(obj);
}

bool DecoratorNode::acceptsAsChild( SimObject *object ) const
{
return (dynamic_cast<Node *>(object) && empty());
}

//------------------------------------------------------------------------------
// Base decorator task
//------------------------------------------------------------------------------
DecoratorTask::DecoratorTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner)
: Parent(node, owner, runner),
mChild(NULL)
{
}

DecoratorTask::~DecoratorTask()
{
if(mChild)
{
delete mChild;
mChild = NULL;
}
}

void DecoratorTask::onInitialize()
{
if(!mChild)
{
if(mNodeRep->size() > 0)
{
Node *childNode = static_cast<Node*>(*mNodeRep->begin());
if(childNode)
{
mChild = childNode->createTask(*mOwner, *mRunner);
if(mChild)
{
mChild->setParent(this);
mChild->reset();
}
}
}
}

mStatus = INVALID;
}

void DecoratorTask::onTerminate()
{
mStatus = INVALID;
}

Task* DecoratorTask::update()
{
// first time through, return child
if(!mIsComplete)
return mStatus != SUSPENDED ? mChild : NULL;

// child has completed, are we latent?
if(mStatus == RUNNING || mStatus == SUSPENDED)
mIsComplete = false;

// no more children
return NULL;
}

void DecoratorTask::onChildComplete(Status s)
{
// set our status to the child status and flag completed
mStatus = s;
mIsComplete = true;
}
@@ -0,0 +1,68 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_DECORATOR_H_
#define _BB_DECORATOR_H_

#ifndef _BB_CORE_H_
#include "BadBehavior/core/Core.h"
#endif

namespace BadBehavior
{
//---------------------------------------------------------------------------
// Decorator node base class
//---------------------------------------------------------------------------
class DecoratorNode : public Node
{
typedef Node Parent;

public:
// only allow 1 child node to be added
virtual void addObject(SimObject *obj);
virtual bool acceptsAsChild( SimObject *object ) const;
};

//---------------------------------------------------------------------------
// Decorator task base class
//---------------------------------------------------------------------------
class DecoratorTask : public Task
{
typedef Task Parent;

protected:
Task* mChild;

virtual Task* update();
virtual void onInitialize();
virtual void onTerminate();

public:
DecoratorTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner);
virtual ~DecoratorTask();

virtual void onChildComplete(Status s);
};

} // namespace BadBehavior

#endif
@@ -0,0 +1,278 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "Runner.h"

#include "console/engineAPI.h"
#include "platform/profiler.h"

using namespace BadBehavior;

IMPLEMENT_CONOBJECT(BehaviorTreeRunner);

BehaviorTreeRunner::BehaviorTreeRunner()
: mOwner(NULL),
mRootNode(NULL),
mRootTask(NULL),
mIsRunning(0),
mTickEvent(0),
mTickFrequency(100)
{}


BehaviorTreeRunner::~BehaviorTreeRunner()
{
delete mRootTask;

if(Sim::isEventPending(mTickEvent))
{
Sim::cancelEvent(mTickEvent);
mTickEvent = 0;
}
}


void BehaviorTreeRunner::initPersistFields()
{
addGroup( "Behavior" );

addProtectedField("rootNode", TYPEID< SimObject >(), Offset(mRootNode, BehaviorTreeRunner),
&_setRootNode, &defaultProtectedGetFn, "@brief The root node of the tree to be run.");

addProtectedField("ownerObject", TYPEID< SimObject >(), Offset(mOwner, BehaviorTreeRunner),
&_setOwner, &defaultProtectedGetFn, "@brief The object that owns the tree to be run.");

addField("frequency", TypeS32, Offset(mTickFrequency, BehaviorTreeRunner),
"@brief The frequency in ms that the tree is ticked at.");

endGroup( "Behavior" );

Parent::initPersistFields();
}

void BehaviorTreeRunner::onDeleteNotify(SimObject *object)
{
// delete ourselves nicely
// - this takes care of any events registered to this runner
if(object == mOwner.getObject())
safeDeleteObject();
}


// processTick is where the magic happens :)
void BehaviorTreeRunner::onTick()
{
PROFILE_SCOPE(BehaviorTreeRunner_processTick);

// check that we are setup to run
if(mOwner.isNull() || mRootNode.isNull())
return;

if(gInBtEditor)
{
if(mRootTask)
reset();
}
else
{
if(!mRootTask)
{
if((mRootTask = mRootNode->createTask(*mOwner, *this)) == NULL)
{
Con::errorf("BehaviorTreeTicker::processTick, no task for root node");
return;
}
}

// dispatch any signals
mSignalHandler.dispatchSignals();

// Evaluate the tree
mRootTask->setup();
mRootTask->tick();
//Con::warnf("Tree returned %s", EngineMarshallData(mRootTask->getStatus()));
mRootTask->finish();
}

// schedule the next tick
if(Sim::isEventPending(mTickEvent))
Sim::cancelEvent(mTickEvent);

mTickEvent = Sim::postEvent(this, new BehaviorTreeTickEvent(), Sim::getCurrentTime() + mTickFrequency);

mIsRunning = true;
}

void BehaviorTreeRunner::onReactivateEvent(Task *task)
{
if(task)
task->onResume();
}

bool BehaviorTreeRunner::_setRootNode( void *object, const char *index, const char *data )
{
BehaviorTreeRunner *runner = static_cast<BehaviorTreeRunner *>( object );
Node* root = NULL;
Sim::findObject( data, root );
if(root)
runner->setRootNode(root);
return false;
}

bool BehaviorTreeRunner::_setOwner( void *object, const char *index, const char *data )
{
BehaviorTreeRunner *runner = static_cast<BehaviorTreeRunner *>( object );
SimObject* owner = NULL;
Sim::findObject( data, owner );
if(owner)
runner->setOwner(owner);
return false;
}

void BehaviorTreeRunner::setOwner(SimObject *owner)
{
reset();
mOwner = owner;
deleteNotify(mOwner);
start();
}


void BehaviorTreeRunner::setRootNode(Node *root)
{
reset();
mRootNode = root;
start();
}


void BehaviorTreeRunner::stop()
{
if(Sim::isEventPending(mTickEvent))
{
Sim::cancelEvent(mTickEvent);
mTickEvent = 0;
}
mIsRunning = false;
}


void BehaviorTreeRunner::start()
{
if(Sim::isEventPending(mTickEvent))
{
Sim::cancelEvent(mTickEvent);
mTickEvent = 0;
}

mIsRunning = true;
if(mRootTask)
mRootTask->reset();

mTickEvent = Sim::postEvent(this, new BehaviorTreeTickEvent(), -1);
}


void BehaviorTreeRunner::reset()
{
//stop();
if(mRootTask)
{
delete mRootTask;
mRootTask = 0;
}
mSignalHandler.reset();
}


void BehaviorTreeRunner::clear()
{
reset();
mRootNode = 0;
}


bool BehaviorTreeRunner::isRunning()
{
return mIsRunning;
}


void BehaviorTreeRunner::subscribeToSignal(const char *signal, SignalSubscriber *subscriber)
{
mSignalHandler.registerSubscriber(signal, subscriber);
}


void BehaviorTreeRunner::unsubscribeFromSignal(const char *signal, SignalSubscriber *subscriber)
{
mSignalHandler.unregisterSubscriber(signal, subscriber);
}


void BehaviorTreeRunner::postSignal(const char *signal)
{
mSignalHandler.postSignal(signal);
}


DefineEngineMethod( BehaviorTreeRunner, stop, void, (), ,
"Halt the execution of the behavior tree.\n\n"
"@note The internal task status is retained, allowing execution to be resumed.")
{
object->stop();
}


DefineEngineMethod( BehaviorTreeRunner, start, void, (), ,
"Resume execution of the (stopped) behavior tree.")
{
object->start();
}


DefineEngineMethod( BehaviorTreeRunner, reset, void, (), ,
"Reset the behavior tree. Any internal state is lost.")
{
object->reset();
}


DefineEngineMethod( BehaviorTreeRunner, clear, void, (), ,
"Clear the behavior tree.")
{
object->clear();
}


DefineEngineMethod( BehaviorTreeRunner, isRunning, bool, (), ,
"Is the behavior tree running")
{
return object->isRunning();
}


DefineEngineMethod(BehaviorTreeRunner, postSignal, void, (const char *signal),,
"@brief Posts a signal to the behavior tree.\n\n")
{
object->postSignal( signal );
}
@@ -0,0 +1,140 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_RUNNER_H_
#define _BB_RUNNER_H_

#ifndef _BB_CORE_H_
#include "Core.h"
#endif
#ifndef _BB_SIGNAL_H_
#include "Signal.h"
#endif
#ifndef _SIMOBJECT_H_
#include "console/simObject.h"
#endif
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#ifndef _TVECTOR_H_
#include "util/tVector.h"
#endif

namespace BadBehavior
{

//---------------------------------------------------------------------------
// BehaviorTreeRunner - handles the evaluation of the tree
//---------------------------------------------------------------------------
class BehaviorTreeRunner : public SimObject
{
typedef SimObject Parent;

private:
// is this tree running?
bool mIsRunning;

// event ID of the tick event
U32 mTickEvent;

// frequency of ticks in ms
U32 mTickFrequency;

// the root node of the tree
SimObjectPtr<Node> mRootNode;

// the task associated with the root node
Task *mRootTask;

// the game object that is using this tree
SimObjectPtr<SimObject> mOwner;

// signal handler for throwing signals around
SignalHandler mSignalHandler;

// setters for the script interface
static bool _setRootNode( void *object, const char *index, const char *data );
static bool _setOwner( void *object, const char *index, const char *data );

public:
/*Ctor*/ BehaviorTreeRunner();
/*Dtor*/ ~BehaviorTreeRunner();

// public setters for the script interface
void setOwner(SimObject *owner);
void setRootNode(Node *root);

// notification if our owner is deleted
virtual void onDeleteNotify(SimObject *object);

// for script control
void stop();
void start();
void reset();
void clear();
bool isRunning();

// tick
void onTick();

// signal handling
void subscribeToSignal(const char *signal, SignalSubscriber *subscriber);
void unsubscribeFromSignal(const char *signal, SignalSubscriber *subscriber);
void postSignal(const char *signal);

// task reactivation
void onReactivateEvent(Task *task);

// script interface
static void initPersistFields();

DECLARE_CONOBJECT(BehaviorTreeRunner);
};


class BehaviorTreeTickEvent : public SimEvent
{
public:
void process( SimObject *object )
{
((BehaviorTreeRunner*)object)->onTick();
}
};

class TaskReactivateEvent : public SimEvent
{
Task *mTask;
public:
TaskReactivateEvent(Task &task)
{
mTask = &task;
}

void process( SimObject *object )
{
((BehaviorTreeRunner*)object)->onReactivateEvent(mTask);
}
};

} // namespace BadBehavior

#endif
@@ -0,0 +1,168 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "Signal.h"

#include "core/stringTable.h"

using namespace BadBehavior;

// clean up
SignalHandler::~SignalHandler()
{
clearSignalQueue();
clearSubscribers();
}

void SignalHandler::clearSignalQueue()
{
while(!mSignalQueue.empty())
{
Signal *sig = mSignalQueue.back();
mSignalQueue.pop_back();
delete sig;
sig = NULL;
}
}

void SignalHandler::clearSubscribers()
{
for( Vector<StringTableEntry>::const_iterator it = mSignals.begin(); it != mSignals.end(); ++it )
{
// Delete the subscriber list.
VectorPtr<SignalSubscriber*>* subscribers = mSubscribers.remove( *it );
if(subscribers)
delete subscribers;
}
}

void SignalHandler::reset()
{
// empty out the signal queue
clearSignalQueue();

// clear out the subscribers table
clearSubscribers();

// reset the vector of registered signals
mSignals.clear();
}

// check if the specified signal is registered
bool SignalHandler::isSignalRegistered(const char *signal)
{
StringTableEntry signalName = StringTable->insert( signal );
return mSignals.contains(signalName);
}

// register a signal
void SignalHandler::registerSignal(const char *signal)
{
// check signal has a name
if(!signal || !signal[0])
return;

// Make sure the signal has not been registered yet.
if(!isSignalRegistered( signal ) )
{
// Add to the signal list.
mSignals.push_back( StringTable->insert( signal ) );

// Create a list of subscribers for this event.
mSubscribers.insert( new VectorPtr<SignalSubscriber*>, signal );
}
}

// register a subscriber to a signal
void SignalHandler::registerSubscriber(const char *signal, SignalSubscriber *subscriber)
{
// must have a subscriber
if(!subscriber)
return;

// and an signal name
if(!signal || !signal[0])
return;

// register a new event if this one hasn't already been registered
if(!isSignalRegistered(signal))
registerSignal(signal);

// add the subscriber if it's not already registered for this signal
VectorPtr<SignalSubscriber*>* subscribers = mSubscribers.retreive( signal );

if(!subscribers->contains(subscriber))
subscribers->push_back(subscriber);
}

// unregister a subscriber
void SignalHandler::unregisterSubscriber(const char *signal, SignalSubscriber *subscriber)
{
// check if the subscriber exists and that the signal is actually registered
if(!subscriber || !isSignalRegistered(signal))
return;

// find the subscriber and remove it
VectorPtr<SignalSubscriber*>* subscribers = mSubscribers.retreive( signal );

for( VectorPtr<SignalSubscriber *>::iterator iter = subscribers->begin(); iter != subscribers->end(); ++iter )
{
if( *iter == subscriber )
{
subscribers->erase_fast( iter );
break;
}
}
}

// post a signal
void SignalHandler::postSignal(const char *signal)
{
// signal must be registered
if(!isSignalRegistered(signal))
return;

// dispatch a signal to each of the subscribers
VectorPtr<SignalSubscriber*>* subscribers = mSubscribers.retreive( signal );

for( VectorPtr<SignalSubscriber *>::iterator iter = subscribers->begin(); iter != subscribers->end(); ++iter )
{
mSignalQueue.push_back(new Signal((*iter)));
}
}


// dispatch queued signals
void SignalHandler::dispatchSignals()
{
if(mSignalQueue.empty())
return;

while(!mSignalQueue.empty())
{
Signal *sig = mSignalQueue.back();
mSignalQueue.pop_back();
sig->send();
delete sig;
sig = NULL;
}
}
@@ -0,0 +1,97 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_SIGNAL_H_
#define _BB_SIGNAL_H_

#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
#ifndef _TSIMPLEHASHTABLE_H
#include "core/tSimpleHashTable.h"
#endif

namespace BadBehavior
{
//---------------------------------------------------------------------------
// Signal subscriber interface
//---------------------------------------------------------------------------
class SignalSubscriber
{
protected:
virtual void subscribe() = 0;
virtual void unsubscribe() = 0;

public:
virtual void onSignal() = 0;
};

//---------------------------------------------------------------------------
// Signal class
//---------------------------------------------------------------------------
class Signal
{
private:
SignalSubscriber *mSubscriber;
public:
Signal(SignalSubscriber *subscriber) : mSubscriber(subscriber) {}
void send() { if(mSubscriber) mSubscriber->onSignal(); }
};

//---------------------------------------------------------------------------
// SignalHandler
// Simple handler to pass signals to tree task listeners
//---------------------------------------------------------------------------
class SignalHandler
{
private:
SimpleHashTable<VectorPtr<SignalSubscriber*> > mSubscribers;
Vector<StringTableEntry> mSignals;

VectorPtr<Signal*> mSignalQueue;

void registerSignal(const char *signal);
bool isSignalRegistered(const char *signal);
void clearSignalQueue();
void clearSubscribers();

public:
SignalHandler() {}
~SignalHandler();

// reset
void reset();

// register a scubscriber
void registerSubscriber(const char *signal, SignalSubscriber *subscriber);

// unregister a subscriber
void unregisterSubscriber(const char *signal, SignalSubscriber *subscriber);

// post an signal
void postSignal(const char *signal);

// dispatch queued signals
void dispatchSignals();
};
} // namespace BadBehavior
#endif
@@ -0,0 +1,71 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "Stepper.h"

using namespace BadBehavior;

Status BehaviorTreeStepper::stepThrough(VectorPtr<Task *> &taskVector)
{
if(taskVector.empty()) return INVALID;

if(taskVector.back()->getStatus() == SUSPENDED)
return SUSPENDED;

Status status = INVALID;

// loop through the tasks in the task list
while(!taskVector.empty())
{
// get a task
Task *currentTask = taskVector.back();

// tick the task
Task *nextTask = currentTask->tick();

// if task returned no children, it has completed
if(!nextTask)
{
// stop if it's RUNNING or SUSPENED
status = currentTask->getStatus();
if(status == RUNNING || status == SUSPENDED)
break;

// otherwise, remove it from the list
taskVector.pop_back();
if(!taskVector.empty())
// and tell its parent that it completed
taskVector.back()->onChildComplete(currentTask->getStatus());

// complete the task
currentTask->finish();
}
else
{
// add the child as a task
nextTask->setup();
taskVector.push_back(nextTask);
}
}

return status;
}
@@ -0,0 +1,43 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_STEPPER_H_
#define _BB_STEPPER_H_

#ifndef _BB_CORE_H_
#include "Core.h"
#endif

namespace BadBehavior
{
//---------------------------------------------------------------------------
// helper class for stepping through a tree
//---------------------------------------------------------------------------
class BehaviorTreeStepper
{
public:
static Status stepThrough(VectorPtr<Task *> &taskVector);
};

} // namespace BadBehavior

#endif
@@ -0,0 +1,106 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "behavior.h"

#include "console/engineAPI.h"
#include "platform/profiler.h"

using namespace BadBehavior;

//------------------------------------------------------------------------------
// script enum for precondition mode
//------------------------------------------------------------------------------
ImplementEnumType( BehaviorPreconditionType,
"@brief When should the precondition function be evaluated.\n\n"
"@ingroup AI\n\n")
{ ONCE, "ONCE", "The first time the node is executed.\n" },
{ TICK, "TICK", "Each time the node is executed.\n" },
EndImplementEnumType;

//------------------------------------------------------------------------------
// Behavior node
//------------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(Behavior);

Behavior::Behavior()
: mPreconditionMode(ONCE)
{
}

void Behavior::initPersistFields()
{
addGroup( "Behavior" );

addField( "preconditionMode", TYPEID< BadBehavior::PreconditionMode >(), Offset(mPreconditionMode, Behavior),
"@brief When to evaluate the precondition function.");

endGroup( "Behavior" );

Parent::initPersistFields();
}

Task *Behavior::createTask(SimObject &owner, BehaviorTreeRunner &runner)
{
return new BehaviorTask(*this, owner, runner);
}

//------------------------------------------------------------------------------
// ScriptedBehavior task
//------------------------------------------------------------------------------
BehaviorTask::BehaviorTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner)
: Parent(node, owner, runner)
{
}

Task* BehaviorTask::update()
{
PROFILE_SCOPE(BehaviorTask_update);

Behavior *node = static_cast<Behavior*>(mNodeRep);

// first check preconditions are valid
bool precondition = true;
if( (node->getPreconditionMode() == ONCE && mStatus == INVALID) || (node->getPreconditionMode() == TICK) )
precondition = node->precondition( mOwner );

if(precondition)
{
// run onEnter if this is the first time the node is ticked
if(mStatus == INVALID)
node->onEnter(mOwner);

// execute the main behavior and get its return value
mStatus = node->behavior(mOwner);
}
else
{
mStatus = FAILURE;
}

mIsComplete = mStatus != RUNNING && mStatus != SUSPENDED;

if(mIsComplete)
node->onExit(mOwner);

return NULL; // leaves don't have children
}
@@ -0,0 +1,87 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_BEHAVIOR_H_
#define _BB_BEHAVIOR_H_

#ifndef _BB_CORE_H_
#include "Core.h"
#endif

namespace BadBehavior
{
// specify when the precondition function should be executed
enum PreconditionMode
{
ONCE, // the first time the behavior is evaluated
TICK // every tick
};

//---------------------------------------------------------------------------
// Behavior - Base class for structured behavior leaf nodes
//---------------------------------------------------------------------------
class Behavior : public LeafNode
{
typedef LeafNode Parent;

protected:
// how often should we valuate the precondition
PreconditionMode mPreconditionMode;

public:
Behavior();

virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner);

static void initPersistFields();

const PreconditionMode &getPreconditionMode() const { return mPreconditionMode; }

virtual bool precondition( SimObject *owner ) { return true; }
virtual void onEnter( SimObject *owner ) {}
virtual void onExit( SimObject *owner ) {}
virtual Status behavior( SimObject *owner ) { return SUCCESS; }

DECLARE_CONOBJECT(Behavior);
};

//---------------------------------------------------------------------------
// Behavior task
//---------------------------------------------------------------------------
class BehaviorTask : public Task
{
typedef Task Parent;

protected:
virtual Task* update();

public:
BehaviorTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner);
};

} // namespace BadBehavior

// make the return precondition mode accessible from script
typedef BadBehavior::PreconditionMode BehaviorPreconditionType;
DefineEnumType( BehaviorPreconditionType );

#endif
@@ -0,0 +1,51 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "FailAlways.h"

using namespace BadBehavior;

//------------------------------------------------------------------------------
// FailAlways decorator node
//------------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(FailAlways);

Task *FailAlways::createTask(SimObject &owner, BehaviorTreeRunner &runner)
{
return new FailAlwaysTask(*this, owner, runner);
}

//------------------------------------------------------------------------------
// FailAlways decorator task
//------------------------------------------------------------------------------
FailAlwaysTask::FailAlwaysTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner)
: Parent(node, owner, runner)
{
}

void FailAlwaysTask::onChildComplete(Status s)
{
Parent::onChildComplete(s);
if(mStatus == SUCCESS)
mStatus = FAILURE;
}

@@ -0,0 +1,61 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_FAILALWAYS_H_
#define _BB_FAILALWAYS_H_

#ifndef _BB_DECORATOR_H_
#include "BadBehavior/core/Decorator.h"
#endif

namespace BadBehavior
{
//---------------------------------------------------------------------------
// FailAlways decorator
// Returns FAILURE if child returns SUCCESS or FAILURE. RUNNING and INVALID are unchanged
//---------------------------------------------------------------------------
class FailAlways : public DecoratorNode
{
typedef DecoratorNode Parent;

public:
virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner);

DECLARE_CONOBJECT(FailAlways);
};

//---------------------------------------------------------------------------
// FailAlways task
//---------------------------------------------------------------------------
class FailAlwaysTask : public DecoratorTask
{
typedef DecoratorTask Parent;

public:
FailAlwaysTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner);

virtual void onChildComplete(Status s);
};

} // namespace BadBehavior

#endif
@@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "Inverter.h"

using namespace BadBehavior;

//------------------------------------------------------------------------------
// Inverter decorator node
//------------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(Inverter);

Task *Inverter::createTask(SimObject &owner, BehaviorTreeRunner &runner)
{
return new InverterTask(*this, owner, runner);
}

//------------------------------------------------------------------------------
// Inverter decorator task
//------------------------------------------------------------------------------
InverterTask::InverterTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner)
: Parent(node, owner, runner)
{
}

void InverterTask::onChildComplete(Status s)
{
Parent::onChildComplete(s);

// invert SUCCESS or FAILURE, leave INVALID and RUNNING unchanged
if (mStatus == SUCCESS)
mStatus = FAILURE;
else if (mStatus == FAILURE)
mStatus = SUCCESS;
}
@@ -0,0 +1,62 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_INVERTER_H_
#define _BB_INVERTER_H_

#ifndef _BB_DECORATOR_H_
#include "BadBehavior/core/Decorator.h"
#endif

namespace BadBehavior
{
//---------------------------------------------------------------------------
// inverter decorator
// invert the return value of the child,
// SUCCESS becomes FAILURE, FAILURE becomes SUCCESS, INVALID and RUNNING are unmodified
//---------------------------------------------------------------------------
class Inverter : public DecoratorNode
{
typedef DecoratorNode Parent;

public:
virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner);

DECLARE_CONOBJECT(Inverter);
};

//---------------------------------------------------------------------------
// inverter decorator task
//---------------------------------------------------------------------------
class InverterTask : public DecoratorTask
{
typedef DecoratorTask Parent;

public:
InverterTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner);

virtual void onChildComplete(Status s);
};

} // namespace BadBehavior

#endif
@@ -0,0 +1,120 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "Loop.h"

#include "console/consoleTypes.h"

using namespace BadBehavior;

//------------------------------------------------------------------------------
// Loop decorator node
//------------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(Loop);

ImplementEnumType( LoopTerminationPolicy,
"@brief The policy to use when determining if the loop should terminate early.\n\n"
"@ingroup AI\n\n")
{ Loop::ON_FAILURE, "ON_FAILURE", "Will terminate and return FAILURE if child fails.\n" },
{ Loop::ON_SUCCESS, "ON_SUCCESS", "Will terminate and return SUCCESS if child succeeds.\n" },
EndImplementEnumType;

Loop::Loop()
: mNumLoops(0),
mTerminationPolicy(ON_FAILURE)
{
}

void Loop::initPersistFields()
{
addGroup( "Behavior" );

addProtectedField( "numLoops", TypeS32, Offset(mNumLoops, Loop), &_setNumLoops, &defaultProtectedGetFn,
"The number of times to repeat the child behavior. 0 = infinite." );

addField( "terminationPolicy", TYPEID< Loop::TerminationPolicy >(), Offset(mTerminationPolicy, Loop),
"@brief The policy to use when deciding if the loop should terminate before completion.");

endGroup( "Behavior" );

Parent::initPersistFields();
}

bool Loop::_setNumLoops(void *object, const char *index, const char *data)
{
Loop *node = static_cast<Loop *>( object );
node->mNumLoops = getMax(0, dAtoi( data ));
return false;
}

Task *Loop::createTask(SimObject &owner, BehaviorTreeRunner &runner)
{
return new LoopTask(*this, owner, runner);
}

//------------------------------------------------------------------------------
// Loop decorator task
//------------------------------------------------------------------------------
LoopTask::LoopTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner)
: Parent(node, owner, runner),
mCurrentLoop(0)
{
}

void LoopTask::onInitialize()
{
Parent::onInitialize();
mCurrentLoop = 0;
}

Task* LoopTask::update()
{
if(Parent::update())
return mChild;

if(mStatus == RUNNING || mStatus == SUSPENDED)
mIsComplete = false;

// child has terminated with SUCCESS or FAILURE
if( mIsComplete )
{
// check if we should continue looping or reset
Loop *nodeRep = static_cast<Loop *>(mNodeRep);
Loop::TerminationPolicy policy = nodeRep->getTerminationPolicy();
S32 numLoops = nodeRep->getNumLoops();

// termination policy not met?
if( ((policy == Loop::ON_FAILURE) && (mStatus != FAILURE)) ||
((policy == Loop::ON_SUCCESS) && (mStatus != SUCCESS)) )
{
// more looping to be done
if ( (++mCurrentLoop < numLoops) || (numLoops == 0) )
{
mIsComplete = false;
mStatus = RUNNING;
}
}
}

// no children to return
return NULL;
}
@@ -0,0 +1,90 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef _BB_LOOP_H_
#define _BB_LOOP_H_

#ifndef _BB_DECORATOR_H_
#include "BadBehavior/core/Decorator.h"
#endif

namespace BadBehavior
{
//---------------------------------------------------------------------------
// loop decorator
// repeats the child behaviour for n times, or until it fails
// returns RUNNING until it completes
//---------------------------------------------------------------------------
class Loop : public DecoratorNode
{
typedef DecoratorNode Parent;

public:
// loop termination policies
enum TerminationPolicy
{
ON_FAILURE,
ON_SUCCESS,
};

protected:
static bool _setNumLoops(void *object, const char *index, const char *data);
S32 mNumLoops;
TerminationPolicy mTerminationPolicy;

public:
Loop();

virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner);

static void initPersistFields();

S32 getNumLoops() const { return mNumLoops; }
TerminationPolicy getTerminationPolicy() const { return mTerminationPolicy; }

DECLARE_CONOBJECT(Loop);
};

//---------------------------------------------------------------------------
// loop task
//---------------------------------------------------------------------------
class LoopTask : public DecoratorTask
{
typedef DecoratorTask Parent;

protected:
S32 mCurrentLoop;

virtual void onInitialize();
virtual Task *update();

public:
LoopTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner);
};

} // namespace BadBehavior

// make the loop termination policy enum accessible from script
typedef BadBehavior::Loop::TerminationPolicy LoopTerminationPolicy;
DefineEnumType( LoopTerminationPolicy );

#endif
@@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 Guy Allard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "Monitor.h"

#include "console/engineAPI.h"

using namespace BadBehavior;

//------------------------------------------------------------------------------
// Monitor decorator node
//------------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(Monitor);

Task *Monitor::createTask(SimObject &owner, BehaviorTreeRunner &runner)
{
return new MonitorTask(*this, owner, runner);
}

//------------------------------------------------------------------------------
// Logger decorator task
//------------------------------------------------------------------------------
MonitorTask::MonitorTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner)
: Parent(node, owner, runner)
{
}

void MonitorTask::onChildComplete(Status s)
{
Parent::onChildComplete(s);

Con::printf("%s (%s) child returning %s", static_cast<Monitor *>(mNodeRep)->getInternalName(),
static_cast<Monitor *>(mNodeRep)->getIdString(),
EngineMarshallData< BehaviorReturnType > (mStatus));
}