From 35fb08914229d8a46230144a4ee1dd8969b4ece7 Mon Sep 17 00:00:00 2001 From: Marc Chapman Date: Sun, 12 Nov 2017 02:05:27 +0000 Subject: [PATCH 1/9] Add Source files and cmake file --- .../BadBehavior/composite/ActiveSelector.cpp | 130 ++++++++ .../BadBehavior/composite/ActiveSelector.h | 92 ++++++ .../source/BadBehavior/composite/Parallel.cpp | 173 ++++++++++ .../source/BadBehavior/composite/Parallel.h | 97 ++++++ .../BadBehavior/composite/RandomSelector.cpp | 68 ++++ .../BadBehavior/composite/RandomSelector.h | 62 ++++ .../source/BadBehavior/composite/Selector.cpp | 54 ++++ .../source/BadBehavior/composite/Selector.h | 60 ++++ .../source/BadBehavior/composite/Sequence.cpp | 55 ++++ .../source/BadBehavior/composite/Sequence.h | 60 ++++ Engine/source/BadBehavior/core/Branch.cpp | 66 ++++ Engine/source/BadBehavior/core/Branch.h | 53 +++ Engine/source/BadBehavior/core/Composite.cpp | 110 +++++++ Engine/source/BadBehavior/core/Composite.h | 70 ++++ Engine/source/BadBehavior/core/Core.cpp | 160 ++++++++++ Engine/source/BadBehavior/core/Core.h | 154 +++++++++ Engine/source/BadBehavior/core/Decorator.cpp | 106 ++++++ Engine/source/BadBehavior/core/Decorator.h | 68 ++++ Engine/source/BadBehavior/core/Runner.cpp | 279 ++++++++++++++++ Engine/source/BadBehavior/core/Runner.h | 140 ++++++++ Engine/source/BadBehavior/core/Signal.cpp | 168 ++++++++++ Engine/source/BadBehavior/core/Signal.h | 97 ++++++ Engine/source/BadBehavior/core/Stepper.cpp | 71 +++++ Engine/source/BadBehavior/core/Stepper.h | 43 +++ Engine/source/BadBehavior/core/behavior.cpp | 106 ++++++ Engine/source/BadBehavior/core/behavior.h | 87 +++++ .../BadBehavior/decorator/FailAlways.cpp | 51 +++ .../source/BadBehavior/decorator/FailAlways.h | 61 ++++ .../source/BadBehavior/decorator/Inverter.cpp | 54 ++++ .../source/BadBehavior/decorator/Inverter.h | 62 ++++ Engine/source/BadBehavior/decorator/Loop.cpp | 118 +++++++ Engine/source/BadBehavior/decorator/Loop.h | 90 ++++++ .../source/BadBehavior/decorator/Monitor.cpp | 52 +++ Engine/source/BadBehavior/decorator/Monitor.h | 61 ++++ Engine/source/BadBehavior/decorator/Root.cpp | 75 +++++ Engine/source/BadBehavior/decorator/Root.h | 73 +++++ .../BadBehavior/decorator/SucceedAlways.cpp | 51 +++ .../BadBehavior/decorator/SucceedAlways.h | 61 ++++ .../source/BadBehavior/decorator/Ticker.cpp | 134 ++++++++ Engine/source/BadBehavior/decorator/Ticker.h | 89 ++++++ Engine/source/BadBehavior/leaf/RandomWait.cpp | 124 ++++++++ Engine/source/BadBehavior/leaf/RandomWait.h | 83 +++++ Engine/source/BadBehavior/leaf/ScriptEval.cpp | 94 ++++++ Engine/source/BadBehavior/leaf/ScriptEval.h | 75 +++++ Engine/source/BadBehavior/leaf/ScriptFunc.cpp | 121 +++++++ Engine/source/BadBehavior/leaf/ScriptFunc.h | 79 +++++ .../BadBehavior/leaf/ScriptedBehavior.cpp | 83 +++++ .../BadBehavior/leaf/ScriptedBehavior.h | 56 ++++ Engine/source/BadBehavior/leaf/SubTree.cpp | 74 +++++ Engine/source/BadBehavior/leaf/SubTree.h | 56 ++++ Engine/source/BadBehavior/leaf/Wait.cpp | 113 +++++++ Engine/source/BadBehavior/leaf/Wait.h | 80 +++++ .../source/BadBehavior/leaf/WaitForSignal.cpp | 143 +++++++++ .../source/BadBehavior/leaf/WaitForSignal.h | 111 +++++++ .../leaf/compiled/followBehaviorAction.cpp | 109 +++++++ .../leaf/compiled/followBehaviorAction.h | 52 +++ .../BadBehavior/tools/BTUndoActions.cpp | 134 ++++++++ .../source/BadBehavior/tools/BTUndoActions.h | 77 +++++ .../BadBehavior/tools/guiBTViewCtrl.cpp | 301 ++++++++++++++++++ .../source/BadBehavior/tools/guiBTViewCtrl.h | 45 +++ Tools/CMake/modules/module_badBehavior.cmake | 32 ++ 61 files changed, 5703 insertions(+) create mode 100644 Engine/source/BadBehavior/composite/ActiveSelector.cpp create mode 100644 Engine/source/BadBehavior/composite/ActiveSelector.h create mode 100644 Engine/source/BadBehavior/composite/Parallel.cpp create mode 100644 Engine/source/BadBehavior/composite/Parallel.h create mode 100644 Engine/source/BadBehavior/composite/RandomSelector.cpp create mode 100644 Engine/source/BadBehavior/composite/RandomSelector.h create mode 100644 Engine/source/BadBehavior/composite/Selector.cpp create mode 100644 Engine/source/BadBehavior/composite/Selector.h create mode 100644 Engine/source/BadBehavior/composite/Sequence.cpp create mode 100644 Engine/source/BadBehavior/composite/Sequence.h create mode 100644 Engine/source/BadBehavior/core/Branch.cpp create mode 100644 Engine/source/BadBehavior/core/Branch.h create mode 100644 Engine/source/BadBehavior/core/Composite.cpp create mode 100644 Engine/source/BadBehavior/core/Composite.h create mode 100644 Engine/source/BadBehavior/core/Core.cpp create mode 100644 Engine/source/BadBehavior/core/Core.h create mode 100644 Engine/source/BadBehavior/core/Decorator.cpp create mode 100644 Engine/source/BadBehavior/core/Decorator.h create mode 100644 Engine/source/BadBehavior/core/Runner.cpp create mode 100644 Engine/source/BadBehavior/core/Runner.h create mode 100644 Engine/source/BadBehavior/core/Signal.cpp create mode 100644 Engine/source/BadBehavior/core/Signal.h create mode 100644 Engine/source/BadBehavior/core/Stepper.cpp create mode 100644 Engine/source/BadBehavior/core/Stepper.h create mode 100644 Engine/source/BadBehavior/core/behavior.cpp create mode 100644 Engine/source/BadBehavior/core/behavior.h create mode 100644 Engine/source/BadBehavior/decorator/FailAlways.cpp create mode 100644 Engine/source/BadBehavior/decorator/FailAlways.h create mode 100644 Engine/source/BadBehavior/decorator/Inverter.cpp create mode 100644 Engine/source/BadBehavior/decorator/Inverter.h create mode 100644 Engine/source/BadBehavior/decorator/Loop.cpp create mode 100644 Engine/source/BadBehavior/decorator/Loop.h create mode 100644 Engine/source/BadBehavior/decorator/Monitor.cpp create mode 100644 Engine/source/BadBehavior/decorator/Monitor.h create mode 100644 Engine/source/BadBehavior/decorator/Root.cpp create mode 100644 Engine/source/BadBehavior/decorator/Root.h create mode 100644 Engine/source/BadBehavior/decorator/SucceedAlways.cpp create mode 100644 Engine/source/BadBehavior/decorator/SucceedAlways.h create mode 100644 Engine/source/BadBehavior/decorator/Ticker.cpp create mode 100644 Engine/source/BadBehavior/decorator/Ticker.h create mode 100644 Engine/source/BadBehavior/leaf/RandomWait.cpp create mode 100644 Engine/source/BadBehavior/leaf/RandomWait.h create mode 100644 Engine/source/BadBehavior/leaf/ScriptEval.cpp create mode 100644 Engine/source/BadBehavior/leaf/ScriptEval.h create mode 100644 Engine/source/BadBehavior/leaf/ScriptFunc.cpp create mode 100644 Engine/source/BadBehavior/leaf/ScriptFunc.h create mode 100644 Engine/source/BadBehavior/leaf/ScriptedBehavior.cpp create mode 100644 Engine/source/BadBehavior/leaf/ScriptedBehavior.h create mode 100644 Engine/source/BadBehavior/leaf/SubTree.cpp create mode 100644 Engine/source/BadBehavior/leaf/SubTree.h create mode 100644 Engine/source/BadBehavior/leaf/Wait.cpp create mode 100644 Engine/source/BadBehavior/leaf/Wait.h create mode 100644 Engine/source/BadBehavior/leaf/WaitForSignal.cpp create mode 100644 Engine/source/BadBehavior/leaf/WaitForSignal.h create mode 100644 Engine/source/BadBehavior/leaf/compiled/followBehaviorAction.cpp create mode 100644 Engine/source/BadBehavior/leaf/compiled/followBehaviorAction.h create mode 100644 Engine/source/BadBehavior/tools/BTUndoActions.cpp create mode 100644 Engine/source/BadBehavior/tools/BTUndoActions.h create mode 100644 Engine/source/BadBehavior/tools/guiBTViewCtrl.cpp create mode 100644 Engine/source/BadBehavior/tools/guiBTViewCtrl.h create mode 100644 Tools/CMake/modules/module_badBehavior.cmake diff --git a/Engine/source/BadBehavior/composite/ActiveSelector.cpp b/Engine/source/BadBehavior/composite/ActiveSelector.cpp new file mode 100644 index 0000000000..05d3d855ff --- /dev/null +++ b/Engine/source/BadBehavior/composite/ActiveSelector.cpp @@ -0,0 +1,130 @@ +//----------------------------------------------------------------------------- +// 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 "ActiveSelector.h" + +using namespace BadBehavior; + +//------------------------------------------------------------------------------ +// Active selector node +//------------------------------------------------------------------------------ +IMPLEMENT_CONOBJECT(ActiveSelector); + +ActiveSelector::ActiveSelector() + : mRecheckFrequency(0) +{ +} + +Task *ActiveSelector::createTask(SimObject &owner, BehaviorTreeRunner &runner) +{ + return new ActiveSelectorTask(*this, owner, runner); +} + +void ActiveSelector::initPersistFields() +{ + addGroup( "Behavior" ); + + addField( "recheckFrequency", TypeS32, Offset(mRecheckFrequency, ActiveSelector), + "@brief The minimum time period in milliseconds to wait between re-evaluations of higher priority branches."); + + endGroup( "Behavior" ); + + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ +// Active selector task +//------------------------------------------------------------------------------ +ActiveSelectorTask::ActiveSelectorTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner) + : Parent(node, owner, runner), + mRecheckTime(0) +{ +} + +void ActiveSelectorTask::onInitialize() +{ + Parent::onInitialize(); + + if(mBranches.empty()) + { + for (VectorPtr::iterator i = mChildren.begin(); i != mChildren.end(); ++i) + { + mBranches.push_back(BehaviorTreeBranch(*i)); + } + } + + mCurrentBranch = mBranches.begin(); + mRunningBranch = mBranches.end(); + mRecheckTime = 0; +} + + +Task* ActiveSelectorTask::update() +{ + // empty node, bail + if(mBranches.empty()) + { + mStatus = INVALID; + return NULL; + } + + // is it time to re-check higher priority branches? + if(Sim::getCurrentTime() >= mRecheckTime) + { + // pick highest priority branch + mCurrentBranch = mBranches.begin(); + + // determine the next recheck time + mRecheckTime = Sim::getCurrentTime() + static_cast(mNodeRep)->getRecheckFrequency(); + } + + // run a branch, if it fails move on to the next + for(mCurrentBranch; mCurrentBranch != mBranches.end(); ++mCurrentBranch) + { + // reset the branch if it's not the current running branch + if(mCurrentBranch != mRunningBranch) + mCurrentBranch->reset(); + + mStatus = mCurrentBranch->update(); + + if(mStatus == FAILURE) // move on to next + continue; + + if(mStatus == RUNNING || mStatus == SUSPENDED) // track the current running branch + mRunningBranch = mCurrentBranch; + + break; + } + + if( (mStatus != RUNNING && mStatus != SUSPENDED) || mCurrentBranch == mBranches.end() ) + mIsComplete = true; + + return NULL; +} + +Status ActiveSelectorTask::getStatus() +{ + if(mStatus == SUSPENDED && mCurrentBranch != mBranches.end()) + return mCurrentBranch->getStatus(); // suspended branch may have resumed + + return mStatus; +} diff --git a/Engine/source/BadBehavior/composite/ActiveSelector.h b/Engine/source/BadBehavior/composite/ActiveSelector.h new file mode 100644 index 0000000000..939979f38c --- /dev/null +++ b/Engine/source/BadBehavior/composite/ActiveSelector.h @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// 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_ACTIVESELECTOR_H_ +#define _BB_ACTIVESELECTOR_H_ + +#ifndef _BB_CORE_H_ +#include "BadBehavior/core/Composite.h" +#endif +#ifndef _BB_BRANCH_H_ +#include "BadBehavior/core/Branch.h" +#endif + +//============================================================================== +// Active Selector +// Re-evaluates its children from the beginning each tick. Lower priority +// children which previously returned RUNNING are resumed if re-selected +// +// ***** TODO - This runs OK, but may need a bit more work +// -- abort previously running branches? +// +//============================================================================== + +namespace BadBehavior +{ + //--------------------------------------------------------------------------- + // Active selector Node + //--------------------------------------------------------------------------- + class ActiveSelector : public CompositeNode + { + typedef CompositeNode Parent; + + protected: + U32 mRecheckFrequency; + + public: + ActiveSelector(); + + virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner); + + static void initPersistFields(); + + U32 getRecheckFrequency() const { return mRecheckFrequency; } + + DECLARE_CONOBJECT(ActiveSelector); + }; + + //--------------------------------------------------------------------------- + // Active selector Task + //--------------------------------------------------------------------------- + class ActiveSelectorTask : public CompositeTask + { + typedef CompositeTask Parent; + + protected: + Vector::iterator mRunningBranch; + Vector::iterator mCurrentBranch; + Vector mBranches; + + U32 mRecheckTime; + + virtual void onInitialize(); + virtual Task* update(); + + public: + ActiveSelectorTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner); + + Status getStatus(); +}; + +} // namespace BadBehavior + +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/composite/Parallel.cpp b/Engine/source/BadBehavior/composite/Parallel.cpp new file mode 100644 index 0000000000..85eecff172 --- /dev/null +++ b/Engine/source/BadBehavior/composite/Parallel.cpp @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// 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" + +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::iterator i = mChildren.begin(); i != mChildren.end(); ++i) + { + mBranches.push_back(BehaviorTreeBranch(*i)); + } + } + else + { + for (Vector::iterator it = mBranches.begin(); it != mBranches.end(); ++it) + { + it->reset(); + } + } +} + +Task* ParallelTask::update() +{ + bool hasRunning = false, hasSuspended = false, hasResume = false; + for (Vector::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(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::iterator it = mBranches.begin(); it != mBranches.end(); ++it) + { + switch(it->getStatus()) + { + case RUNNING: + return RUNNING; + case RESUME: + return RESUME; + } + } + } + return mStatus; +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/composite/Parallel.h b/Engine/source/BadBehavior/composite/Parallel.h new file mode 100644 index 0000000000..91467b44b4 --- /dev/null +++ b/Engine/source/BadBehavior/composite/Parallel.h @@ -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 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 \ No newline at end of file diff --git a/Engine/source/BadBehavior/composite/RandomSelector.cpp b/Engine/source/BadBehavior/composite/RandomSelector.cpp new file mode 100644 index 0000000000..03e0225264 --- /dev/null +++ b/Engine/source/BadBehavior/composite/RandomSelector.cpp @@ -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 "math/mMathFn.h" + +#include "RandomSelector.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 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(); +} diff --git a/Engine/source/BadBehavior/composite/RandomSelector.h b/Engine/source/BadBehavior/composite/RandomSelector.h new file mode 100644 index 0000000000..ed24177319 --- /dev/null +++ b/Engine/source/BadBehavior/composite/RandomSelector.h @@ -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 \ No newline at end of file diff --git a/Engine/source/BadBehavior/composite/Selector.cpp b/Engine/source/BadBehavior/composite/Selector.cpp new file mode 100644 index 0000000000..b62828b5cd --- /dev/null +++ b/Engine/source/BadBehavior/composite/Selector.cpp @@ -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; +} diff --git a/Engine/source/BadBehavior/composite/Selector.h b/Engine/source/BadBehavior/composite/Selector.h new file mode 100644 index 0000000000..1a2386c331 --- /dev/null +++ b/Engine/source/BadBehavior/composite/Selector.h @@ -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 \ No newline at end of file diff --git a/Engine/source/BadBehavior/composite/Sequence.cpp b/Engine/source/BadBehavior/composite/Sequence.cpp new file mode 100644 index 0000000000..27b87f8bd0 --- /dev/null +++ b/Engine/source/BadBehavior/composite/Sequence.cpp @@ -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; +} + diff --git a/Engine/source/BadBehavior/composite/Sequence.h b/Engine/source/BadBehavior/composite/Sequence.h new file mode 100644 index 0000000000..49f07b5d15 --- /dev/null +++ b/Engine/source/BadBehavior/composite/Sequence.h @@ -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 \ No newline at end of file diff --git a/Engine/source/BadBehavior/core/Branch.cpp b/Engine/source/BadBehavior/core/Branch.cpp new file mode 100644 index 0000000000..69e6701144 --- /dev/null +++ b/Engine/source/BadBehavior/core/Branch.cpp @@ -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 "Stepper.h" +#include "Branch.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(); +} diff --git a/Engine/source/BadBehavior/core/Branch.h b/Engine/source/BadBehavior/core/Branch.h new file mode 100644 index 0000000000..4198b524ea --- /dev/null +++ b/Engine/source/BadBehavior/core/Branch.h @@ -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 mTasks; + + public: + BehaviorTreeBranch(); + BehaviorTreeBranch(Task *root); + + Status getStatus(); + Status update(); + void reset(); + }; + + +} // namespace BadBehavior + +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/core/Composite.cpp b/Engine/source/BadBehavior/core/Composite.cpp new file mode 100644 index 0000000000..6f3b4d9375 --- /dev/null +++ b/Engine/source/BadBehavior/core/Composite.cpp @@ -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(object)) + Parent::addObject(object); +} + +bool CompositeNode::acceptsAsChild( SimObject *object ) const +{ + return (dynamic_cast(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(mNodeRep); + for(SimSet::iterator i = node->begin(); i != node->end(); ++i) + { + Task *task = static_cast(*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); +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/core/Composite.h b/Engine/source/BadBehavior/core/Composite.h new file mode 100644 index 0000000000..47bc236d3b --- /dev/null +++ b/Engine/source/BadBehavior/core/Composite.h @@ -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 mChildren; + + // the current child task + VectorPtr::iterator mCurrentChild; + + virtual void onInitialize(); + virtual void onTerminate(); + virtual Task* update(); + + public: + CompositeTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner); + virtual ~CompositeTask(); + }; + +} // namespace BadBehavior + +#endif diff --git a/Engine/source/BadBehavior/core/Core.cpp b/Engine/source/BadBehavior/core/Core.cpp new file mode 100644 index 0000000000..5991077ab9 --- /dev/null +++ b/Engine/source/BadBehavior/core/Core.cpp @@ -0,0 +1,160 @@ +//----------------------------------------------------------------------------- +// 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 "console/engineAPI.h" +#include "platform/profiler.h" +#include "Core.h" +#include "Runner.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; +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/core/Core.h b/Engine/source/BadBehavior/core/Core.h new file mode 100644 index 0000000000..a0c297e6f5 --- /dev/null +++ b/Engine/source/BadBehavior/core/Core.h @@ -0,0 +1,154 @@ +//----------------------------------------------------------------------------- +// 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 _ENGINEAPI_H_ +#include "console\engineAPI.h" +#endif +#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 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 \ No newline at end of file diff --git a/Engine/source/BadBehavior/core/Decorator.cpp b/Engine/source/BadBehavior/core/Decorator.cpp new file mode 100644 index 0000000000..146e72ffd8 --- /dev/null +++ b/Engine/source/BadBehavior/core/Decorator.cpp @@ -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(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(*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; +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/core/Decorator.h b/Engine/source/BadBehavior/core/Decorator.h new file mode 100644 index 0000000000..8aa4c70364 --- /dev/null +++ b/Engine/source/BadBehavior/core/Decorator.h @@ -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 diff --git a/Engine/source/BadBehavior/core/Runner.cpp b/Engine/source/BadBehavior/core/Runner.cpp new file mode 100644 index 0000000000..d024929042 --- /dev/null +++ b/Engine/source/BadBehavior/core/Runner.cpp @@ -0,0 +1,279 @@ +//----------------------------------------------------------------------------- +// 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 "console/engineAPI.h" +#include "platform/profiler.h" + +#include "Runner.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( 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( 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 ); +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/core/Runner.h b/Engine/source/BadBehavior/core/Runner.h new file mode 100644 index 0000000000..ab601089bc --- /dev/null +++ b/Engine/source/BadBehavior/core/Runner.h @@ -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 mRootNode; + + // the task associated with the root node + Task *mRootTask; + + // the game object that is using this tree + SimObjectPtr 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 \ No newline at end of file diff --git a/Engine/source/BadBehavior/core/Signal.cpp b/Engine/source/BadBehavior/core/Signal.cpp new file mode 100644 index 0000000000..cd86fdce06 --- /dev/null +++ b/Engine/source/BadBehavior/core/Signal.cpp @@ -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 "core/stringTable.h" + +#include "Signal.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::const_iterator it = mSignals.begin(); it != mSignals.end(); ++it ) + { + // Delete the subscriber list. + VectorPtr* 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, 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* 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* subscribers = mSubscribers.retreive( signal ); + + for( VectorPtr::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* subscribers = mSubscribers.retreive( signal ); + + for( VectorPtr::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; + } +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/core/Signal.h b/Engine/source/BadBehavior/core/Signal.h new file mode 100644 index 0000000000..05f770303e --- /dev/null +++ b/Engine/source/BadBehavior/core/Signal.h @@ -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> mSubscribers; + Vector mSignals; + + VectorPtr 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 \ No newline at end of file diff --git a/Engine/source/BadBehavior/core/Stepper.cpp b/Engine/source/BadBehavior/core/Stepper.cpp new file mode 100644 index 0000000000..f919681bec --- /dev/null +++ b/Engine/source/BadBehavior/core/Stepper.cpp @@ -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 &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; +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/core/Stepper.h b/Engine/source/BadBehavior/core/Stepper.h new file mode 100644 index 0000000000..78d2df5beb --- /dev/null +++ b/Engine/source/BadBehavior/core/Stepper.h @@ -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 &taskVector); + }; + +} // namespace BadBehavior + +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/core/behavior.cpp b/Engine/source/BadBehavior/core/behavior.cpp new file mode 100644 index 0000000000..7bb3ad6629 --- /dev/null +++ b/Engine/source/BadBehavior/core/behavior.cpp @@ -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 "console/engineAPI.h" +#include "platform/profiler.h" + +#include "behavior.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(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 +} diff --git a/Engine/source/BadBehavior/core/behavior.h b/Engine/source/BadBehavior/core/behavior.h new file mode 100644 index 0000000000..cdc8e9482a --- /dev/null +++ b/Engine/source/BadBehavior/core/behavior.h @@ -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 \ No newline at end of file diff --git a/Engine/source/BadBehavior/decorator/FailAlways.cpp b/Engine/source/BadBehavior/decorator/FailAlways.cpp new file mode 100644 index 0000000000..49c2d08524 --- /dev/null +++ b/Engine/source/BadBehavior/decorator/FailAlways.cpp @@ -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; +} + diff --git a/Engine/source/BadBehavior/decorator/FailAlways.h b/Engine/source/BadBehavior/decorator/FailAlways.h new file mode 100644 index 0000000000..65ba3766cc --- /dev/null +++ b/Engine/source/BadBehavior/decorator/FailAlways.h @@ -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 \ No newline at end of file diff --git a/Engine/source/BadBehavior/decorator/Inverter.cpp b/Engine/source/BadBehavior/decorator/Inverter.cpp new file mode 100644 index 0000000000..0defd9ab79 --- /dev/null +++ b/Engine/source/BadBehavior/decorator/Inverter.cpp @@ -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; +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/decorator/Inverter.h b/Engine/source/BadBehavior/decorator/Inverter.h new file mode 100644 index 0000000000..0cc38537e5 --- /dev/null +++ b/Engine/source/BadBehavior/decorator/Inverter.h @@ -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 \ No newline at end of file diff --git a/Engine/source/BadBehavior/decorator/Loop.cpp b/Engine/source/BadBehavior/decorator/Loop.cpp new file mode 100644 index 0000000000..d873aab865 --- /dev/null +++ b/Engine/source/BadBehavior/decorator/Loop.cpp @@ -0,0 +1,118 @@ +//----------------------------------------------------------------------------- +// 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" + +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( 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(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; +} diff --git a/Engine/source/BadBehavior/decorator/Loop.h b/Engine/source/BadBehavior/decorator/Loop.h new file mode 100644 index 0000000000..2b5deb5be0 --- /dev/null +++ b/Engine/source/BadBehavior/decorator/Loop.h @@ -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 \ No newline at end of file diff --git a/Engine/source/BadBehavior/decorator/Monitor.cpp b/Engine/source/BadBehavior/decorator/Monitor.cpp new file mode 100644 index 0000000000..685af1e7e6 --- /dev/null +++ b/Engine/source/BadBehavior/decorator/Monitor.cpp @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// 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" + +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(mNodeRep)->getInternalName(), + static_cast(mNodeRep)->getIdString(), + EngineMarshallData< BehaviorReturnType > (mStatus)); +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/decorator/Monitor.h b/Engine/source/BadBehavior/decorator/Monitor.h new file mode 100644 index 0000000000..2fbd707ba2 --- /dev/null +++ b/Engine/source/BadBehavior/decorator/Monitor.h @@ -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_MONITOR_H_ +#define _BB_MONITOR_H_ + +#ifndef _BB_DECORATOR_H_ +#include "BadBehavior/core/Decorator.h" +#endif + +namespace BadBehavior +{ + //--------------------------------------------------------------------------- + // Monitor decorator + // outputs the return status to the console, + //--------------------------------------------------------------------------- + class Monitor : public DecoratorNode + { + typedef DecoratorNode Parent; + + public: + virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner); + + DECLARE_CONOBJECT(Monitor); + }; + + //--------------------------------------------------------------------------- + // Monitor decorator task + //--------------------------------------------------------------------------- + class MonitorTask : public DecoratorTask + { + typedef DecoratorTask Parent; + + public: + MonitorTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner); + + virtual void onChildComplete(Status s); + }; + +} // namespace BadBehavior + +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/decorator/Root.cpp b/Engine/source/BadBehavior/decorator/Root.cpp new file mode 100644 index 0000000000..42f10a5dc8 --- /dev/null +++ b/Engine/source/BadBehavior/decorator/Root.cpp @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// 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 "Root.h" + +using namespace BadBehavior; + +//------------------------------------------------------------------------------ +// Root decorator node +//------------------------------------------------------------------------------ +IMPLEMENT_CONOBJECT(Root); + +Task *Root::createTask(SimObject &owner, BehaviorTreeRunner &runner) +{ + return new RootTask(*this, owner, runner); +} + +//------------------------------------------------------------------------------ +// Root decorator task +//------------------------------------------------------------------------------ +RootTask::RootTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner) + : Parent(node, owner, runner), + mBranch(NULL) +{ +} + +RootTask::~RootTask() +{ + if(mBranch) + delete mBranch; +} + +void RootTask::onInitialize() +{ + Parent::onInitialize(); + if(!mBranch) + mBranch = new BehaviorTreeBranch(mChild); + else + mBranch->reset(); +} + +Task *RootTask::update() +{ + mStatus = mBranch->update(); + + mIsComplete = mStatus != RUNNING && mStatus != SUSPENDED; + return NULL; +} + +Status RootTask::getStatus() +{ + if(mStatus == SUSPENDED) + return mBranch->getStatus(); + + return mStatus; +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/decorator/Root.h b/Engine/source/BadBehavior/decorator/Root.h new file mode 100644 index 0000000000..33f1be5614 --- /dev/null +++ b/Engine/source/BadBehavior/decorator/Root.h @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// 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_ROOT_H_ +#define _BB_ROOT_H_ + +#ifndef _BB_BRANCH_H_ +#include "BadBehavior\core\Branch.h" +#endif +#ifndef _BB_DECORATOR_H_ +#include "BadBehavior/core/Decorator.h" +#endif + +namespace BadBehavior +{ + //--------------------------------------------------------------------------- + // Root decorator + // A placeholder node to mark the start of a tree. + // Used by the editor, bypassed during tree evaluation. + //--------------------------------------------------------------------------- + class Root : public DecoratorNode + { + typedef DecoratorNode Parent; + + public: + virtual Task* createTask(SimObject &owner, BehaviorTreeRunner &runner); + + DECLARE_CONOBJECT(Root); + }; + + //--------------------------------------------------------------------------- + // Root task + //--------------------------------------------------------------------------- + class RootTask : public DecoratorTask + { + typedef DecoratorTask Parent; + + protected: + BehaviorTreeBranch *mBranch; + + virtual Task* update(); + virtual void onInitialize(); + //virtual void onTerminate(); + + public: + RootTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner); + virtual ~RootTask(); + + virtual Status getStatus(); + }; + +} // namespace BadBehavior + +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/decorator/SucceedAlways.cpp b/Engine/source/BadBehavior/decorator/SucceedAlways.cpp new file mode 100644 index 0000000000..fb6846234c --- /dev/null +++ b/Engine/source/BadBehavior/decorator/SucceedAlways.cpp @@ -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 "SucceedAlways.h" + +using namespace BadBehavior; + +//------------------------------------------------------------------------------ +// SucceedAlways decorator node +//------------------------------------------------------------------------------ +IMPLEMENT_CONOBJECT(SucceedAlways); + +Task *SucceedAlways::createTask(SimObject &owner, BehaviorTreeRunner &runner) +{ + return new SucceedAlwaysTask(*this, owner, runner); +} + +//------------------------------------------------------------------------------ +// SucceedAlways decorator task +//------------------------------------------------------------------------------ +SucceedAlwaysTask::SucceedAlwaysTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner) + : Parent(node, owner, runner) +{ +} + +void SucceedAlwaysTask::onChildComplete(Status s) +{ + Parent::onChildComplete(s); + if(mStatus == FAILURE) + mStatus = SUCCESS; +} + diff --git a/Engine/source/BadBehavior/decorator/SucceedAlways.h b/Engine/source/BadBehavior/decorator/SucceedAlways.h new file mode 100644 index 0000000000..cdb1c02045 --- /dev/null +++ b/Engine/source/BadBehavior/decorator/SucceedAlways.h @@ -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_SUCCEEDALWAYS_H_ +#define _BB_SUCCEEDALWAYS_H_ + +#ifndef _BB_DECORATOR_H_ +#include "BadBehavior/core/Decorator.h" +#endif + +namespace BadBehavior +{ + //--------------------------------------------------------------------------- + // SucceedAlways decorator + // Returns SUCCESS if child returns SUCCESS or FAILURE. RUNNING and INVALID are unchanged + //--------------------------------------------------------------------------- + class SucceedAlways : public DecoratorNode + { + typedef DecoratorNode Parent; + + public: + virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner); + + DECLARE_CONOBJECT(SucceedAlways); + }; + + //--------------------------------------------------------------------------- + // SucceedAlways task + //--------------------------------------------------------------------------- + class SucceedAlwaysTask : public DecoratorTask + { + typedef DecoratorTask Parent; + + public: + SucceedAlwaysTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner); + + virtual void onChildComplete(Status s); + }; + +} // namespace BadBehavior + +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/decorator/Ticker.cpp b/Engine/source/BadBehavior/decorator/Ticker.cpp new file mode 100644 index 0000000000..c094f6d786 --- /dev/null +++ b/Engine/source/BadBehavior/decorator/Ticker.cpp @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// 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 "BadBehavior\core\Runner.h" +#include "Ticker.h" + +using namespace BadBehavior; + +//------------------------------------------------------------------------------ +// Ticker decorator node +// **** EXPERIMENTAL **** +//------------------------------------------------------------------------------ +IMPLEMENT_CONOBJECT(Ticker); + +Ticker::Ticker() + : mFrequencyMs(100) +{ +} + +void Ticker::initPersistFields() +{ + addGroup( "Behavior" ); + + addProtectedField( "frequencyMs", TypeS32, Offset(mFrequencyMs, Ticker), &_setFrequency, &defaultProtectedGetFn, + "The time to wait between evaluations of this nodes child." ); + + endGroup( "Behavior" ); + + Parent::initPersistFields(); +} + +bool Ticker::_setFrequency(void *object, const char *index, const char *data) +{ + Ticker *node = static_cast( object ); + node->mFrequencyMs = getMax(0, dAtoi( data )); + return false; +} + +Task *Ticker::createTask(SimObject &owner, BehaviorTreeRunner &runner) +{ + return new TickerTask(*this, owner, runner); +} + +//------------------------------------------------------------------------------ +// Ticker decorator task +//------------------------------------------------------------------------------ +TickerTask::TickerTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner) + : Parent(node, owner, runner), + mNextTimeMs(0), + mEventId(0), + mBranch(NULL) +{ +} + +TickerTask::~TickerTask() +{ + cancelEvent(); + + if(mBranch) + delete mBranch; +} + +void TickerTask::cancelEvent() +{ + if(Sim::isEventPending(mEventId)) + { + Sim::cancelEvent(mEventId); + mEventId = 0; + } +} + +void TickerTask::onInitialize() +{ + Parent::onInitialize(); + cancelEvent(); + if(!mBranch) + mBranch = new BehaviorTreeBranch(mChild); + else + mBranch->reset(); +} + +void TickerTask::onTerminate() +{ + Parent::onTerminate(); + cancelEvent(); +} + +Task* TickerTask::update() +{ + if(Sim::getCurrentTime() < mNextTimeMs) + { + if(!Sim::isEventPending(mEventId)) + mEventId = Sim::postEvent(mRunner, new TaskReactivateEvent(*this), mNextTimeMs); + + mStatus = SUSPENDED; + } + else if(mStatus != SUSPENDED) + { + mNextTimeMs = Sim::getCurrentTime() + static_cast(mNodeRep)->getFrequencyMs(); + Status s = mBranch->update(); + mStatus = s != SUSPENDED ? s : RUNNING; + } + + mIsComplete = mStatus != SUSPENDED && mStatus != RUNNING; + + return NULL; +} + +Status TickerTask::getStatus() +{ + if(mStatus != SUSPENDED && mStatus != RESUME) + return mBranch->getStatus(); + + return mStatus; +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/decorator/Ticker.h b/Engine/source/BadBehavior/decorator/Ticker.h new file mode 100644 index 0000000000..a9756d72f1 --- /dev/null +++ b/Engine/source/BadBehavior/decorator/Ticker.h @@ -0,0 +1,89 @@ +//----------------------------------------------------------------------------- +// 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_TICKER_H_ +#define _BB_TICKER_H_ + +#ifndef _BB_DECORATOR_H_ +#include "BadBehavior/core/Decorator.h" +#endif +#ifndef _BB_BRANCH_H_ +#include "BadBehavior\core\Branch.h" +#endif + +namespace BadBehavior +{ + //--------------------------------------------------------------------------- + // Ticker decorator + // Limits the frequency at which it's children are updated. + //--------------------------------------------------------------------------- + class Ticker : public DecoratorNode + { + typedef DecoratorNode Parent; + + protected: + static bool _setFrequency(void *object, const char *index, const char *data); + + // time between ticks (in ms) + S32 mFrequencyMs; + + public: + Ticker(); + + virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner); + + static void initPersistFields(); + + S32 getFrequencyMs() const { return mFrequencyMs; } + + DECLARE_CONOBJECT(Ticker); + }; + + //--------------------------------------------------------------------------- + // Ticker decorator task + //--------------------------------------------------------------------------- + class TickerTask : public DecoratorTask + { + typedef DecoratorTask Parent; + + protected: + U32 mNextTimeMs; + U32 mEventId; + + BehaviorTreeBranch *mBranch; + + virtual void onInitialize(); + virtual void onTerminate(); + virtual Task* update(); + + void cancelEvent(); + + public: + TickerTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner); + virtual ~TickerTask(); + + virtual Status getStatus(); + }; + +} // namespace BadBehavior + +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/leaf/RandomWait.cpp b/Engine/source/BadBehavior/leaf/RandomWait.cpp new file mode 100644 index 0000000000..b668387b0f --- /dev/null +++ b/Engine/source/BadBehavior/leaf/RandomWait.cpp @@ -0,0 +1,124 @@ +//----------------------------------------------------------------------------- +// 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 "math/mMathFn.h" + +#include "BadBehavior/core/Runner.h" +#include "RandomWait.h" + +using namespace BadBehavior; + +//------------------------------------------------------------------------------ +// RandomWait leaf node +//------------------------------------------------------------------------------ +IMPLEMENT_CONOBJECT(RandomWait); + +RandomWait::RandomWait() + : mWaitMinMs(0), + mWaitMaxMs(99999) +{ +} + +void RandomWait::initPersistFields() +{ + addGroup( "Behavior" ); + + addProtectedField( "waitMinMs", TypeS32, Offset(mWaitMinMs, RandomWait), &_setWaitMin, &defaultProtectedGetFn, + "The minimum time period in ms to wait before completion." ); + + addProtectedField( "waitMaxMs", TypeS32, Offset(mWaitMaxMs, RandomWait), &_setWaitMax, &defaultProtectedGetFn, + "The maximum time period in ms to wait before completion." ); + + endGroup( "Behavior" ); + + Parent::initPersistFields(); +} + +bool RandomWait::_setWaitMin(void *object, const char *index, const char *data) +{ + RandomWait *node = static_cast( object ); + node->mWaitMinMs = getMin(node->mWaitMaxMs, dAtoi( data )); + return false; +} + +bool RandomWait::_setWaitMax(void *object, const char *index, const char *data) +{ + RandomWait *node = static_cast( object ); + node->mWaitMaxMs = getMax(node->mWaitMinMs, dAtoi( data )); + return false; +} + +Task *RandomWait::createTask(SimObject &owner, BehaviorTreeRunner &runner) +{ + return new RandomWaitTask(*this, owner, runner); +} + +//------------------------------------------------------------------------------ +// RandomWait task +//------------------------------------------------------------------------------ +RandomWaitTask::RandomWaitTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner) + : Parent(node, owner, runner) +{ +} + +RandomWaitTask::~RandomWaitTask() +{ + cancelEvent(); +} + +void RandomWaitTask::cancelEvent() +{ + if(Sim::isEventPending(mEventId)) + { + Sim::cancelEvent(mEventId); + mEventId = 0; + } +} + +void RandomWaitTask::onInitialize() +{ + Parent::onInitialize(); + cancelEvent(); +} + +void RandomWaitTask::onTerminate() +{ + Parent::onTerminate(); + cancelEvent(); +} + +Task* RandomWaitTask::update() +{ + if(mStatus == RESUME) + { + mStatus = SUCCESS; + mIsComplete = true; + } + else if(mStatus == INVALID) + { + RandomWait *node = static_cast(mNodeRep); + mEventId = Sim::postEvent(mRunner, new TaskReactivateEvent(*this), Sim::getCurrentTime() + mRandI(node->getWaitMinMs(), node->getWaitMaxMs())); + mStatus = SUSPENDED; + } + + return NULL; +} diff --git a/Engine/source/BadBehavior/leaf/RandomWait.h b/Engine/source/BadBehavior/leaf/RandomWait.h new file mode 100644 index 0000000000..b83e0f4c12 --- /dev/null +++ b/Engine/source/BadBehavior/leaf/RandomWait.h @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// 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_RANDOMWAIT_H_ +#define _BB_RANDOMWAIT_H_ + +#ifndef _BB_CORE_H_ +#include "BadBehavior/core/Core.h" +#endif + +namespace BadBehavior +{ + //--------------------------------------------------------------------------- + // RandomWait leaf + // Pauses for a random period of time between delayMin and delayMax ms before completing. + //--------------------------------------------------------------------------- + class RandomWait : public LeafNode + { + typedef LeafNode Parent; + + protected: + static bool _setWaitMin(void *object, const char *index, const char *data); + static bool _setWaitMax(void *object, const char *index, const char *data); + + S32 mWaitMinMs; + S32 mWaitMaxMs; + + public: + RandomWait(); + + virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner); + + static void initPersistFields(); + + S32 getWaitMinMs() const { return mWaitMinMs; } + S32 getWaitMaxMs() const { return mWaitMaxMs; } + + DECLARE_CONOBJECT(RandomWait); + }; + + //--------------------------------------------------------------------------- + // RandomWait leaf task + //--------------------------------------------------------------------------- + class RandomWaitTask : public Task + { + typedef Task Parent; + + protected: + U32 mEventId; + + virtual void onInitialize(); + virtual void onTerminate(); + virtual Task* update(); + + void cancelEvent(); + + public: + RandomWaitTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner); + virtual ~RandomWaitTask(); + }; + +} // namespace BadBehavior + +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/leaf/ScriptEval.cpp b/Engine/source/BadBehavior/leaf/ScriptEval.cpp new file mode 100644 index 0000000000..cc155408bf --- /dev/null +++ b/Engine/source/BadBehavior/leaf/ScriptEval.cpp @@ -0,0 +1,94 @@ +//----------------------------------------------------------------------------- +// 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 "console/engineAPI.h" +#include "platform/profiler.h" + +#include "ScriptEval.h" + +using namespace BadBehavior; + +//------------------------------------------------------------------------------ +// ScriptEval node - warning, slow! +//------------------------------------------------------------------------------ +IMPLEMENT_CONOBJECT(ScriptEval); + +ScriptEval::ScriptEval() + : mDefaultReturnStatus(SUCCESS) +{ +} + +void ScriptEval::initPersistFields() +{ + addGroup( "Behavior" ); + + addField( "behaviorScript", TypeCommand, Offset(mBehaviorScript, ScriptEval), + "@brief The command to execute when the leaf is ticked. Max 255 characters." ); + + addField( "defaultReturnStatus", TYPEID< BadBehavior::Status >(), Offset(mDefaultReturnStatus, ScriptEval), + "@brief The default value for this node to return."); + + endGroup( "Behavior" ); + + Parent::initPersistFields(); +} + +Task *ScriptEval::createTask(SimObject &owner, BehaviorTreeRunner &runner) +{ + return new ScriptEvalTask(*this, owner, runner); +} + +Status ScriptEval::evaluateScript( SimObject *owner ) +{ + PROFILE_SCOPE(ScriptEval_evaluateScript); + + if(mBehaviorScript.isEmpty()) + return mDefaultReturnStatus; + + // get the result + const char *result = Con::evaluatef("%%obj=%s; %s return %s;", + owner->getIdString(), + mBehaviorScript.c_str(), + EngineMarshallData< BehaviorReturnType >(mDefaultReturnStatus)); + + if(result[0] == '1' || result[0] == '0') + // map true or false to SUCCEED or FAILURE + return static_cast(dAtoi(result)); + + // convert the returned value to our internal enum type + return EngineUnmarshallData< BehaviorReturnType >()( result ); +} + +//------------------------------------------------------------------------------ +// RunScript task +//------------------------------------------------------------------------------ +ScriptEvalTask::ScriptEvalTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner) + : Parent(node, owner, runner) +{ +} + +Task* ScriptEvalTask::update() +{ + mStatus = static_cast(mNodeRep)->evaluateScript( mOwner ); + + return NULL; // leaves don't have children +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/leaf/ScriptEval.h b/Engine/source/BadBehavior/leaf/ScriptEval.h new file mode 100644 index 0000000000..23be24b185 --- /dev/null +++ b/Engine/source/BadBehavior/leaf/ScriptEval.h @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// 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_SCRIPTEVAL_H_ +#define _BB_SCRIPTEVAL_H_ + +#ifndef _BB_CORE_H_ +#include "BadBehavior/core/Core.h" +#endif + +namespace BadBehavior +{ + //--------------------------------------------------------------------------- + // ScriptEval - evaluates a set of Torque Script commands when run + // Warning - slow, handle with care! + //--------------------------------------------------------------------------- + class ScriptEval : public LeafNode + { + typedef LeafNode Parent; + + private: + // status to return if the command does not return a value + Status mDefaultReturnStatus; + + // the torque script to evaluate + String mBehaviorScript; + + public: + ScriptEval(); + + virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner); + + static void initPersistFields(); + + // execute the script + Status evaluateScript(SimObject *owner); + + DECLARE_CONOBJECT(ScriptEval); + }; + + //--------------------------------------------------------------------------- + // ScriptEval task + //--------------------------------------------------------------------------- + class ScriptEvalTask : public Task + { + typedef Task Parent; + + protected: + virtual Task* update(); + + public: + ScriptEvalTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner); + }; + +} // namespace BadBehavior +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/leaf/ScriptFunc.cpp b/Engine/source/BadBehavior/leaf/ScriptFunc.cpp new file mode 100644 index 0000000000..ab6ebe9df1 --- /dev/null +++ b/Engine/source/BadBehavior/leaf/ScriptFunc.cpp @@ -0,0 +1,121 @@ +//----------------------------------------------------------------------------- +// 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 "console/engineAPI.h" +#include "platform/profiler.h" + +#include "ScriptFunc.h" + +using namespace BadBehavior; + +//------------------------------------------------------------------------------ +// ScriptFunc node +//------------------------------------------------------------------------------ +IMPLEMENT_CONOBJECT(ScriptFunc); + +ScriptFunc::ScriptFunc() + : mDefaultReturnStatus(SUCCESS), + mScriptFunction(StringTable->insert("")) +{ + for(U8 i = 0; i < MAX_COMMAND_ARGS; ++i) + mScriptArgs[i] = StringTable->insert(""); +} + + +void ScriptFunc::initPersistFields() +{ + addGroup( "Behavior" ); + + addField( "func", TypeCaseString, Offset(mScriptFunction, ScriptFunc), + "@brief The function to execute when the leaf is ticked." ); + + addField( "args", TypeCaseString, Offset(mScriptArgs, ScriptFunc), MAX_COMMAND_ARGS, + "@brief The arguments to be passed to the function to be executed." ); + + addField( "defaultReturnStatus", TYPEID< BadBehavior::Status >(), Offset(mDefaultReturnStatus, ScriptFunc), + "@brief The default value for this node to return."); + + endGroup( "Behavior" ); + + Parent::initPersistFields(); +} + +Task *ScriptFunc::createTask(SimObject &owner, BehaviorTreeRunner &runner) +{ + return new ScriptFuncTask(*this, owner, runner); +} + +Status ScriptFunc::evaluate( SimObject *owner ) +{ + PROFILE_SCOPE(ScriptFunc_evaluate); + + if(!owner) + return INVALID; + + if(!mScriptFunction || !mScriptFunction[0]) + return mDefaultReturnStatus; + + S32 argc = 0; + + const char *args[MAX_COMMAND_ARGS + 2]; + args[0] = mScriptFunction; + + while(argc < MAX_COMMAND_ARGS && (mScriptArgs[argc] && mScriptArgs[argc][0])) + { + args[argc + 2] = mScriptArgs[argc]; + ++argc; + } + + argc += 2; + + // get the result + const char *result = Con::execute(owner, argc, args); + + // if function didn't return a result, return our default status + if(!result || !result[0]) + return mDefaultReturnStatus; + + // map true or false to SUCCEED or FAILURE + if(result[0] == '1' || result[0] == '0') + return static_cast(dAtoi(result)); + + // convert the returned value to our internal enum type + return EngineUnmarshallData< BehaviorReturnType >()( result ); +} + +//------------------------------------------------------------------------------ +// ScriptFunc task +//------------------------------------------------------------------------------ +ScriptFuncTask::ScriptFuncTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner) + : Parent(node, owner, runner) +{ +} + +Task* ScriptFuncTask::update() +{ + mStatus = static_cast(mNodeRep)->evaluate( mOwner ); + + if(mStatus != RUNNING && mStatus != SUSPENDED) + mIsComplete = true; + + return NULL; // leaves don't have children +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/leaf/ScriptFunc.h b/Engine/source/BadBehavior/leaf/ScriptFunc.h new file mode 100644 index 0000000000..af264db96b --- /dev/null +++ b/Engine/source/BadBehavior/leaf/ScriptFunc.h @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// 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_SCRIPTFUNC_H_ +#define _BB_SCRIPTFUNC_H_ + +#ifndef _BB_CORE_H_ +#include "BadBehavior/core/Core.h" +#endif + +namespace BadBehavior +{ + static const U8 MAX_COMMAND_ARGS = 4; + + //--------------------------------------------------------------------------- + // ScriptFunc - executes a function on the owner object + //--------------------------------------------------------------------------- + class ScriptFunc : public LeafNode + { + typedef LeafNode Parent; + + protected: + // the function to call + StringTableEntry mScriptFunction; + + // the arguments for the function + StringTableEntry mScriptArgs[MAX_COMMAND_ARGS]; + + // status to return if the command does not return a value + Status mDefaultReturnStatus; + + public: + ScriptFunc(); + + virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner); + + static void initPersistFields(); + + // execute the command + Status evaluate(SimObject *owner); + + DECLARE_CONOBJECT(ScriptFunc); + }; + + //--------------------------------------------------------------------------- + // ScriptFunc task + //--------------------------------------------------------------------------- + class ScriptFuncTask : public Task + { + typedef Task Parent; + + protected: + virtual Task* update(); + + public: + ScriptFuncTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner); + }; + +} // namespace BadBehavior +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/leaf/ScriptedBehavior.cpp b/Engine/source/BadBehavior/leaf/ScriptedBehavior.cpp new file mode 100644 index 0000000000..54f7bffff2 --- /dev/null +++ b/Engine/source/BadBehavior/leaf/ScriptedBehavior.cpp @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// 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 "console/engineAPI.h" +#include "platform/profiler.h" + +#include "ScriptedBehavior.h" + +using namespace BadBehavior; + +//------------------------------------------------------------------------------ +// ScriptedBehavior node +//------------------------------------------------------------------------------ +IMPLEMENT_CONOBJECT(ScriptedBehavior); + +IMPLEMENT_CALLBACK( ScriptedBehavior, onEnter, void, ( SimObject* owner ), ( owner ), + "Called when the behavior is first run.\n" + "@param owner The object that this behavior tree belongs to." ); + +IMPLEMENT_CALLBACK( ScriptedBehavior, onExit, void, ( SimObject* owner ), ( owner ), + "Called when the behavior is complete.\n" + "@param owner The object that this behavior tree belongs to." ); + +IMPLEMENT_CALLBACK( ScriptedBehavior, precondition, bool, ( SimObject* owner ), ( owner ), + "Called prior to evaluating the behavior.\n" + "@param owner The object that this behavior tree belongs to.\n" + "A return value of false indicates that evaluation of the behavior should stop.\n" + "A return value of true indicates that evaluation of the behavior should continue."); + +IMPLEMENT_CALLBACK( ScriptedBehavior, behavior, Status, ( SimObject* owner ), ( owner ), + "Evaluate the main behavior of this node.\n" + "@param owner The object that this behavior tree belongs to.\n"); + +ScriptedBehavior::ScriptedBehavior() +{ +} + +bool ScriptedBehavior::precondition( SimObject *owner ) +{ + PROFILE_SCOPE( ScriptedBehavior_precondition); + + if(isMethod("precondition")) + return precondition_callback(owner); + + return true; +} + +void ScriptedBehavior::onEnter( SimObject *owner ) +{ + PROFILE_SCOPE( ScriptedBehavior_onEnter ); + onEnter_callback(owner); +} + +void ScriptedBehavior::onExit( SimObject *owner ) +{ + PROFILE_SCOPE( ScriptedBehavior_onExit ); + onExit_callback(owner); +} + +Status ScriptedBehavior::behavior( SimObject *owner ) +{ + PROFILE_SCOPE( ScriptedBehavior_behavior ); + return behavior_callback( owner ); +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/leaf/ScriptedBehavior.h b/Engine/source/BadBehavior/leaf/ScriptedBehavior.h new file mode 100644 index 0000000000..8dddc8f027 --- /dev/null +++ b/Engine/source/BadBehavior/leaf/ScriptedBehavior.h @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------------- +// 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_SCRIPTEDBEHAVIOR_H_ +#define _BB_SCRIPTEDBEHAVIOR_H_ + +#ifndef _BB_CORE_H_ +#include "BadBehavior/core/behavior.h" +#endif + +namespace BadBehavior +{ + //--------------------------------------------------------------------------- + // ScriptedBehavior - A structured behavior leaf node which defines a series + // Of scripted callbacks + //--------------------------------------------------------------------------- + class ScriptedBehavior : public Behavior + { + typedef Behavior Parent; + + public: + ScriptedBehavior(); + + virtual bool precondition( SimObject *owner ); + virtual void onEnter( SimObject *owner ); + virtual void onExit( SimObject *owner ); + virtual Status behavior( SimObject *owner ); + + DECLARE_CONOBJECT(ScriptedBehavior); + DECLARE_CALLBACK(void, onEnter, (SimObject *owner)); + DECLARE_CALLBACK(void, onExit, (SimObject *owner)); + DECLARE_CALLBACK(bool, precondition, (SimObject *owner)); + DECLARE_CALLBACK(Status, behavior, (SimObject *owner)); + }; +} // namespace BadBehavior + +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/leaf/SubTree.cpp b/Engine/source/BadBehavior/leaf/SubTree.cpp new file mode 100644 index 0000000000..b07ca7d2f3 --- /dev/null +++ b/Engine/source/BadBehavior/leaf/SubTree.cpp @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// 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 "SubTree.h" + +using namespace BadBehavior; + + +//------------------------------------------------------------------------------ +// SubTree node - links to another behavior tree +//------------------------------------------------------------------------------ +IMPLEMENT_CONOBJECT(SubTree); + +SubTree::SubTree() + : mSubTreeName(0) +{ +} + +void SubTree::initPersistFields() +{ + addGroup( "Behavior" ); + + addField( "subTreeName", TypeString, Offset(mSubTreeName, SubTree), + "@brief The name of the behavior tree that this node links to. Max 255 characters." ); + + endGroup( "Behavior" ); + + Parent::initPersistFields(); +} + +Task *SubTree::createTask(SimObject &owner, BehaviorTreeRunner &runner) +{ + if(!mSubTreeName || mSubTreeName[0] == 0) + { + Con::errorf("SubTree::onInitialize: no sub tree specified"); + return NULL; + } + + SimObject *subTreeNode; + + if(!Sim::findObject(mSubTreeName, subTreeNode)) + { + Con::errorf("SubTree:onInitialize: the specified sub tree does not exist"); + return NULL; + } + + Node *node = dynamic_cast(subTreeNode); + if(!node) + { + Con::errorf("SubTree::onInitialize: the specified sub tree is not a behavior tree node"); + return NULL; + } + + return node->createTask(owner, runner); +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/leaf/SubTree.h b/Engine/source/BadBehavior/leaf/SubTree.h new file mode 100644 index 0000000000..01481eb4d7 --- /dev/null +++ b/Engine/source/BadBehavior/leaf/SubTree.h @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------------- +// 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_SUBTREE_H_ +#define _BB_SUBTREE_H_ + +#ifndef _BB_CORE_H_ +#include "BadBehavior/core/Core.h" +#endif + +namespace BadBehavior +{ + //--------------------------------------------------------------------------- + // SubTree decorator + // Turns a named Behavior into a subtree of this node. + // Does not actually have a child, so is a subclass of leaf + //--------------------------------------------------------------------------- + class SubTree : public LeafNode + { + typedef LeafNode Parent; + + protected: + const char* mSubTreeName; + + public: + SubTree(); + + virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner); + + static void initPersistFields(); + + DECLARE_CONOBJECT(SubTree); + }; + +} // namespace BadBehavior + +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/leaf/Wait.cpp b/Engine/source/BadBehavior/leaf/Wait.cpp new file mode 100644 index 0000000000..0b67bd454c --- /dev/null +++ b/Engine/source/BadBehavior/leaf/Wait.cpp @@ -0,0 +1,113 @@ +//----------------------------------------------------------------------------- +// 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 "math/mMathFn.h" + +#include "BadBehavior\core\Runner.h" +#include "Wait.h" + +using namespace BadBehavior; + +//------------------------------------------------------------------------------ +// Wait leaf node +//------------------------------------------------------------------------------ +IMPLEMENT_CONOBJECT(Wait); + +Wait::Wait() + : mWaitMs(1000) +{ +} + +void Wait::initPersistFields() +{ + addGroup( "Behavior" ); + + addProtectedField( "waitMs", TypeS32, Offset(mWaitMs, Wait), &_setWait, &defaultProtectedGetFn, + "The time in ms that the node should wait before completion." ); + + endGroup( "Behavior" ); + + Parent::initPersistFields(); +} + +bool Wait::_setWait(void *object, const char *index, const char *data) +{ + Wait *node = static_cast( object ); + node->mWaitMs = getMax(0, dAtoi( data )); + return false; +} + +Task *Wait::createTask(SimObject &owner, BehaviorTreeRunner &runner) +{ + return new WaitTask(*this, owner, runner); +} + +//------------------------------------------------------------------------------ +// Wait task +//------------------------------------------------------------------------------ +WaitTask::WaitTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner) + : Parent(node, owner, runner), + mEventId(0) +{ +} + +WaitTask::~WaitTask() +{ + cancelEvent(); +} + +void WaitTask::cancelEvent() +{ + if(Sim::isEventPending(mEventId)) + { + Sim::cancelEvent(mEventId); + mEventId = 0; + } +} + +void WaitTask::onInitialize() +{ + Parent::onInitialize(); + cancelEvent(); +} + +void WaitTask::onTerminate() +{ + Parent::onTerminate(); + cancelEvent(); +} + +Task* WaitTask::update() +{ + if(mStatus == RESUME) + { + mStatus = SUCCESS; + mIsComplete = true; + } + else if(mStatus == INVALID) + { + mEventId = Sim::postEvent(mRunner, new TaskReactivateEvent(*this), Sim::getCurrentTime() + static_cast(mNodeRep)->getWaitMs()); + mStatus = SUSPENDED; + } + + return NULL; +} diff --git a/Engine/source/BadBehavior/leaf/Wait.h b/Engine/source/BadBehavior/leaf/Wait.h new file mode 100644 index 0000000000..20a49ee010 --- /dev/null +++ b/Engine/source/BadBehavior/leaf/Wait.h @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// 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_WAIT_H_ +#define _BB_WAIT_H_ + +#ifndef _BB_CORE_H_ +#include "BadBehavior/core/Core.h" +#endif + +namespace BadBehavior +{ + //--------------------------------------------------------------------------- + // Wait leaf + // Pauses for a set time period. + //--------------------------------------------------------------------------- + class Wait : public LeafNode + { + typedef LeafNode Parent; + + protected: + static bool _setWait(void *object, const char *index, const char *data); + + S32 mWaitMs; + + public: + Wait(); + + virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner); + + static void initPersistFields(); + + S32 getWaitMs() const { return mWaitMs; } + + DECLARE_CONOBJECT(Wait); + }; + + //--------------------------------------------------------------------------- + // Wait leaf task + //--------------------------------------------------------------------------- + class WaitTask : public Task + { + typedef Task Parent; + + protected: + U32 mEventId; + + virtual void onInitialize(); + virtual void onTerminate(); + virtual Task* update(); + + void cancelEvent(); + + public: + WaitTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner); + virtual ~WaitTask(); + }; + +} // namespace BadBehavior + +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/leaf/WaitForSignal.cpp b/Engine/source/BadBehavior/leaf/WaitForSignal.cpp new file mode 100644 index 0000000000..c4ed4ad7fc --- /dev/null +++ b/Engine/source/BadBehavior/leaf/WaitForSignal.cpp @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------------- +// 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 "BadBehavior\core\Runner.h" +#include "WaitForSignal.h" + +using namespace BadBehavior; + +//------------------------------------------------------------------------------ +// WaitForSignal leaf node +//------------------------------------------------------------------------------ +IMPLEMENT_CONOBJECT(WaitForSignal); + +WaitForSignal::WaitForSignal() + : mTimeoutMs(0) +{ +} + +void WaitForSignal::initPersistFields() +{ + addGroup( "Behavior" ); + + addField( "signalName", TypeRealString, Offset(mSignalName, WaitForSignal), + "The time in ms that the node should wait before completion." ); + + addProtectedField( "timeoutMs", TypeS32, Offset(mTimeoutMs, WaitForSignal), &_setTimeout, &defaultProtectedGetFn, + "The maximum time in ms that the node should wait for the signal.\n" + "A value of 0 indicates no limit"); + + endGroup( "Behavior" ); + + Parent::initPersistFields(); +} + +Task *WaitForSignal::createTask(SimObject &owner, BehaviorTreeRunner &runner) +{ + return new WaitForSignalTask(*this, owner, runner); +} + +bool WaitForSignal::_setTimeout(void *object, const char *index, const char *data) +{ + WaitForSignal *node = static_cast( object ); + node->mTimeoutMs = getMax(0, dAtoi( data )); + return false; +} + +//------------------------------------------------------------------------------ +// Wait task +//------------------------------------------------------------------------------ +WaitForSignalTask::WaitForSignalTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner) + : Parent(node, owner, runner), + mEventId(0) +{ +} + +WaitForSignalTask::~WaitForSignalTask() +{ + unsubscribe(); + cancelEvent(); +} + +void WaitForSignalTask::subscribe() +{ + mRunner->subscribeToSignal(static_cast(mNodeRep)->getSignalName().c_str(), this); +} + +void WaitForSignalTask::unsubscribe() +{ + mRunner->unsubscribeFromSignal(static_cast(mNodeRep)->getSignalName().c_str(), this); +} + +void WaitForSignalTask::onSignal() +{ + onResume(); +} + +void WaitForSignalTask::onTimeout() +{ + mStatus = FAILURE; +} + +void WaitForSignalTask::cancelEvent() +{ + if(mEventId != 0 && Sim::isEventPending(mEventId)) + { + Sim::cancelEvent(mEventId); + mEventId = 0; + } +} + +void WaitForSignalTask::onInitialize() +{ + Parent::onInitialize(); + subscribe(); + cancelEvent(); +} + +void WaitForSignalTask::onTerminate() +{ + Parent::onTerminate(); + unsubscribe(); + cancelEvent(); +} + +Task* WaitForSignalTask::update() +{ + if(mStatus == RESUME) + { + mStatus = SUCCESS; + } + else if (mStatus == INVALID) + { + mStatus = SUSPENDED; + + S32 timeout = static_cast(mNodeRep)->getTimeoutMs(); + if(timeout > 0) + mEventId = Sim::postEvent(mRunner, new WaitForSignalTimeoutEvent(*this), Sim::getCurrentTime() + timeout); + } + + if(mStatus == SUCCESS || mStatus == FAILURE) + mIsComplete = true; + + return NULL; +} diff --git a/Engine/source/BadBehavior/leaf/WaitForSignal.h b/Engine/source/BadBehavior/leaf/WaitForSignal.h new file mode 100644 index 0000000000..53e9e1591f --- /dev/null +++ b/Engine/source/BadBehavior/leaf/WaitForSignal.h @@ -0,0 +1,111 @@ +//----------------------------------------------------------------------------- +// 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_WAITFORSIGNAL_H_ +#define _BB_WAITFORSIGNAL_H_ + +#ifndef _BB_CORE_H_ +#include "BadBehavior/core/Core.h" +#endif +#ifndef _BB_SIGNAL_H_ +#include "BadBehavior/core/Signal.h" +#endif + +namespace BadBehavior +{ + //--------------------------------------------------------------------------- + // WaitForSignal leaf + // Waits until it receives the requested signal. + //--------------------------------------------------------------------------- + class WaitForSignal : public LeafNode + { + typedef LeafNode Parent; + + protected: + String mSignalName; + S32 mTimeoutMs; + + static bool _setTimeout(void *object, const char *index, const char *data); + + public: + WaitForSignal(); + + virtual Task *createTask(SimObject &owner, BehaviorTreeRunner &runner); + + static void initPersistFields(); + + const String &getSignalName() const { return mSignalName; } + S32 getTimeoutMs() const { return mTimeoutMs; } + + DECLARE_CONOBJECT(WaitForSignal); + }; + + //--------------------------------------------------------------------------- + // WaitForSignal leaf task + //--------------------------------------------------------------------------- + class WaitForSignalTask : public Task, public virtual SignalSubscriber + { + typedef Task Parent; + + protected: + U32 mEventId; + + virtual void onInitialize(); + virtual void onTerminate(); + virtual Task* update(); + + void cancelEvent(); + + // SignalSubscriber + virtual void subscribe(); + virtual void unsubscribe(); + + public: + WaitForSignalTask(Node &node, SimObject &owner, BehaviorTreeRunner &runner); + virtual ~WaitForSignalTask(); + + // SignalSubscriber + virtual void onSignal(); + + // timeout + void onTimeout(); + }; + + + class WaitForSignalTimeoutEvent : public SimEvent + { + WaitForSignalTask *mTask; + public: + WaitForSignalTimeoutEvent(WaitForSignalTask &task) + { + mTask = &task; + } + + void process( SimObject *object ) + { + mTask->onTimeout(); + } + }; + +} // namespace BadBehavior + +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/leaf/compiled/followBehaviorAction.cpp b/Engine/source/BadBehavior/leaf/compiled/followBehaviorAction.cpp new file mode 100644 index 0000000000..df1bfc28f6 --- /dev/null +++ b/Engine/source/BadBehavior/leaf/compiled/followBehaviorAction.cpp @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------------- +// 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 "FollowBehaviorAction.h" + +#include "T3D/aiPlayer.h" + +using namespace BadBehavior; + +//------------------------------------------------------------------------------ +// FollowBehavior node +//------------------------------------------------------------------------------ +IMPLEMENT_CONOBJECT(FollowBehaviorAction); + +FollowBehaviorAction::FollowBehaviorAction() +{ +} + +bool FollowBehaviorAction::precondition( SimObject *owner ) +{ + PROFILE_SCOPE( FollowBehaviorAction_precondition); + + // check that our owner is an AIPlayer + AIPlayer *aiPlayer = dynamic_cast(owner); + if(!aiPlayer) + return false; + + return true; +} + +void FollowBehaviorAction::onEnter( SimObject *owner ) +{ + //PROFILE_SCOPE( FollowBehaviorAction_onEnter ); +} + +void FollowBehaviorAction::onExit( SimObject *owner ) +{ + //PROFILE_SCOPE( FollowBehaviorAction_onExit ); +} + +Status FollowBehaviorAction::behavior( SimObject *owner ) +{ + PROFILE_SCOPE( FollowBehaviorAction_behavior ); + + // get the owning AIPlayer object + AIPlayer *aiPlayer = dynamic_cast(owner); + if(!aiPlayer) + return FAILURE; + + // get the script-specified followObject + const char *followFieldName = StringTable->insert("followObject"); + const char *followFieldValue = owner->getDataField(followFieldName, NULL); + if(!followFieldValue || !followFieldValue[0]) + return FAILURE; + + SimObject *followSimObj = Sim::findObject(dAtoi(followFieldValue)); + if(!followSimObj) + return FAILURE; + + // check that the follow object is a SceneObject + SceneObject *followObject = dynamic_cast(followSimObj); + if(!followObject) + return FAILURE; + + // get the script-specified followDistance field + const char *distanceFieldName = StringTable->insert("followDistance"); + const char *distanceFieldValue = owner->getDataField(distanceFieldName, NULL); + float followDistance = 1.f; + if(distanceFieldValue && distanceFieldValue[0]) + followDistance = dAtof(distanceFieldValue); + + // try and stay at followDistance from the followObject + Point3F targetPos = followObject->getPosition(); + Point3F followVec = aiPlayer->getPosition() - targetPos; + + // get current distance (ignore z component) + F32 curDist = Point3F(followVec.x, followVec.y, 0.f).len(); + + if(mFabs(curDist - followDistance) > aiPlayer->getMoveTolerance()) + { + followVec.normalize(); + followVec *= followDistance; + Point3F destination = targetPos + followVec; + + aiPlayer->setMoveDestination(destination, true); + return RUNNING; + } + + return SUCCESS; +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/leaf/compiled/followBehaviorAction.h b/Engine/source/BadBehavior/leaf/compiled/followBehaviorAction.h new file mode 100644 index 0000000000..9410c0324a --- /dev/null +++ b/Engine/source/BadBehavior/leaf/compiled/followBehaviorAction.h @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// 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_FOLLOWBEHAVIORACTION_H_ +#define _BB_FOLLOWBEHAVIORACTION_H_ + +#ifndef _BB_CORE_H_ +#include "BadBehavior/core/behavior.h" +#endif + +namespace BadBehavior +{ + //--------------------------------------------------------------------------- + // FollowBehaviorAction - Make an AIPlayer follow another object + // Demonstrates how to create a compiled behavior by subclassing Behavior + //--------------------------------------------------------------------------- + class FollowBehaviorAction : public Behavior + { + typedef Behavior Parent; + + public: + FollowBehaviorAction(); + + virtual bool precondition( SimObject *owner ); + virtual void onEnter( SimObject *owner ); + virtual void onExit( SimObject *owner ); + virtual Status behavior( SimObject *owner ); + + DECLARE_CONOBJECT(FollowBehaviorAction); + }; +} // namespace BadBehavior + +#endif \ No newline at end of file diff --git a/Engine/source/BadBehavior/tools/BTUndoActions.cpp b/Engine/source/BadBehavior/tools/BTUndoActions.cpp new file mode 100644 index 0000000000..b0d735b7e4 --- /dev/null +++ b/Engine/source/BadBehavior/tools/BTUndoActions.cpp @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// 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 "BTUndoActions.h" +#include "console/consoleTypes.h" +#include "console/simSet.h" + +S32 getNextObjectInGroup(SimObject *object, SimGroup *group) +{ + group->lock(); + S32 nextId = -1; + + if(object != group->last() && group->find( group->begin(), group->end(), object ) != group->end()) + { + for( SimSet::iterator i = group->begin(); i != group->end(); i++) + { + if( *i == object ) + { + nextId = (*++i)->getId(); + break; + } + } + group->unlock(); + } + + return nextId; +} + +IMPLEMENT_CONOBJECT( BTDeleteUndoAction ); + +ConsoleDocClass( BTDeleteUndoAction, + "@brief Behavior Tree Editor delete undo instance\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +BTDeleteUndoAction::BTDeleteUndoAction( const UTF8 *actionName ) + : UndoAction( actionName ) +{ +} + +BTDeleteUndoAction::~BTDeleteUndoAction() +{ +} + +void BTDeleteUndoAction::initPersistFields() +{ + Parent::initPersistFields(); +} + +void BTDeleteUndoAction::deleteObject( SimObject *object ) +{ + AssertFatal( object, "BTDeleteUndoAction::deleteObject() - Got null object!" ); + AssertFatal( object->isProperlyAdded(), + "BTDeleteUndoAction::deleteObject() - Object should be registered!" ); + + // Capture the object id. + mObject.id = object->getId(); + + // Save the state. + mObject.memento.save( object ); + + // Store the group. + SimGroup *group = object->getGroup(); + if ( group ) + { + mObject.groupId = group->getId(); + + // and the next object in the group + mObject.nextId = getNextObjectInGroup(object, group); + } + + // Now delete the object. + object->deleteObject(); +} + +ConsoleMethod( BTDeleteUndoAction, deleteObject, void, 3, 3, "( SimObject obj )") +{ + SimObject *obj = NULL; + if ( Sim::findObject( argv[2], obj ) && obj ) + object->deleteObject( obj ); +} + +void BTDeleteUndoAction::undo() +{ + // Create the object. + SimObject::setForcedId(mObject.id); // Restore the object's Id + SimObject *object = mObject.memento.restore(); + if ( !object ) + return; + + // Now restore its group. + SimGroup *group; + if ( Sim::findObject( mObject.groupId, group ) ) + { + group->addObject( object ); + + // restore its position in the group + SimObject *nextObj; + if ( Sim::findObject( mObject.nextId, nextObj ) ) + { + group->reOrder(object, nextObj); + } + } + + Con::executef( this, "onUndone" ); +} + +void BTDeleteUndoAction::redo() +{ + SimObject *object = Sim::findObject( mObject.id ); + if ( object ) + object->deleteObject(); + + Con::executef( this, "onRedone" ); +} diff --git a/Engine/source/BadBehavior/tools/BTUndoActions.h b/Engine/source/BadBehavior/tools/BTUndoActions.h new file mode 100644 index 0000000000..931e53e1ec --- /dev/null +++ b/Engine/source/BadBehavior/tools/BTUndoActions.h @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// 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 _BT_UNDO_ACTIONS_H_ +#define _BT_UNDO_ACTIONS_H_ + +#ifndef _UNDO_H_ +#include "util/undo.h" +#endif +#ifndef _CONSOLE_SIMOBJECTMEMENTO_H_ +#include "console/simObjectMemento.h" +#endif + +S32 getNextObjectInSet(SimObject *obj, SimGroup &group); + +class BTDeleteUndoAction : public UndoAction +{ + typedef UndoAction Parent; + +protected: + + struct ObjectState + { + /// The object we deleted and will restore in undo. + SimObjectId id; + + /// The captured object state. + SimObjectMemento memento; + + /// Keep track of the parent group. + SimObjectId groupId; + + /// Keep track of the position within the parent group + SimObjectId nextId; + }; + + /// The object we're deleting. + ObjectState mObject; + +public: + + DECLARE_CONOBJECT( BTDeleteUndoAction ); + static void initPersistFields(); + + BTDeleteUndoAction( const UTF8* actionName = "Delete Object" ); + virtual ~BTDeleteUndoAction(); + + /// + void deleteObject( SimObject *object ); + + // UndoAction + virtual void undo(); + virtual void redo(); +}; + + + +#endif // _BT_UNDO_ACTIONS_H_ \ No newline at end of file diff --git a/Engine/source/BadBehavior/tools/guiBTViewCtrl.cpp b/Engine/source/BadBehavior/tools/guiBTViewCtrl.cpp new file mode 100644 index 0000000000..812d3eec54 --- /dev/null +++ b/Engine/source/BadBehavior/tools/guiBTViewCtrl.cpp @@ -0,0 +1,301 @@ +//----------------------------------------------------------------------------- +// 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 "gfx/gfxDrawUtil.h" +#include "gui/worldEditor/editorIconRegistry.h" +#include "gui/controls/guiTextEditCtrl.h" + +#include "guiBTViewCtrl.h" + +using namespace BadBehavior; + +IMPLEMENT_CONOBJECT(GuiBehaviorTreeViewCtrl); + +void GuiBehaviorTreeViewCtrl::onRenderCell(Point2I offset, Point2I cell, bool, bool ) +{ + if( !mVisibleItems.size() ) + return; + + // Do some sanity checking and data retrieval. + AssertFatal(cell.y < mVisibleItems.size(), "GuiTreeViewCtrl::onRenderCell: invalid cell"); + Item * item = mVisibleItems[cell.y]; + + // If there's no object, deal with it. + if(item->isInspectorData()) + if(!item->getObject()) + return; + + RectI drawRect( offset, mCellSize ); + GFXDrawUtil *drawer = GFX->getDrawUtil(); + drawer->clearBitmapModulation(); + + FrameAllocatorMarker txtBuff; + + // Ok, we have the item. There are a few possibilities at this point: + // - We need to draw inheritance lines and a treeview-chosen icon + // OR + // - We have to draw an item-dependent icon + // - If we're mouseover, we have to highlight it. + // + // - We have to draw the text for the item + // - Taking into account various mouseover states + // - Taking into account the value (set or not) + // - If it's an inspector data, we have to do some custom rendering + // - ADDED: If it is being renamed, we also have custom rendering. + + // Ok, first draw the tab and icon. + + // Do we draw the tree lines? + if( mFlags.test(ShowTreeLines) ) + { + drawRect.point.x += ( mTabSize * item->mTabLevel ); + Item* parent = item->mParent; + for ( S32 i = item->mTabLevel; ( parent && i > 0 ); i-- ) + { + drawRect.point.x -= mTabSize; + if ( parent->mNext ) + drawer->drawBitmapSR( mProfile->mTextureObject, drawRect.point, mProfile->mBitmapArrayRects[BmpLine] ); + + parent = parent->mParent; + } + } + + // Now, the icon... + drawRect.point.x = offset.x + mTabSize * item->mTabLevel; + + // First, draw the rollover glow, if it's an inner node. + if ( item->isParent() && item->mState.test( Item::MouseOverBmp ) ) + drawer->drawBitmapSR( mProfile->mTextureObject, drawRect.point, mProfile->mBitmapArrayRects[BmpGlow] ); + + // Now, do we draw a treeview-selected item or an item dependent one? + S32 newOffset = 0; // This is stored so we can render glow, then update render pos. + + S32 bitmap = 0; + + // Ok, draw the treeview lines as appropriate. + + bool drawBitmap = true; + if ( !item->isParent() ) + { + if( mFlags.test( ShowTreeLines ) ) + { + if( ( item->mNext && item->mPrevious ) + || ( item->mNext && item->mParent && ( !_isRootLevelItem( item ) || mShowRoot ) ) ) + bitmap = BmpChild; + else if( item->mNext && ( !item->mParent || !mShowRoot ) ) + bitmap = BmpFirstChild; + else if( item->mPrevious || ( item->mParent && !_isRootLevelItem( item ) ) ) + bitmap = BmpLastChild; + else + drawBitmap = false; + } + else + drawBitmap = false; + } + else + { + bitmap = item->isExpanded() ? BmpExp : BmpCon; + + if( mFlags.test( ShowTreeLines ) ) + { + // Shift indices to show versions with tree lines. + + if ( item->mParent || item->mPrevious ) + bitmap += ( item->mNext ? 3 : 2 ); + else + bitmap += ( item->mNext ? 1 : 0 ); + } + } + + if( ( bitmap >= 0 ) && ( bitmap < mProfile->mBitmapArrayRects.size() ) ) + { + if( drawBitmap ) + drawer->drawBitmapSR( mProfile->mTextureObject, drawRect.point, mProfile->mBitmapArrayRects[bitmap] ); + newOffset = mProfile->mBitmapArrayRects[bitmap].extent.x; + } + + if(item->isInspectorData()) + { + // draw lock icon if need be + S32 icon = Lock1; + S32 icon2 = Hidden; + + if (item->getObject() && item->getObject()->isLocked()) + { + if (mIconTable[icon]) + { + //drawRect.point.x = offset.x + mTabSize * item->mTabLevel + mIconTable[icon].getWidth(); + drawRect.point.x += mIconTable[icon].getWidth(); + drawer->drawBitmap( mIconTable[icon], drawRect.point ); + } + } + + if (item->getObject() && item->getObject()->isHidden()) + { + if (mIconTable[icon2]) + { + //drawRect.point.x = offset.x + mTabSize * item->mTabLevel + mIconTable[icon].getWidth(); + drawRect.point.x += mIconTable[icon2].getWidth(); + drawer->drawBitmap( mIconTable[icon2], drawRect.point ); + } + } + + /*SimObject * pObject = item->getObject(); + SimGroup * pGroup = ( pObject == NULL ) ? NULL : dynamic_cast( pObject ); + + // If this item is a VirtualParent we can use the generic SimGroup123 icons. + // However if there is already an icon in the EditorIconRegistry for this + // exact class (not counting parent class icons) we want to use that instead. + bool hasClassIcon = gEditorIcons.hasIconNoRecurse( pObject ); + + // draw the icon associated with the item + if ( !hasClassIcon && item->mState.test(Item::VirtualParent)) + { + if ( pGroup != NULL) + { + if (item->isExpanded()) + item->mIcon = SimGroup1; + else + item->mIcon = SimGroup2; + } + else + item->mIcon = SimGroup2; + } + + if ( !hasClassIcon && item->mState.test(Item::Marked)) + { + if (item->isInspectorData()) + { + if ( pGroup != NULL ) + { + if (item->isExpanded()) + item->mIcon = SimGroup3; + else + item->mIcon = SimGroup4; + } + } + }*/ + + GFXTexHandle iconHandle; + + if ( ( item->mIcon != -1 ) && mIconTable[item->mIcon] ) + iconHandle = mIconTable[item->mIcon]; +#ifdef TORQUE_TOOLS + else + iconHandle = gEditorIcons.findIcon( item->getObject() ); +#endif + + if ( iconHandle.isValid() ) + { + S32 iconHeight = (mItemHeight - iconHandle.getHeight()) / 2; + S32 oldHeight = drawRect.point.y; + if(iconHeight > 0) + drawRect.point.y += iconHeight; + drawRect.point.x += iconHandle.getWidth(); + drawer->drawBitmap( iconHandle, drawRect.point ); + drawRect.point.y = oldHeight; + } + } + else + { + S32 icon = item->isExpanded() ? item->mScriptInfo.mExpandedImage : item->mScriptInfo.mNormalImage; + if ( icon ) + { + if (mIconTable[icon]) + { + S32 iconHeight = (mItemHeight - mIconTable[icon].getHeight()) / 2; + S32 oldHeight = drawRect.point.y; + if(iconHeight > 0) + drawRect.point.y += iconHeight; + drawRect.point.x += mIconTable[icon].getWidth(); + drawer->drawBitmap( mIconTable[icon], drawRect.point ); + drawRect.point.y = oldHeight; + } + } + } + + // Ok, update offset so we can render some text! + drawRect.point.x += newOffset; + + // Ok, now we're off to rendering the actual data for the treeview item. + + U32 bufLen = 1024; //item->mDataRenderWidth + 1; + char *displayText = (char *)txtBuff.alloc(bufLen); + displayText[bufLen-1] = 0; + item->getDisplayText(bufLen, displayText); + + // Draw the rollover/selected bitmap, if one was specified. + drawRect.extent.x = mProfile->mFont->getStrWidth( displayText ) + ( 2 * mTextOffset ); + if ( item->mState.test( Item::Selected ) && mTexSelected ) + drawer->drawBitmapStretch( mTexSelected, drawRect ); + else if ( item->mState.test( Item::MouseOverText ) && mTexRollover ) + drawer->drawBitmapStretch( mTexRollover, drawRect ); + + // Offset a bit so as to space text properly. + drawRect.point.x += mTextOffset; + + // Determine what color the font should be. + ColorI fontColor; + + fontColor = item->mState.test( Item::Selected ) ? mProfile->mFontColorSEL : + ( item->mState.test( Item::MouseOverText ) ? mProfile->mFontColorHL : mProfile->mFontColor ); + + if (item->mState.test(Item::Selected)) + { + drawer->drawRectFill(drawRect, mProfile->mFillColorSEL); + } + else if (item->mState.test(Item::MouseOverText)) + { + drawer->drawRectFill(drawRect, mProfile->mFillColorHL); + } + + if( item->mState.test(Item::MouseOverText) ) + { + fontColor = mProfile->mFontColorHL; + } + + drawer->setBitmapModulation( fontColor ); + + // Center the text horizontally. + S32 height = (mItemHeight - mProfile->mFont->getHeight()) / 2; + + if(height > 0) + drawRect.point.y += height; + + // JDD - offset by two pixels or so to keep the text from rendering RIGHT ONTOP of the outline + drawRect.point.x += 2; + + drawer->drawText( mProfile->mFont, drawRect.point, displayText, mProfile->mFontColors ); + + if ( mRenamingItem == item && mRenameCtrl ) + { + Point2I ctrPos = globalToLocalCoord( drawRect.point ); + ctrPos.y -= height; + ctrPos.x -= 2; + + Point2I ctrExtent( getWidth() - ctrPos.x, drawRect.extent.y ); + + mRenameCtrl->setPosition( ctrPos ); + mRenameCtrl->setExtent( ctrExtent ); + mRenameCtrl->setVisible( true ); + } +} \ No newline at end of file diff --git a/Engine/source/BadBehavior/tools/guiBTViewCtrl.h b/Engine/source/BadBehavior/tools/guiBTViewCtrl.h new file mode 100644 index 0000000000..9800f2d679 --- /dev/null +++ b/Engine/source/BadBehavior/tools/guiBTViewCtrl.h @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// 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 _GUI_BTVIEWCTRL_H_ +#define _GUI_BTVIEWCTRL_H_ + +#ifndef _GUI_TREEVIEWCTRL_H +#include "gui/controls/guiTreeViewCtrl.h" +#endif + +namespace BadBehavior +{ + // subclassing GuiTreeViewCtrl so that we can tweak it for the behavior tree editor + class GuiBehaviorTreeViewCtrl : public GuiTreeViewCtrl + { + typedef GuiTreeViewCtrl Parent; + + public: + void onRenderCell(Point2I offset, Point2I cell, bool, bool); + + DECLARE_CONOBJECT(GuiBehaviorTreeViewCtrl); + }; + +} // namespace BadBehavior + +#endif diff --git a/Tools/CMake/modules/module_badBehavior.cmake b/Tools/CMake/modules/module_badBehavior.cmake new file mode 100644 index 0000000000..744bc72fee --- /dev/null +++ b/Tools/CMake/modules/module_badBehavior.cmake @@ -0,0 +1,32 @@ +# ----------------------------------------------------------------------------- +# 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. +# ----------------------------------------------------------------------------- + +# Navigation module +option(TORQUE_BADBEHAVIOR "Enable BadBehavior module" ON) + +if(TORQUE_BADBEHAVIOR) + #addDef( "TORQUE_BADBEHAVIOR_ENABLED" ) + + # files + addPathRec( "${srcDir}/BadBehavior" ) +endif(TORQUE_BADBEHAVIOR) + From ff9760e50823e01b685cb155caa0ed5f1f99fb5c Mon Sep 17 00:00:00 2001 From: Marc Chapman Date: Sun, 12 Nov 2017 02:07:40 +0000 Subject: [PATCH 2/9] Add Full template files --- .../Full/game/levels/BehaviorTestbed.mis | 226 +++++++++ .../game/levels/BehaviorTestbed_preview.png | Bin 0 -> 343625 bytes .../game/scripts/server/BadBehavior/BadBot.cs | 478 ++++++++++++++++++ .../server/BadBehavior/behaviorTreeManager.cs | 138 +++++ .../BadBehavior/behaviorTrees/FollowTree.cs | 26 + .../BadBehavior/behaviorTrees/PatrolTree.cs | 50 ++ .../BadBehavior/behaviorTrees/WanderTree.cs | 33 ++ .../BadBehavior/behaviorTrees/botMatchTree.cs | 142 ++++++ .../BadBehavior/behaviorTrees/botTree.cs | 70 +++ .../BadBehavior/behaviorTrees/combatTree.cs | 99 ++++ .../behaviorTrees/getHealthTree.cs | 33 ++ .../scripts/server/BadBehavior/botMatch.cs | 50 ++ .../game/scripts/server/BadBehavior/main.cs | 25 + .../gui/BTEditorCreatePrompt.ed.gui | 217 ++++++++ .../gui/behaviorTreeEditor.ed.gui | 321 ++++++++++++ .../images/BehaviorTreeView.png | Bin 0 -> 923 bytes .../images/classIcons/Randomwait.png | Bin 0 -> 278 bytes .../images/classIcons/activeSelector.png | Bin 0 -> 333 bytes .../images/classIcons/behavior.png | Bin 0 -> 274 bytes .../images/classIcons/failAlways.png | Bin 0 -> 364 bytes .../images/classIcons/inverter.png | Bin 0 -> 376 bytes .../images/classIcons/loop.png | Bin 0 -> 366 bytes .../images/classIcons/monitor.png | Bin 0 -> 367 bytes .../images/classIcons/parallel.png | Bin 0 -> 293 bytes .../images/classIcons/randomSelector.png | Bin 0 -> 354 bytes .../images/classIcons/root.png | Bin 0 -> 317 bytes .../images/classIcons/scriptEval.png | Bin 0 -> 286 bytes .../images/classIcons/scriptFunc.png | Bin 0 -> 297 bytes .../images/classIcons/scriptedBehavior.png | Bin 0 -> 293 bytes .../images/classIcons/selector.png | Bin 0 -> 348 bytes .../images/classIcons/sequence.png | Bin 0 -> 286 bytes .../images/classIcons/subTree.png | Bin 0 -> 376 bytes .../images/classIcons/succeedAlways.png | Bin 0 -> 376 bytes .../images/classIcons/ticker.png | Bin 0 -> 387 bytes .../images/classIcons/wait.png | Bin 0 -> 281 bytes .../images/classIcons/waitForSignal.png | Bin 0 -> 334 bytes .../game/tools/behaviorTreeEditor/main.cs | 31 ++ .../scripts/behaviorTreeEditor.ed.cs | 390 ++++++++++++++ .../scripts/behaviorTreeEditorCanvas.ed.cs | 152 ++++++ .../behaviorTreeEditorContentList.ed.cs | 133 +++++ .../scripts/behaviorTreeEditorInspector.ed.cs | 50 ++ .../scripts/behaviorTreeEditorProfiles.ed.cs | 26 + .../scripts/behaviorTreeEditorStatusBar.ed.cs | 41 ++ .../scripts/behaviorTreeEditorUndo.ed.cs | 174 +++++++ .../scripts/guiBehaviorTreeViewCtrl.ed.cs | 433 ++++++++++++++++ 45 files changed, 3338 insertions(+) create mode 100644 Templates/Full/game/levels/BehaviorTestbed.mis create mode 100644 Templates/Full/game/levels/BehaviorTestbed_preview.png create mode 100644 Templates/Full/game/scripts/server/BadBehavior/BadBot.cs create mode 100644 Templates/Full/game/scripts/server/BadBehavior/behaviorTreeManager.cs create mode 100644 Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/FollowTree.cs create mode 100644 Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/PatrolTree.cs create mode 100644 Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/WanderTree.cs create mode 100644 Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/botMatchTree.cs create mode 100644 Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/botTree.cs create mode 100644 Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/combatTree.cs create mode 100644 Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/getHealthTree.cs create mode 100644 Templates/Full/game/scripts/server/BadBehavior/botMatch.cs create mode 100644 Templates/Full/game/scripts/server/BadBehavior/main.cs create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/gui/BTEditorCreatePrompt.ed.gui create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/gui/behaviorTreeEditor.ed.gui create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/BehaviorTreeView.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Randomwait.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/activeSelector.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/behavior.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/failAlways.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/inverter.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/loop.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/monitor.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/parallel.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/randomSelector.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/root.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptEval.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptFunc.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptedBehavior.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/selector.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/sequence.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/subTree.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/succeedAlways.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ticker.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/wait.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/waitForSignal.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/main.cs create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditor.ed.cs create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorCanvas.ed.cs create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorContentList.ed.cs create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorInspector.ed.cs create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorProfiles.ed.cs create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorStatusBar.ed.cs create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorUndo.ed.cs create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/scripts/guiBehaviorTreeViewCtrl.ed.cs diff --git a/Templates/Full/game/levels/BehaviorTestbed.mis b/Templates/Full/game/levels/BehaviorTestbed.mis new file mode 100644 index 0000000000..253b9708cc --- /dev/null +++ b/Templates/Full/game/levels/BehaviorTestbed.mis @@ -0,0 +1,226 @@ +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + canSave = "1"; + canSaveDynamicFields = "1"; + enabled = "1"; + + new LevelInfo(theLevelInfo) { + nearClip = "0.1"; + visibleDistance = "1000"; + decalBias = "0.0015"; + fogColor = "0.6 0.6 0.7 1"; + fogDensity = "0"; + fogDensityOffset = "700"; + fogAtmosphereHeight = "0"; + canvasClearColor = "0 0 0 255"; + ambientLightBlendPhase = "1"; + ambientLightBlendCurve = "0 0 -1 -1"; + advancedLightmapSupport = "0"; + soundAmbience = "AudioAmbienceDefault"; + soundDistanceModel = "Linear"; + canSave = "1"; + canSaveDynamicFields = "1"; + desc0 = "Somewhere to play with behavior trees"; + enabled = "1"; + levelName = "Behavior Testbed"; + }; + new SkyBox(theSky) { + Material = "BlackSkyMat"; + drawBottom = "0"; + fogBandHeight = "0"; + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new Sun(theSun) { + azimuth = "230.396"; + elevation = "45"; + color = "0.996078 0.996078 0.996078 1"; + ambient = "0.560784 0.564706 0.572549 1"; + brightness = "1"; + castShadows = "1"; + coronaEnabled = "1"; + coronaScale = "0.5"; + coronaTint = "1 1 1 1"; + coronaUseLightColor = "1"; + flareScale = "1"; + attenuationRatio = "0 1 1"; + shadowType = "PSSM"; + texSize = "1024"; + overDarkFactor = "3000 2000 1000 250"; + shadowDistance = "400"; + shadowSoftness = "0.25"; + numSplits = "4"; + logWeight = "0.96"; + fadeStartDistance = "325"; + lastSplitTerrainOnly = "0"; + representedInLightmap = "0"; + shadowDarkenColor = "0 0 0 -1"; + includeLightmappedGeometryInShadow = "0"; + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new SimGroup(PlayerDropPoints) { + canSave = "1"; + canSaveDynamicFields = "1"; + enabled = "1"; + + new SpawnSphere() { + autoSpawn = "0"; + spawnTransform = "0"; + radius = "5"; + sphereWeight = "1"; + indoorWeight = "1"; + outdoorWeight = "1"; + isAIControlled = "0"; + dataBlock = "SpawnSphereMarker"; + position = "0 0 2"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + canSave = "1"; + canSaveDynamicFields = "1"; + enabled = "1"; + homingCount = "0"; + lockCount = "0"; + }; + }; + new GroundPlane() { + squareSize = "100"; + scaleU = "5"; + scaleV = "5"; + Material = "Grid512_ForestGreenLines_Mat"; + canSave = "1"; + canSaveDynamicFields = "1"; + enabled = "1"; + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + }; + new SimGroup(podia) { + canSave = "1"; + canSaveDynamicFields = "1"; + + new ConvexShape() { + Material = "Grid512_OrangeLines_Mat"; + position = "10 -10 0.500001"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + surface = "0 0 0 1 4.05839e-010 1.44067e-009 0.112745"; + surface = "0 1 0 0 5.05789e-009 -4.56528e-009 -0.5"; + surface = "0.311251 0 0 0.950328 1.59573e-009 1.39021 -0.163826"; + surface = "0 -0.311251 0.950328 -0 4.85838e-010 -1.39021 -0.163826"; + surface = "0.220088 0.220088 -0.671983 0.671983 -1.39021 -3.2702e-008 -0.163826"; + surface = "0.220088 -0.220088 0.671983 0.671983 1.39021 -2.28065e-008 -0.163826"; + }; + new ConvexShape() { + Material = "Grid512_OrangeLines_Mat"; + position = "-10 -10 0.500001"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + surface = "0 0 0 1 4.05839e-010 1.44067e-009 0.112745"; + surface = "0 1 0 0 5.05789e-009 -4.56528e-009 -0.5"; + surface = "0.311251 0 0 0.950328 1.59573e-009 1.39021 -0.163826"; + surface = "0 -0.311251 0.950328 -0 4.85838e-010 -1.39021 -0.163826"; + surface = "0.220088 0.220088 -0.671983 0.671983 -1.39021 -3.2702e-008 -0.163826"; + surface = "0.220088 -0.220088 0.671983 0.671983 1.39021 -2.28065e-008 -0.163826"; + }; + new ConvexShape() { + Material = "Grid512_OrangeLines_Mat"; + position = "-10 10 0.500001"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + surface = "0 0 0 1 4.05839e-010 1.44067e-009 0.112745"; + surface = "0 1 0 0 5.05789e-009 -4.56528e-009 -0.5"; + surface = "0.311251 0 0 0.950328 1.59573e-009 1.39021 -0.163826"; + surface = "0 -0.311251 0.950328 -0 4.85838e-010 -1.39021 -0.163826"; + surface = "0.220088 0.220088 -0.671983 0.671983 -1.39021 -3.2702e-008 -0.163826"; + surface = "0.220088 -0.220088 0.671983 0.671983 1.39021 -2.28065e-008 -0.163826"; + }; + new ConvexShape() { + Material = "Grid512_OrangeLines_Mat"; + position = "10 10 0.500001"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + surface = "0 0 0 1 4.05839e-010 1.44067e-009 0.112745"; + surface = "0 1 0 0 5.05789e-009 -4.56528e-009 -0.5"; + surface = "0.311251 0 0 0.950328 1.59573e-009 1.39021 -0.163826"; + surface = "0 -0.311251 0.950328 -0 4.85838e-010 -1.39021 -0.163826"; + surface = "0.220088 0.220088 -0.671983 0.671983 -1.39021 -3.2702e-008 -0.163826"; + surface = "0.220088 -0.220088 0.671983 0.671983 1.39021 -2.28065e-008 -0.163826"; + }; + }; + new SimGroup(Paths) { + canSave = "1"; + canSaveDynamicFields = "1"; + + new Path(PatrolPath) { + isLooping = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new Marker() { + seqNum = "0"; + type = "Normal"; + msToNext = "1000"; + smoothingType = "Spline"; + position = "-10 10 0.928092"; + rotation = "0 0 -1 48.0534"; + scale = "1 1 1"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new Marker() { + seqNum = "1"; + type = "Normal"; + msToNext = "1000"; + smoothingType = "Spline"; + position = "10 10 0.928092"; + rotation = "0 0 1 46.6231"; + scale = "1 1 1"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new Marker() { + seqNum = "2"; + type = "Normal"; + msToNext = "1000"; + smoothingType = "Spline"; + position = "10 -10 0.928092"; + rotation = "0 0 1 137.031"; + scale = "1 1 1"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new Marker() { + seqNum = "3"; + type = "Normal"; + msToNext = "1000"; + smoothingType = "Spline"; + position = "-10 -10 0.928092"; + rotation = "0 0 1 225.424"; + scale = "1 1 1"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- + diff --git a/Templates/Full/game/levels/BehaviorTestbed_preview.png b/Templates/Full/game/levels/BehaviorTestbed_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..a440c9e1d444228f2f53c3cfbb29a0dc2a2dad4a GIT binary patch literal 343625 zcmeFX`#aPB{|CNK)JuhgB6Oq>$@#c)2+<_wkYkQHle4iEl~WESrx@jIPB|N9

Jg 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

SIg&~zrS3qZryqI z?9l_wNKq~>PMqVqsh_Ry3uGSWl~mRv80?MEP*bS#Z|23C5rIc)hMmK}?V%@b-a?UDbg{`F2)fqH-i?3rx zF1S*^c+g!&;|2_rJ5nZfybGYuq%4vJUg$PvvvY?6^;(}p1*hJ_;A0jLxL}jz_uu)ACsM}*ZF!Ddjpx=KseWp?9stN z8QqA9@frHXyTyOUew@)g7t;&oIAOO@=)L#OJLg67q?vftk#fD+m%8ACAZW*o>G zI4##J$6PS`!828m`*@=j4xnk8GGMBGNz%T#TK`1a<7eYlyW!f~&px&XbmtF)S-aWa zg(t>CHdb!w6g-9wmMwx9Ng%ON^WD=?a+Z|{qQsGzxaKNdq@2IQNBHi3St*d zALqlvdb!L8he2Js3`c@)KIaT>?aUp@|Fp}5Q_)`^$|YSU6IH> zranN?Zk3c&@V(lPcJyCag!P7}vFO45LRjp^SCOl$RqWz?v3~I2 zk#uX(nF!u}^!V3*^S92<=gUw zpH#(swLHDNtVPbh@TDh@Pg-T4GjfXOXZ6MTJ_pnj+B=A|gUIZ?XCjwl0Ixt$zcR;) z54^wc_xBT_uETWN)EBFmGqambCrwo)(W}L(sVnOHV5mx3uGbP(g^19n8$rw#BP9V7 z3xwI)I;sc-^6cZf)0_)?@s2%NPv}6_<$0*fx8=?hD;Sh9rU8Sj2I)dWdJnTj(1Nf6 zDbq4%79n;_sn-*TjQ?&DSaq>n+R6Gl{IbdX>4|QLK`;oz4XYk7{N7qMm)4tVuo7j- zj@-b`dM2P_b~o&79(yC%#%$wT)hUcIk#$ahZt4AlzE4Dbg3}QBz0sURRW+_-N*M(0 zqyHqEKrBHP&a(^sA|zY5S~6cZ$8e)nGve{z>~Z?10zjbi%X5rWb9*w!ZtDSi1#UB{ z*S6s#!>=4$R(Hv}Fb_sa``$^+VE&7&KTi4CSeakYL!PLY34Ji;6@^Da9Bav-92u@1u9#uhTk&DpYk{RiUbb3su$Bbr4N2 zz5jz;`SY{W7hZYkpZxa!adB}TLe;PjA-wvM}bvd>u3A6XS_aTH}x!_jAU|T)e zc2)oit)#YS=Rw7aDWw!c2+?xEQi>^Vg7lu^1{vD9E4IAxH1cv?f9_!@B#MX(Ol>@X zHimYXnSE7ypP3a+wi*CaGuXzPdf9L7Ar%Ueo=p-!P|t2UsV8+kX)0x()+==sW4G?Q zHm9Zv-nlL%5$Qw#ICey=qV0MeW3&e{U!r$DQC_v}q@ED?i4Nd^Y7>r)(Tl|BZ*9*~Gc#l$vKvBIf$ zFWTr@lu?nO(x|GQ2PPkF{M+q8G*+;?_o?uF*pK7R7u&#u{53P6%7 zl$=8D-HHm-d!&HIHP)G+dgK8@PJlw4IuFUD8N`_0)%p-1tWL4M06-jsylNsL0jsNQ z*#W?l1w>8nm!6PCOIm&w7Uc|J)5i}?!Vr)u!OVc7$pfsaY}ND1q-#HSFzX)HB)nfQ zB_wR%6=cc5=7j(`1)Ix4z=LgL0EZG(z`* zk2B|?lnOaOnOHPu$-n$R%>Ube>5z~JSR}icf<(w|R=i}(6b_Vs@#xdPznpeQ-(OYX z^Iv}FgCD$mxmYaQR^Z=#^yJ_FkN=Cur)SI68laR4a@_akp_Fph#XtS*pMUC$U-_+n z_>X?$SO3PXTZfh4@xv3jxbR&UAq(I_a0q=2ELC~E4sCbm$)n}|L3``2R#he!s*qEp zw&U4Upiu-s6pA?)A04nt-Q|4AKplKZ_+ZMN%$MsDqe5B=>ke%o0W0jo&Q+BYDpfnJ zzFnvTYOg@aEh++96+D!g+Ss2xZBWVc0Wc(j^ya5I1!#j3n-+*(CtHQR%EC8`V%vvq z3>fW57aZ&2L)o0+(z{FB*vYVXdx1#cu}`tEU*ukRjkHZF`T=?kweXBlpCVOFIY}$j zm%i@rvwU7~zW%(YJ&uB zAM1;&-^k3~`@l^<>c47Jz&7P5exx1WLE+E4Q@pWi#-PmbEARa<>^mNt;q0^NjEJm~ zl6USm&(7}Le*5w1EY2EuYI+y>JS3^P*qJ;H&qp4icJP}P$ec;Jx9!}qM-^h z-ukQ?8~dzzkbJh9r69wln&9rlD?kW7#29_>&{B-jF@xwMP5Y^3m`tau)v7lOw$y42 zCfx?a8#x-b)G6R;^trv=2)tsXn+@08A>%>fU;l~SyC%&!_j*)uUgemX*;_vp=biI= z`?F@+OeT|J40u0&h;2h;gL8UYvpuV^0GpCv>3qs5q+qcXJMK6FS&{|_&bgElvm2ot zo1M+TjhkPvOX=BYN239aF*!JCGm(;aR1twxkyVJ8Nr?rp7z1ROH=ndrGB<~A z!EHb!Sq;LUO(*N5T1~o?dS>6nn22GHYbZJAJh5dNRaNM^&cb8`2)+V30ESjSIa>f2 zB1o$cjt=*qy>mR9HeF09%WARkrli_LFfrVg!>A{Uex!h%G277sna33kG>fpZWEr2c zP(Q3(gF#a4*|l9enM@sXHL0wKwRhezPp2~rzVD~87%R4D3w**PBFt=~W?AF~tIx65 z@5@WhDH^S8(}Aiwb~*RCE%sPI%yD}W>6n1W%TW!#-G&oJ`~4Z8z74|!i)R>nmU^gg z!#Ds7E)o%={;}8?_?Pw){raDCPDFN$i%m4`Fjv;bm=JtG&m(e5%x-K2^?O@?qZB$2 zaq3ndey05oetPl#dzYRC)Bu1a5Ggs3M~Y>gx}Yk^8BnkTh;oDqz)Fl9Nd!#V9HZKY ziY`yEI)@6#i$Y}uQ3ZnoVhc?W5IH&Ynk`9=g)0a&M}af+J&_lLzMLSD3rc`yC4qO! zo{)ru&z61GE}V@s7X_%FNb@-Wz^0Y4LPdrQ0v!&UR1s1?RfQ@Gpdd}l4ZZa>ksa`> ze+B>cf1l2l-Z{t0QKNlHH)W75U6ve?NY)aBt%A&?N``d%W!7K%-!J~|&+p%wn$yBr z7z!vTD22H{M=6c*w`c`HXbE+Jih6>U>4)RmBCb>-E9e6I?u5 z?;p2Ex4o)^xPyISN6dNM0ebiL!Rrqmiz)*#=jCEefD(D{Ol$6TXba%+u*feqHYycGCNjL^(vwSSo`k*B5}d> zu*^u~@9muH=kFoF_u4M!S35`Eee^E2)kGTopO6OXVz$;0B?kPMUkbq~$+%F^KyW>} zQ4C-$SuCV#Ed{cWI>$sDLhvEfpMjdK~^*OTetEuGIVZ{N9n`}Xr$(pztSV=!zw3 z_Ed%1aqvs;z3*iG{PTA@S(|Os3pD@;!P_#?Ucn@gXjX#~`!xE&X4o+yf)A_pNdYf0jc;Hv(7hl(VP^6pyoSdBu;#GHJ{aLwq;VW0!)Mu5FdftyXv&%9{c-YnB z?IEy16#Ho&YqWZkJ+YBtznb1@*WF2G?jo z?RE5%LCq`i(nF3ffSfa_3bcn}M_jVtNrmmPH8U$EcCopzeP5M;oV81`N|a(IU$Ll4 zN2*!$YO$>AV7#B1yLA*5Yh~YdY1O5xb*rk&<%-!Ndh%)=uNF(Px|*eNC46xA@7-a? zIZI4Ar+jvP>DO&L()Vd=khcFu8vGWU-h50n9$A~7-Xt8Ci}K|tTTL$mFl3~3?dRjUMr9V3W|}MPJjIq)4%(>^EWS9dW}zug}~mioxu>foO9ol z90uAM6v4tG>0(X)`R_0P&d(hXgH@<-z-&bV3;qm+^h{8tOCTr9oG$0f@BP5Nx8Hp- zU##AK=V9tvN19dc-Mjtf+YhM(tQS!>00^r3`a2J==F7J0-u~iOAAjbvZP(%85YIld zx|*+^oJ1RdoXIhav_wcTxi}4%XVF(2oWFgGnp&KboD?{}oEskCyzgSP3il|+i+I*5 zFo}=L?&!{;dII2rbIhU{+UMQ(rt22a4mFoM6J;ylKu}eN0$lRF0I2lv9rfrqS-Y*# z3LjDoh?$GcpeiakWfi>@*Q`pC-}d!@#g_%xvtL_&Y_e()Hhq05vsW;*me`SGwmi#C z55F~DF=VD#BVQTLN5w$Z;ah|0(38|Q^BFUv9vDE87le_>82wDUwkrZ;R5oYB^Q6qu z@C%D7wn1U%N;vxtCjt>s6{;b1kcev3eR|kNkl9F4Y0?6-R5!K+3XrjdbYM&{Y5_}S z3|Y7E0j6B`PH|=tYQyG9BQ9B*}pYgw`;S;_ojQpIt6Q~66QuA@d)=E z@eUQ$z1iLnT4g6m&E!Yq)ASs*@<3jHEJlGc7*z==98G<1G-$u(OOc{2 zI;7s{GjSg#A^70f)pcz#K$Wj-azpTyuK-x*pBssB{ErYq$QJbDtq?Rj=Um)GtQq>5 zVvKQMLFEC5n^Ya74sG-`8b8C>=-nazF~Bi))s3x+p?;24g5k2vxu6~(5InOGF&I`D zAL#jDA8)R*PU=&NNx1EN2!Q4sp(3dR*X_Cu-Vt+*UDvHlrq@-~cF{2}SM6%mK0ZC0 zFIHBxfK(s~CXiaUM0V`GyL0PkZ!&Stt=cZ2O(f``-YYc5*b8*%PT<^ivv=DjFqP~njQ7T)y>-+N*#@m2A@_E> zA%qws?XEgSDK|3$ey-oa_MvOfLEA5b`9J>J&Smw+kK|hM)2D6LmCQ^&-hN~=K-tNM zpK1TyAA9FR$CqsJHCf?mNH$F!#BhwXQV}S?6>^3oKmi9Ow6@L}j-d%zK!oN{C$~v~ z_Ci1dxyiF(oVC zONF&dOUMK;vw|c?QdKZjw?Q>>*5wIxXJsjZAWPGy!Cob4jvzs!uxO>2R7I851t>}G zEUgZJ!ey=A_;i%as*;mH6x#jD$LiP5erMASKEkQlIJsv8JU=F)gp ztxEtq68Xkie(fZ^a>t2+kzvkD$pisA^k%uLDDjlL{q7rl=T&BY?e&LWd+W)M|H$4a z|Ji3!>bkbeS$_O4eD9O9tG6FKfkJYUp`33|V1S;UT|7B`jI0k|d*kArccopg-+Hrq z;l=g-VYghb7E9_n5CPtMuPJs4N6gMSrkE4oxc4{Wh&;{b?kEo<=8-WJEp~`%=G4Ss19b{6jSfK z_Gt|EK(~);D*&aU0?jco^HBM1?A8zwA+ksTB7!1I9@z7@ViLT^cWxS)_dvTgXJYcs zIRXnwDeh7XWZH9L*H4P67*c-mex*UsaYTP|fE}GpXyk znoLFIa60?g$9^8rCr=)K=Cgl#adDX>izpG@yZ21p)KCZL{9NkiUsx`e%jNQNetC6v zUcqw}SYwiK`Q*HACe@NtS}+roRP_Aq^HY9eCMbF%9=#rnO&7UQE(Mk7z5Us~eb9Do zA1l>2?{xlZKL4NHi$6$T|3Uqu-_`H1qfBgab8j}=n@tCzH_FeNhZ!&2LLpKr;xq9v z9gq9xlo?W0GuRXW?agNM#RAa9#Zn>Wzo_cU3~Ci!E|)5@@ow~S6$k$6eA6)xYpIFTm>aagbK)kz2>e6a;^bEiVA=oh>==oRxq5?oRE`(Q6Hc>rktd^ zqWPm@9-u;MRRmDDppsNFRG|zXAZ<~t$OEJjHB)I2T(o~J)k4`5F*Jj{3X~iG%X9+; zcw!`CPsHR1&QXp^PRU_%Tk9hN&|YwNNkr;cxq@U^{3to)pedT;59}3AQxw%wXH*F( z_3_&oM8u9H#y0097I>nniqrMYs{jBX07*naR7~VvKK8%%ar~$Mk3#D%rDC>0JRx_Ay9UZ*<;{A6XJbwGZladY#_h0|P>OcAUSraG|6H}Nz`{?K={_wM3`-S6A zgNVSU;GV|tyYC*)qaMf3dDrE9alSY?IlsJ`D}g;teSNSuyPU7tInCL;5K=-@3XR#S z`19A^zCD?qeep|~D0OKGbdR4bt0vF(qO17ol5ZF^;lpK>PdFQ8->B;H5i_r@Gs6q_kEzZWl zZl(b>Fyi*!tA%eeqcwVAXGPXT3e)SW@mr7Q6e9z~#T%oe7>U9sEezg;p(qD?mxtVm z!cVmK3Zhrp$<9~G7>sz9u^CtlyKE5pw3a>w%$|lU7N@1y&qUBMtoUyh%+oYcgfx!glA6@ zCJ?ve9d`eE>wW`w+6}luU4WT+3J_xKZdWf^sUKns3?y4cY>|a1H_yxi^TCi*p_@uU zfD)-Aw#;?YYzP{HV`lQ+h2Xt+z7C

$-00s&1x}i8l4RswR`!!NK9je){J`<@D_2 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%0A|tsjzZrljf+7GpO9c$rTZB=~%6#1~<~?ej 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 literal 0 HcmV?d00001 diff --git a/Templates/Full/game/scripts/server/BadBehavior/BadBot.cs b/Templates/Full/game/scripts/server/BadBehavior/BadBot.cs new file mode 100644 index 0000000000..0756505915 --- /dev/null +++ b/Templates/Full/game/scripts/server/BadBehavior/BadBot.cs @@ -0,0 +1,478 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Base frequency for behavior tree ticks (in milliseconds) +//----------------------------------------------------------------------------- +$BotTickFrequency = 100; + +// ---------------------------------------------------------------------------- +// make the local player invisible to the AI +//----------------------------------------------------------------------------- +function setGodMode(%val) +{ + LocalClientConnection.player.isGod = %val; +} + +// don't damage an invincible player +function DefaultPlayerData::damage(%this, %obj, %sourceObject, %position, %damage, %damageType) +{ + if(%obj.isGod) + return; + + Parent::damage(%this, %obj, %sourceObject, %position, %damage, %damageType); +} + +//----------------------------------------------------------------------------- +// bot datablock +//----------------------------------------------------------------------------- +datablock PlayerData(BadBotData : DefaultPlayerData) +{ + // max visible distance + VisionRange = 40; + + // vision field of view + VisionFov = 120; + + // max range to look for items + findItemRange = 20; + + // the type of object to search for when looking for targets + targetObjectTypes = $TypeMasks::PlayerObjectType; + + // the type of object to search for when looking for items + itemObjectTypes = $TypeMasks::itemObjectType; + + // some numbers for testing + + // distance the bot wants to be from its target when using the Ryder + optimalRange["Ryder"] = 8; + + // number of milliseconds to hold the trigger down when using the Ryder + burstLength["Ryder"] = 100; + + // distance the bot wants to be from its target when using the Lurker + optimalRange["Lurker"] = 12; + + // number of milliseconds to hold the trigger down when using the Lurker + burstLength["Lurker"] = 750; + + // +/- deviation from optimal range that is tolerated + rangeTolerance = 3; + + // probability that the bot will switch from its current target to another, closer target + switchTargetProbability = 0.1; + + // disable other weapons, we don't know how to use them yet + maxInv[LurkerGrenadeLauncher] = 0; + maxInv[LurkerGrenadeAmmo] = 0; + maxInv[ProxMine] = 0; + maxInv[DeployableTurret] = 0; +}; + + +//============================================================================= +// Supporting functions for an AIPlayer driven by a behavior tree +//============================================================================= + +// Spawn a bot called %name located at %startpos +function BadBot::spawn(%name, %startPos) +{ + // create the bot + %bot = new AIPlayer(%name) { + dataBlock = BadBotData; + class = "BadBot"; + }; + + // give it a name + if(%name !$= "") + %bot.setShapeName(%name); + + // set its position, or use the default if no position is given + if(isObject(%startPos)) + { + %startPos = %startPos.position; + } + else if(%startPos $= "") + { + %spawnPoint = pickPlayerSpawnPoint(PlayerDropPoints); + if(isObject(%spawnPoint)) + %startPos = %spawnPoint.getPosition(); + } + + %bot.setPosition(%startPos); + + // tetherpoint will give the bot a place to call home + %bot.tetherPoint = %startPos; + + return %bot; +} + + +// override getMuzzleVector so that the bots aim at where they are looking +function BadBot::getMuzzleVector(%this, %slot) +{ + return %this.getEyeVector(); +} + + +// use onAdd to equip the bot +function BadBotData::onAdd(%data, %obj) +{ + // give him the standard player loadout + game.loadout(%obj); +} + + +// Override onDisabled so we can stop running the behavior tree +function BadBotData::onDisabled(%this, %obj, %state) +{ + %obj.behaviorTree.stop(); + Parent::onDisabled(%this, %obj, %state); +} + + +// moveTo command, %dest can be either a location or an object +function BadBot::moveTo(%this, %dest, %slowDown) +{ + %pos = isObject(%dest) ? %dest.getPosition() : %dest; + %this.setMoveDestination(%pos, %slowDown); + %obj.atDestination = false; +} + +// forward onReachDestination to the behavior tree as a signal +function BadBotData::onReachDestination(%data, %obj) +{ + if(isObject(%obj.behaviorTree)) + %obj.behaviorTree.postSignal("onReachDestination"); + + %obj.atDestination = true; +} + +// forward animationDone callback to the behavior tree as a signal +function BadBotData::animationDone(%data, %obj) +{ + if(isObject(%obj.behaviorTree)) + %obj.behaviorTree.postSignal("onAnimationDone"); +} + +// get the index of the closest node on the specified path +function BadBot::getClosestNodeOnPath(%this, %path) +{ + if(isObject(%path) && %path.isMemberOfClass("SimSet") && (%numNodes = %path.getCount()) > 0) + { + %bestNode = 0; + %bestDist = VectorDist(%path.getObject(%bestNode).position, %this.position); + + for(%i=1; %i < %numNodes; %i++) + { + %node = %path.getObject(%i); + %dist = VectorDist(%node.position, %this.position); + + if(%dist < %bestDist) + { + %bestNode = %i; + %bestDist = %dist; + } + } + + return %bestNode; + } + return -1; +} + + +// send a chat message from the bot +function BadBot::say(%this, %message) +{ + chatMessageAll(%this, '\c3%1: %2', %this.getShapeName(), %message); +} + + +//=============================Global Utility================================== +function RandomPointOnCircle(%center, %radius) +{ + %randVec = (getRandom() - 0.5) SPC (getRandom() - 0.5) SPC "0"; + %randVec = VectorNormalize(%randVec); + %randVec = VectorScale(%randVec, %radius); + return VectorAdd(%center, %randVec); +} + +//===========================ScriptedBehavior Tasks============================= +/* + ScriptedBehavior Tasks are composed of four (optional) parts: + 1) precondition - this function should return a boolean indicating whether + or not the behavior should continue. If precondition returns true, the + rest of the behavior is evaluated. If precondition returns false, the + behavior will abort. + + There are two options for the evaluation of the precondition that can be + set in the editor: + ONCE - The precondition is run the first time the behavior becomes active + TICK - The precondition is run each time the behavior is ticked (if latent) + + 2) onEnter - This is called the first time the behavior is run if the + precondition was successful. onEnter does not use a return value. + + 3) onExit - This is called if the behavior reaches completion. onExit does + not use a return value. + + 4) behavior - This is the main behavior function, evaluated each tick. + behavior must return a status (SUCCES / FAILURE / RUNNING). +*/ + +//============================================================================== +// wander behavior task +//============================================================================== +function wanderTask::behavior(%this, %obj) +{ + // stop aiming at things + %obj.clearAim(); + + // if the bot has a tetherPoint, use that as the center of his wander area, + // otherwise use his current position + %basePoint = %obj.tetherPoint !$= "" ? %obj.tetherPoint : %obj.position; + + // move + %obj.moveTo(RandomPointOnCircle(%basePoint, 10)); + return SUCCESS; +} + + +//============================================================================== +// Move to closest node task +//============================================================================== +function moveToClosestNodeTask::precondition(%this, %obj) +{ + // check that the object has a path to folow + return isObject(%obj.path); +} + +function moveToClosestNodeTask::onEnter(%this, %obj) +{ + // stop aiming + %obj.clearAim(); +} + +function moveToClosestNodeTask::behavior(%this, %obj) +{ + // get the closest node + %obj.currentNode = %obj.getClosestNodeOnPath(%obj.path); + + // move toward it + %obj.moveTo(patrolPath.getObject(%obj.currentNode)); + return SUCCESS; +} + +//============================================================================== +// Patrol behavior task +//============================================================================== +function patrolTask::precondition(%this, %obj) +{ + // the bot needs a path object + return isObject(%obj.path); +} + +function patrolTask::onEnter(%this, %obj) +{ + // stop aiming + %obj.clearAim(); +} + +function patrolTask::behavior(%this, %obj) +{ + // hook into the standard AIPlayer path following + %obj.moveToNextNode(); + return SUCCESS; +} + +//============================================================================= +// findHealth task +//============================================================================= +function findHealthTask::behavior(%this, %obj) +{ + // get the objects datablock + %db = %obj.dataBlock; + + // do a container search for items + initContainerRadiusSearch( %obj.position, %db.findItemRange, %db.itemObjectTypes ); + while ( (%item = containerSearchNext()) != 0 ) + { + // filter out irrelevant items + if(%item.dataBlock.category !$= "Health" || !%item.isEnabled() || %item.isHidden()) + continue; + + // check that the item is within the bots view cone + if(%obj.checkInFov(%item, %db.visionFov)) + { + // set the targetItem field on the bot + %obj.targetItem = %item; + break; + } + } + + return isObject(%obj.targetItem) ? SUCCESS : FAILURE; +} + +//============================================================================= +// getHealth task +//============================================================================= +function getHealthTask::precondition(%this, %obj) +{ + // check that we have a valid health item to go for + return (isObject(%obj.targetItem) && %obj.targetItem.isEnabled() && !%obj.targetItem.isHidden()); +} + +function getHealthTask::onEnter(%this, %obj) +{ + // move to the item + %obj.moveTo(%obj.targetItem.position); +} + +function getHealthTask::behavior(%this, %obj) +{ + // succeed when we reach the item + if(!%obj.atDestination) + return RUNNING; + + return SUCCESS; +} + +//============================================================================= +// pickTarget task +//============================================================================= +function pickTargetTask::precondition(%this, %obj) +{ + // decide if we should pick a new target or keep the old one + if(isObject(%obj.targetObject)) + { + return (VectorDist(%obj, %obj.targetObject) > %obj.dataBlock.visionRange || + getRandom() < %obj.dataBlock.switchTargetProbability); + } + + return true; +} + +function pickTargetTask::behavior(%this, %obj) +{ + %obj.targetObject = -1; + %db = %obj.dataBlock; + + // container search for other players + initContainerRadiusSearch( %obj.position, %db.VisionRange, %db.targetObjectTypes ); + while ( (%target = containerSearchNext()) != 0 ) + { + // don't target ourself, dead players or god. + if(%target == %obj || !%target.isEnabled() || %target.isGod) + continue; + + // Check that the target is within the bots view cone + if(%obj.checkInFov(%target, %db.visionFov)) + { + // set the targetObject + %obj.targetObject = %target; + break; + } + } + + return SUCCESS; +} + +//============================================================================= +// aimAtTargetTask +//============================================================================= +function aimAtTargetTask::precondition(%this, %obj) +{ + // need to be alive and have a target + return isObject(%obj.targetObject) && %obj.isEnabled(); +} + +function aimAtTargetTask::behavior(%this, %obj) +{ + // calculate an aim offset + %targetPos = %obj.targetObject.getWorldBoxCenter(); + %weaponImage = %obj.getMountedImage($WeaponSlot); + %projectile = %weaponImage.projectile; + %correction = "0 0 1"; + if(isObject(%projectile)) + { + // simple target leading approximation (not for ballistics) + %targetDist = VectorDist(%targetPos, %obj.position); + %bulletVel = %projectile.muzzleVelocity; + %targetVel = %obj.targetObject.getVelocity(); + %correction = VectorAdd(%correction, VectorScale( %targetVel, (%targetDist / %bulletVel) )); + } + %obj.setAimObject(%obj.targetObject, %correction); + + return SUCCESS; +} + +//============================================================================= +// shootAtTargetTask +//============================================================================= +function shootAtTargetTask::precondition(%this, %obj) +{ + return isObject(%obj.targetObject) && + %obj.checkInLos(%obj.targetObject) && + VectorDot(VectorNormalize(VectorSub(%obj.getAimLocation(), %obj.position)), %obj.getForwardVector()) > 0.9 && + %obj.getImageAmmo($WeaponSlot); +} + +function shootAtTargetTask::behavior(%this, %obj) +{ + if(!isEventPending(%obj.triggerSchedule)) + { + %obj.setImageTrigger($WeaponSlot, true); + %burstLength = %obj.dataBlock.burstLength[%obj.getMountedImage($WeaponSlot).item.description]; + %obj.triggerSchedule = %obj.schedule(%burstLength, setImageTrigger, $WeaponSlot, false); + } + + return SUCCESS; +} + + +//============================================================================= +// combatMoveTask +//============================================================================= +function combatMoveTask::behavior(%this, %obj) +{ + %image = %obj.getMountedImage($WeaponSlot); + %db = %obj.getDatablock(); + %optimalRange = %db.optimalRange[%image.item.description]; + %currentRange = VectorDist(%obj.position, %obj.targetObject.position); + %rangeDelta = %currentRange - %optimalRange; + + %moveVec = "0 0 0"; + %fwd = %obj.getForwardVector(); + %right = %obj.getRightVector(); + + // forward / back to stay in range + if(mAbs(%rangeDelta) > %db.rangeTolerance) + %moveVec = VectorScale(%fwd, %rangeDelta); + + // random side strafe + %moveVec = VectorAdd(%moveVec, VectorScale(%right, 5 * (getRandom(0,2) - 1))); + + %obj.moveTo(VectorAdd(%obj.position, %moveVec)); + + return SUCCESS; +} diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTreeManager.cs b/Templates/Full/game/scripts/server/BadBehavior/behaviorTreeManager.cs new file mode 100644 index 0000000000..ca6798af87 --- /dev/null +++ b/Templates/Full/game/scripts/server/BadBehavior/behaviorTreeManager.cs @@ -0,0 +1,138 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +function BehaviorTreeManager::create() +{ + if(isObject(BehaviorTreeManager)) + BehaviorTreeManager.delete(); + + if(isObject(ServerGroup)) + pushInstantGroup(ServerGroup); + + new ScriptObject(BehaviorTreeManager); + + if(isObject(ServerGroup)) + popInstantGroup(); +} + +function BehaviorTreeManager::onAdd(%this) +{ + if(isObject(BehaviorTreeGroup)) + BehaviorTreeGroup.delete(); + + if(isObject(ActiveBehaviorTreeGroup)) + ActiveBehaviorTreeGroup.delete(); + + new SimGroup(BehaviorTreeGroup); + new SimGroup(ActiveBehaviorTreeGroup); + + %this.loadTrees(); +} + +function BehaviorTreeManager::onRemove(%this) +{ + if(isObject(BehaviorTreeGroup)) + BehaviorTreeGroup.delete(); + + if(isObject(ActiveBehaviorTreeGroup)) + ActiveBehaviorTreeGroup.delete(); +} + +function BehaviorTreeManager::loadTrees(%this) +{ + if(!isDirectory("./BehaviorTrees")) + return; + + pushInstantGroup(BehaviorTreeGroup); + + %pattern = "./BehaviorTrees/*.cs"; + %file = findFirstFile( %pattern ); + if ( %file $= "" ) + { + // Try for DSOs next. + %pattern = "./BehaviorTrees/*.cs.dso"; + %file = findFirstFile( %pattern ); + } + + while( %file !$= "" ) + { + exec( %file ); + %file = findNextFile( %pattern ); + } + + popInstantGroup(); +} + +function BehaviorTreeManager::createTree(%this, %obj, %tree) +{ + if(!isObject(%obj)) + { + error("BehaviorTreeManager::assignTree - object does not exist"); + return -1; + } + + if(!BehaviorTreeGroup.isMember(%tree)) + { + error("BehaviorTreeManager::assignTree - tree is not a member of BehaviorTreeGroup"); + return -1; + } + + pushInstantGroup(ActiveBehaviorTreeGroup); + %behaviorTree = new BehaviorTreeRunner() { + rootNode = %tree; + ownerObject = %obj; + }; + popInstantGroup(); + + return %behaviorTree; +} + +function BehaviorTreeManager::onBehaviorTreeEditor(%this, %val) +{ + if(%val) + onBehaviorTreeEditorStart(); + else + onBehaviorTreeEditorStop(); +} + +// give an object a behavior tree +function SimObject::setBehavior(%this, %tree, %frequency) +{ + if(isObject(%this.behaviorTree)) + %this.behaviorTree.rootNode = %tree; + else + %this.behaviorTree = BehaviorTreeManager.createTree(%this, %tree); + + if(%frequency) + %this.behaviorTree.frequency = %frequency; +} + + +// stop running a behavior tree on an object +function SimObject::clearBehavior(%this) +{ + if(isObject(%this.behaviorTree)) + %this.behaviorTree.clear(); +} + +BehaviorTreeManager::create(); + diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/FollowTree.cs b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/FollowTree.cs new file mode 100644 index 0000000000..9bda795745 --- /dev/null +++ b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/FollowTree.cs @@ -0,0 +1,26 @@ +//--- OBJECT WRITE BEGIN --- +new Root(FollowTree) { + canSave = "1"; + canSaveDynamicFields = "1"; + + new Sequence() { + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptEval() { + behaviorScript = "return isObject(%obj.followObject) ? SUCCESS : FAILURE;\n"; + defaultReturnStatus = "SUCCESS"; + internalName = "has object to follow?"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + + new FollowBehaviorAction() { + preconditionMode = "TICK"; + internalName = "follow the object"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/PatrolTree.cs b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/PatrolTree.cs new file mode 100644 index 0000000000..05eb73ea6c --- /dev/null +++ b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/PatrolTree.cs @@ -0,0 +1,50 @@ +//--- OBJECT WRITE BEGIN --- +new Root(PatrolTree) { + canSave = "1"; + canSaveDynamicFields = "1"; + + new Sequence() { + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptedBehavior() { + preconditionMode = "ONCE"; + internalName = "Move to closest node on path"; + class = "moveToClosestNodeTask"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new WaitForSignal() { + signalName = "onReachDestination"; + internalName = "onReachDestination"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new Loop() { + numLoops = "0"; + terminationPolicy = "ON_FAILURE"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new Sequence() { + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptedBehavior() { + preconditionMode = "ONCE"; + internalName = "move along path"; + class = "patrolTask"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new WaitForSignal() { + signalName = "onReachDestination"; + internalName = "onReachDestination"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/WanderTree.cs b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/WanderTree.cs new file mode 100644 index 0000000000..ab3330fdbe --- /dev/null +++ b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/WanderTree.cs @@ -0,0 +1,33 @@ +//--- OBJECT WRITE BEGIN --- +new Root(WanderTree) { + canSave = "1"; + canSaveDynamicFields = "1"; + + new Sequence() { + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptedBehavior() { + preconditionMode = "ONCE"; + internalName = "move somewhere"; + class = "wanderTask"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new WaitForSignal() { + signalName = "onReachDestination"; + timeoutMs = "0"; + internalName = "wait until there"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new RandomWait() { + waitMinMs = "0"; + waitMaxMs = "1000"; + internalName = "pause"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/botMatchTree.cs b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/botMatchTree.cs new file mode 100644 index 0000000000..7e8dd53823 --- /dev/null +++ b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/botMatchTree.cs @@ -0,0 +1,142 @@ +//--- OBJECT WRITE BEGIN --- +new Root(botMatchTree) { + canSave = "1"; + canSaveDynamicFields = "1"; + + new Selector() { + canSave = "1"; + canSaveDynamicFields = "1"; + + new Sequence() { + internalName = "run the match"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptEval() { + behaviorScript = "if(isObject(%obj.botGroup))\n %obj.botGroup.delete();\n\n%obj.botGroup = new SimGroup();\n%obj.countdown = 5;\n%obj.numBotsToSpawn = %obj.numBots;"; + defaultReturnStatus = "SUCCESS"; + internalName = "init"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new Sequence() { + internalName = "countdown"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new Ticker() { + frequencyMs = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new Loop() { + numLoops = "5"; + terminationPolicy = "ON_FAILURE"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptEval() { + behaviorScript = "centerPrintAll(\"BotMatch in\" SPC %obj.countdown, 2);\n%obj.countdown --;"; + defaultReturnStatus = "SUCCESS"; + internalName = "countdown message"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + }; + new ScriptEval() { + behaviorScript = "centerPrintAll(\"GO!\",1);"; + defaultReturnStatus = "SUCCESS"; + internalName = "lets go"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + new SucceedAlways() { + canSave = "1"; + canSaveDynamicFields = "1"; + + new Loop() { + numLoops = "0"; + terminationPolicy = "ON_FAILURE"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new Sequence() { + internalName = "spawn bots"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptEval() { + behaviorScript = "// pick a marker to spawn at\n%spawnpoint = PatrolPath.getRandom();\n\n// create the bot\n%bot = BadBot::spawn(\"\", %spawnpoint);\n\n// set its behavior\n%bot.setbehavior(BotTree, $BotTickFrequency);\n\n// add it to the botgroup\n%obj.botGroup.add(%bot);\n\n// keep track of the current bot\n%obj.currentBot = %bot;\n\n// decrement the number of bots left to spawn\n%obj.numBotsToSpawn --;"; + defaultReturnStatus = "SUCCESS"; + internalName = "spawn one bot"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new RandomSelector() { + internalName = "pick a weapon"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptEval() { + behaviorScript = "%obj.currentBot.use(Ryder);"; + defaultReturnStatus = "SUCCESS"; + internalName = "Ryder"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new ScriptEval() { + behaviorScript = "%obj.currentBot.use(Lurker);"; + defaultReturnStatus = "SUCCESS"; + internalName = "Lurker"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + new ScriptEval() { + behaviorScript = "if(%obj.numBotsToSpawn == 0) return FAILURE;"; + defaultReturnStatus = "SUCCESS"; + internalName = "check if more bots to spawn"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + }; + }; + new FailAlways() { + canSave = "1"; + canSaveDynamicFields = "1"; + + new WaitForSignal() { + signalName = "onBotmatchCancel"; + timeoutMs = "0"; + internalName = "stop on cancel signal"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + }; + new Sequence() { + internalName = "end the match"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptEval() { + behaviorScript = "%obj.botGroup.delete();"; + defaultReturnStatus = "SUCCESS"; + internalName = "remove bots"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new ScriptEval() { + behaviorScript = "%obj.behaviorTree.schedule(10, stop);"; + defaultReturnStatus = "SUCCESS"; + internalName = "stop"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/botTree.cs b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/botTree.cs new file mode 100644 index 0000000000..b48967718e --- /dev/null +++ b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/botTree.cs @@ -0,0 +1,70 @@ +//--- OBJECT WRITE BEGIN --- +new Root(BotTree) { + canSave = "1"; + canSaveDynamicFields = "1"; + + new Parallel() { + returnPolicy = "REQUIRE_ALL"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new Ticker() { + frequencyMs = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new Loop() { + numLoops = "0"; + terminationPolicy = "ON_FAILURE"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptedBehavior() { + preconditionMode = "ONCE"; + internalName = "look for enemy"; + class = "pickTargetTask"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + }; + new Loop() { + numLoops = "0"; + terminationPolicy = "ON_FAILURE"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new ActiveSelector() { + recheckFrequency = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new SubTree() { + subTreeName = "getHealthTree"; + internalName = "get health"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new SubTree() { + subTreeName = "combatTree"; + internalName = "combat"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new SubTree() { + subTreeName = "PatrolTree"; + internalName = "patrol"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new SubTree() { + subTreeName = "WanderTree"; + internalName = "wander"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/combatTree.cs b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/combatTree.cs new file mode 100644 index 0000000000..81678e39f4 --- /dev/null +++ b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/combatTree.cs @@ -0,0 +1,99 @@ +//--- OBJECT WRITE BEGIN --- +new Root(CombatTree) { + canSave = "1"; + canSaveDynamicFields = "1"; + + new Sequence() { + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptEval() { + behaviorScript = "if (isObject(%obj.targetObject) && %obj.targetObject.isEnabled()) return SUCCESS;"; + defaultReturnStatus = "FAILURE"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new Parallel() { + returnPolicy = "REQUIRE_ALL"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new Loop() { + numLoops = "0"; + terminationPolicy = "ON_FAILURE"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new Sequence() { + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptedBehavior() { + preconditionMode = "ONCE"; + internalName = "combatMoveTask"; + class = "combatMoveTask"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new RandomWait() { + waitMinMs = "500"; + waitMaxMs = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + }; + new Loop() { + numLoops = "0"; + terminationPolicy = "ON_FAILURE"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new Ticker() { + frequencyMs = "500"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptedBehavior() { + preconditionMode = "TICK"; + internalName = "aim"; + class = "aimAtTargetTask"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + }; + new Loop() { + numLoops = "0"; + terminationPolicy = "ON_FAILURE"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new SucceedAlways() { + canSave = "1"; + canSaveDynamicFields = "1"; + + new Sequence() { + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptedBehavior() { + preconditionMode = "ONCE"; + internalName = "fire"; + class = "shootAtTargetTask"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new RandomWait() { + waitMinMs = "500"; + waitMaxMs = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/getHealthTree.cs b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/getHealthTree.cs new file mode 100644 index 0000000000..3dfca5b16e --- /dev/null +++ b/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/getHealthTree.cs @@ -0,0 +1,33 @@ +//--- OBJECT WRITE BEGIN --- +new Root(getHealthTree) { + canSave = "1"; + canSaveDynamicFields = "1"; + + new Sequence() { + canSave = "1"; + canSaveDynamicFields = "1"; + + new ScriptEval() { + behaviorScript = "if (%obj.getDamagePercent() > 0.75) return SUCCESS;"; + defaultReturnStatus = "FAILURE"; + internalName = "need health?"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new ScriptedBehavior() { + preconditionMode = "ONCE"; + internalName = "look for health"; + class = "findHealthTask"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new ScriptedBehavior() { + preconditionMode = "TICK"; + internalName = "get health"; + class = "getHealthTask"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/Full/game/scripts/server/BadBehavior/botMatch.cs b/Templates/Full/game/scripts/server/BadBehavior/botMatch.cs new file mode 100644 index 0000000000..951e6ad182 --- /dev/null +++ b/Templates/Full/game/scripts/server/BadBehavior/botMatch.cs @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +$BotMatchTickFrequency = 250; + +// start a bot match +function botMatch(%numBots) +{ + // Avoid having lots of dead bodies lying around. + $CorpseTimeoutValue = 2000; + + // script object to attach the BT to + if(!isObject(BotMatch)) + new ScriptObject(botMatch); + + // number of bots that will be spawned + botMatch.numBots = %numBots; + + // set the behavior tree + botMatch.setBehavior(botMatchTree, $BotMatchTickFrequency); +} + + +// cancel the match +function cancelBotmatch() +{ + // post the signal to the behavior tree + if(isObject(botMatch)) + botMatch.behaviorTree.postSignal("onBotmatchCancel"); +} + diff --git a/Templates/Full/game/scripts/server/BadBehavior/main.cs b/Templates/Full/game/scripts/server/BadBehavior/main.cs new file mode 100644 index 0000000000..85361f0f99 --- /dev/null +++ b/Templates/Full/game/scripts/server/BadBehavior/main.cs @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +exec("./behaviorTreeManager.cs"); +exec("./BadBot.cs"); +exec("./botMatch.cs"); \ No newline at end of file diff --git a/Templates/Full/game/tools/behaviorTreeEditor/gui/BTEditorCreatePrompt.ed.gui b/Templates/Full/game/tools/behaviorTreeEditor/gui/BTEditorCreatePrompt.ed.gui new file mode 100644 index 0000000000..ceefffe00b --- /dev/null +++ b/Templates/Full/game/tools/behaviorTreeEditor/gui/BTEditorCreatePrompt.ed.gui @@ -0,0 +1,217 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(BTEditorCreatePrompt,EditorGuiGroup) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + fixedAspectRatio = "0"; + + new GuiWindowCtrl() { + text = "Create New Tree"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "0"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + canCollapse = "0"; + closeCommand = "canvas.popDialog(DatablockEditorCreatePrompt);"; + edgeSnap = "0"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "389 252"; + extent = "207 145"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiWindowProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Your new tree must have a name"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "7 26"; + extent = "190 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "Name"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "7 45"; + extent = "191 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "CreateTreeName"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Create"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "7 114"; + extent = "122 22"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + Command = "BTEditor.createPromptNameCheck();"; + accelerator = "return"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "135 114"; + extent = "63 22"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + Command = "canvas.popDialog(BTEditorCreatePrompt);"; + accelerator = "escape"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Copy tree from"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "7 66"; + extent = "100 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "7 87"; + extent = "191 19"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "CopySourceDropdown"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/Full/game/tools/behaviorTreeEditor/gui/behaviorTreeEditor.ed.gui b/Templates/Full/game/tools/behaviorTreeEditor/gui/behaviorTreeEditor.ed.gui new file mode 100644 index 0000000000..5e9bbd4061 --- /dev/null +++ b/Templates/Full/game/tools/behaviorTreeEditor/gui/behaviorTreeEditor.ed.gui @@ -0,0 +1,321 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(BTEditor) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "height"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + lastContent = "1663"; + + new GuiControl() { + position = "0 0"; + extent = "1024 32"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "top bar"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiContainer(BTToolBar) { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "16000 32"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "menubarProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { + position = "5 0"; + extent = "695 32"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiPopUpMenuCtrl(BTEditorContentList) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "BotBehavior"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "8 7"; + extent = "145 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; + new GuiControl() { + position = "0 32"; + extent = "1024 719"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "height"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "middle bar"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiFrameSetCtrl() { + columns = "0 514"; + rows = "0"; + borderWidth = "4"; + borderColor = "48 32 48 32"; + borderEnable = "alwaysOn"; + borderMovable = "alwaysOn"; + autoBalance = "0"; + fudgeFactor = "0"; + docking = "None"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "1024 719"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "height"; + profile = "ToolsGuiFrameSetProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "510 719"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTabBookCtrl(BTEditorTabBook) { + tabPosition = "Top"; + tabMargin = "7"; + minTabWidth = "48"; + tabHeight = "20"; + allowReorder = "1"; + defaultPage = "0"; + selectedPage = "0"; + frontTabPadding = "0"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "510 719"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "height"; + profile = "ToolsGuiTabBookProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + }; + }; + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOn"; + vScrollBar = "alwaysOn"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "514 0"; + extent = "510 719"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiInspector(BehaviorTreeInspector) { + dividerMargin = "5"; + showCustomFields = "1"; + stackingType = "Vertical"; + horizStacking = "Left to Right"; + vertStacking = "Top to Bottom"; + padding = "1"; + dynamicSize = "1"; + dynamicNonStackExtent = "0"; + dynamicPos = "0"; + changeChildSizeToFit = "1"; + changeChildPosition = "1"; + position = "1 1"; + extent = "493 328"; + minExtent = "16 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiInspectorProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + }; + }; + }; + }; + new GuiControl() { + position = "0 751"; + extent = "1024 17"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "top"; + profile = "menubarProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "bottom bar"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl(BTEditorStatusBar) { + text = "Status"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "5 0"; + extent = "1019 17"; + minExtent = "64 17"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/BehaviorTreeView.png b/Templates/Full/game/tools/behaviorTreeEditor/images/BehaviorTreeView.png new file mode 100644 index 0000000000000000000000000000000000000000..8315d7137d1d44dcb99211db49e7b27267f5b8d7 GIT binary patch literal 923 zcmV;M17!S(P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyh_ z2{0B?GHm7m00SFIL_t(|+U=N4Yg17WhTnJQ+(1niLW|f!DJT@&~_00G6VoRoz4pY-GzmPzlSU@FL#%gmb}Cc8F}%-ZJ&QT^p17N zr9e8J&eG!I;>aEALyk+ytK*RCF@ol0-EMbeh_}5%9CBkHUS6H+Ch8|opP#zv?dzAP zi~8~Wvp)jfy?J$Z+i!gSbnLbDwUvu(`+<(lIT_Z*oSU7nGe5i@&p+d^n;Tx4oD6%1b=lY$vMVT(;l(`2Lvv z&u{y1k4^Kt?R~a)h}+&FZhN=A&-ShYzsYgP&AiE3Ut2K%oPGG{HrRgfI9EE`^Q46O zZ11-B61(l)_HO&ef55rf37_rV_Fm%u`zELM$-M_|d!Oxn@N?U{?cMfnd!OxH1#Ww< zjzb*c5Qp4M+cUqe0D_qXk`;i8WXw~P4VzC0K=MXyK$tf$1DQdo2?}{bFte7KwNpxW z@&^4K+Tq4ZMD|GbNcMAdRzt+hS~14S7~}mI<75VDBjP9`d#bvvs$W%gn`8nIQJ>k& z+A+qde!u^yEXx@)Yd1nB03q2ciejVHYEeY?&8!3vNiqP?l+sv?ak4DSnX0O0%}hk3 zN3w5bt&~zbe=}6|o2qV#$j>7=0Z^or#>}jp0lOiDO%d5mDUDZEHI-5t&p8_dP}D4gwBi=z0Tsy||kcLAWPs_A@0H8U|Y=A<7fF#sfzeG%DJ)gL1A9ROmC zyCnM=()|39KB;RiIS1o)1Tn_D`6w-t2Lqo!gpjmGxg@33uBvLPj-Zi~wutm{MH0y) z&1L|~MkA%XaeA;iBzq#V2cR76*eD|Vs=8AY#RdR0`XrG&$mQ-8MX{l(J0h}QH(Gt1 xiexW@@CAUW>efje8$#I35!Vp&OPE^^`2|!4{R`sCrAq(+002ovPDHLkV1hW!w<7=m literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Randomwait.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Randomwait.png new file mode 100644 index 0000000000000000000000000000000000000000..460f61353752d65f61f52f4cebfd6b9f461732be GIT binary patch literal 278 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPqw=j>C*>b^-oj{?*o-U3d7N?H}-{w86z_Z6$QmMLx zz3c^(oU&xQ>@Mc&67KKJw}hmFOn;v6^hl_A&fd1F%XY#M{rTCB40lC5troAkG|hZ# z`!#W^eiw7c=1v0@@%Ei}%sKR&RP|RUL>_&+*2CiV5zdBLzy3Qh9Jm!LCwgEakKLR( zN-jt29#&js>*6Tta$WE}q56q=QP+LPBH>fZ`_#&lB~lu0|9zNa8$a*wD*cRO5q!yv Si2*?8GI+ZBxvX zJ`R&*BMx+Q8EGHja9MPY&m=@dWF`aWM%JgxJ!E#YeeSzA-Di68RXaBt z|JP-l>*D$F%(6M>oz^Q}_{Lds@r=q!<&QnOjjax%jdSYa4ITMywy~RdSEQeCTD7V1 zbk|;fp>qr~XVx)RH!W#h8MbNHDa$#}dHn;Ucsw)%7|&U*ozA~vwM@hBQt4+0a_-01 zJ)6TUl_S37?#G`K&E#kO@XDLc%aD50Y!6ez(N|Yiu2>zD`~I=@F^zwrv#0EfSKZ2A ZsDHexLfBxnxF67$44$rjF6*2UngDd~fbIYQ literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/behavior.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/behavior.png new file mode 100644 index 0000000000000000000000000000000000000000..c391b6ad0ff0fb5cfda730c03b8d11d2c0365db0 GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP~BP)lv_57I+F9C(-d%8G=Se!l@e4F>M0?!_2$tK%f z%+@80>yPo>Y_z>`=&wR3$Fa^?HCKc@W?1jHSh32&c+!qTf39;RL@9Yb4hijhtNVB1 zai_)NUnZFFNX8T~A1S*ZX&`B{Nm#{3_7Rgq`@i~43=OwSD$N<*g?xD0GD+Aa`bQ{ug6}i}QgO%^QH; PU|{fc^>bP0l+XkKleJ_c literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/failAlways.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/failAlways.png new file mode 100644 index 0000000000000000000000000000000000000000..01f998448f8ecc67fbf6f75bd7e3fd196ea0b2da GIT binary patch literal 364 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPqw~(NqdXu=?F`&>-PZ!4!i_=>t+xi_b5NX-ZDU;0o z?f_>-5xbVv;76K`s3#_v%fz+ zVP-#5}ET%`Q2xhl(%J>nZ&SaX_Ts@{Nh6!?cFymY1yq~s;lLH$;Z*; zkocl=CzX$N*?3G{dG4g`k1fZ27@kX36j_GHd}`Km*v6W%WXfdke-Uk7!GFE^uWU8- zUmyN*U*@c*3Q>Yi9#4MHHh*zTs^M^6@Tn(TlGgL>=2c`_@O)YOmmh!T&G`Lx*59(V zCq-C3eEoUw@Lkr$LTtPD)hgP%&)>c6nvmQ6A9H1whiu6|(K+>M=3!89GB9|$`njxg HN@xNAFE5lk literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/inverter.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/inverter.png new file mode 100644 index 0000000000000000000000000000000000000000..88578b2b855bcdf9de111a26d36654e50063becf GIT binary patch literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4Zm| zh9?j9to#gQaTa()7Bet#3xhBt!>l!1zPiy-28V4^rY>1<&+5zJdy_n? z?x>lXytc`_+--13J>>b5j)}@5Cz-C#uwhv4a-+DF>65m~>iOL)g>yXE8YXcxOzakp z>^wN-pRI91u;`8&o8O-|=}dQ>aaiK<$@aVI7eZJW7VMrEG`Ht;+Pih?63GlaG5?kK z?yId#iQL70(Y)*K0-AbW|YuPgf-_trZlsHdd8h7K ztMa5(9V|aOUYT1a_*@j_tT_<&{Mx<~k~=T6d`Vt)dctP!N4okYnxEdAOk~+;$oAf0 zme%2aCYOJ2(z&c;)N1H<^7T>Pj8!}g1-8#$wpMLi|6Jyn1cUrKyCruYt~=ws_u$fd z9lg>=>TKn2tUJcxR$v-0_wf5S>DgM7Mcml_oKu-uW07xhcaN~E?I$gu&lx;j{an^L HB{Ts5JcN*1 literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/monitor.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/monitor.png new file mode 100644 index 0000000000000000000000000000000000000000..83914b53aac389e90139f98ff91465ea4dfa9478 GIT binary patch literal 367 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*x37`TN&n2}-D90{Nxdx@v7EBhUGW`1dD#Z3;5K%w`ZE{-7e>G-Z}m3ohg}d}{+sVuul3}OYzcCjbCjqhRWXF0}- z?y0-8^z$a2=e}E1S|&VL{@Z`WYNm$6d6QQ^DL*=oe>blp%YxL)?O#5X`8=uHowh$V z+r)wKPfh*DKNW`;wCaWNzyI?6uP5Q|jdtof6FuOb3Jj3RAb&D#AGj8ep0RMQ3BVz zWwK_42gLYR#V=gHXjT*Z0_oR}pVsU)f17aee(%=Tzw72Ti3_-I&0x-`Ts2AOtGAJH guublRRAw87FMB=L>duT`0(3Bgr>mdKI;Vst09FWRAOHXW literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/randomSelector.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/randomSelector.png new file mode 100644 index 0000000000000000000000000000000000000000..f2c366c60f529f2b02d051f4b6d2e7cd1101487f GIT binary patch literal 354 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rl| zd|+T`d(5v46yYrJh%9Dc;1&j9Muu5)B!GhKC7!;n?048%g_#-i=BTU!3O(|4aSX9I zeKdGC?;!`CTK!WD=1kTGjJ{%=cA|f)Afz!~*$}9K|`*Q(~B2Cy-|b?Q|y_Dt$wNn+$()U$R=3U8lp zn%r*{VUc@|;ZG)F|@HQ<55y2=sZ|`Rb`_Cr4Q+T~EyTC&KkVedG&t)PP z81Gf*h4C+VFU4@+@2<<$3Pv^aLO$PP=D4xP)^mS#w!_LCTE!B`@zyTE6>*?YcVsU!w z;;(5MpfjcFUw`S)FOCHrFxE zKT95*;Izy&*c=>qU+4X6%d{DBE=*dOCvQctc cc>PuJ75WJV=h!BR0iDg@>FVdQ&MBb@0H`}|rvLx| literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptFunc.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptFunc.png new file mode 100644 index 0000000000000000000000000000000000000000..e2e2cac37a6bdb9f545ba5f3dada2e29804a940b GIT binary patch literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgfSn=^#NYh2_kjD8W@Ss)6|Z)R=k7Ev>J1K^80=!Jwrhhy-Q77wUC)(xrB5Z@dlo0- m$6ArM{-F8cPuG|KV%t{jz2{&2&19gX89ZJ6T-G@yGywn*6mEt9 literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptedBehavior.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptedBehavior.png new file mode 100644 index 0000000000000000000000000000000000000000..64a3ada632dc8838e6f1d8a03d01f1d415aea253 GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPqx1=zSuBY~g)j*+5o-U3d7N?I+KF!?Rtm$!LUC1)+! zSn;aynydY>k5c}-ovz#|e>Nw>oqgjbvrT$I(bC&gJ0E`AA~l7QL70(Y)*K0-AbW|YuPgf=bP&}xk10`re3i-vX>#Nw`njxgN@xNAS`>qt literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/sequence.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/sequence.png new file mode 100644 index 0000000000000000000000000000000000000000..d7006db7b5abbe68778ebebb08a9c032328e8d1c GIT binary patch literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*x37`TN&n2}-D90{Nxdx@v7EBhTbAx>7cn#*D3K%se_E{-7OFsLs~>&5;%cgA?DhVAnk*Bh>ozRrU+{B<*C!LbliNhyr6p<` Z)NSUPuW~=j&kb}bgQu&X%Q~loCICM^WJUl0 literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/subTree.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/subTree.png new file mode 100644 index 0000000000000000000000000000000000000000..18e3f4c3f1d825548f81a607668a4ecd0c801e6a GIT binary patch literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgf8Z9J{an^LB{Ts5y}O)> literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/succeedAlways.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/succeedAlways.png new file mode 100644 index 0000000000000000000000000000000000000000..da6ed8621f28a8f07c3ffdf2de6b782c27b73e69 GIT binary patch literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4Zm| zh9?j9to#gQaTa()7Bet#3xhBt!>l{!J)*WZ|y&iFbD88?9s1c$PLb<&InHHf#5Y zpjEr)MMZ_k-SqJ_Suy`b6(@fOZ{=jA;Mg|n2{X#y9oTs`vSLoRAD6>6R*jWUy0{G< zx5o5rWMW>i(mU>6e$c*EtA562ICCf->@SzUa7C42`Qf}%C*O;fms-8yP-KvuZMWq4 z^DhBe!|+`b<_-^Mn^P-0>K?ce5~iff)N(=!cUZ2#w6Uw)=h9fOnq^d0}w SJT?HsfWgz%&t;ucLK6TH`jwaUKmMhnMg&>!Qa~L@Vd0WfFUT>Xy z!QfJs?c$mL|DRctnigZ^8FTulys>fmw$)slo_u+GG$Nlb=ov$Z_g*ifHT^t)9~@8e z3}*fL_DtaK0(aHavsWZf%iUEy@y24#&9jV?;u}IP7Pgu6f71Q&ai)a_>xnZg9Bb8d zpGJFrRP-0WJ;Nh@M&qt+&%Qp^W1Y3EP@w1bmbgNuwH%5n&zAr8e{oBy;c=by`Xl}+ zcJH>g2sA7-vt2TM`}&Z|8@U#5%rey~&p)4^KTkgYnC_0X^7l^umDs#)?ba!a?f;w; d+isG*zi!I!)MJ(hDu5xx;OXk;vd$@?2>{!Wo0R|n literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/wait.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/wait.png new file mode 100644 index 0000000000000000000000000000000000000000..66b01e21808a777e7f40e9f7d29436ac8af21579 GIT binary patch literal 281 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPqw~(MELzROqJ5Xqur;B5V#p$EL7X=S1aMYeZEpSuW zdWW-kf@%2+zIPLKU$A?ccD=k>H^tiafVvIi%2hjrC;e#oxn7{bdt%D-kkD=Cqq7zt zbGjaQ!TZvM6p1*q16j}cwpwl0W&ZXnaW_v|3gd#azxPjKXmBsJlx8sIIcKf7oI_Bv z_~71%obbSNR#SEUVelF{r5}E)=OK4*N literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/waitForSignal.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/waitForSignal.png new file mode 100644 index 0000000000000000000000000000000000000000..b3a4fdada3dd42f5640accc6fb81444d96823166 GIT binary patch literal 334 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP~qX0jf1kZY=89<@io-U3d7N?g^e%O0hfv0tUyOqoH z9bV>6Wjk1Z%$Hswt887;;`LJI?xVZ>3Mw((Q@(k!HRq<-OXpmkD){}4qmV7*u5S)) z?mo>$*Ee$>IQK1H^=XdhFZq=bcAb?zujD#846YU#{Ya;`POcj?2Aha=P7UdypY<9$Qk+R_)HGxAujdkL@rt z@@LMBygEgFjnO`_Nqc=B+VIN6rFO{GRh=(e6KeEhS8VNnIWZ$Y`|D@rFD(6)_s9P4 ax%yA84>m3Jvw8#cCWEJ|pUXO@geCw9#DydP literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/main.cs b/Templates/Full/game/tools/behaviorTreeEditor/main.cs new file mode 100644 index 0000000000..1f341789ea --- /dev/null +++ b/Templates/Full/game/tools/behaviorTreeEditor/main.cs @@ -0,0 +1,31 @@ +function reloadBTE() +{ + if(isObject(BehaviorTreeEditorGui)) + BehaviorTreeEditorGui.delete(); + + initializeBehaviorTreeEditor(); +} + +function initializeBehaviorTreeEditor() +{ + echo( " % - Initializing Behavior Tree Editor" ); + + // exec the scripts and gui + exec("./scripts/behaviorTreeEditorProfiles.ed.cs"); + exec("./gui/behaviorTreeEditor.ed.gui"); + exec("./gui/BTEditorCreatePrompt.ed.gui"); + exec("./scripts/guiBehaviorTreeViewCtrl.ed.cs"); + exec("./scripts/behaviorTreeEditorCanvas.ed.cs"); + exec("./scripts/behaviorTreeEditor.ed.cs"); + exec("./scripts/behaviorTreeEditorUndo.ed.cs"); + exec("./scripts/behaviorTreeEditorStatusBar.ed.cs"); + exec("./scripts/behaviorTreeEditorContentList.ed.cs"); + exec("./scripts/behaviorTreeEditorInspector.ed.cs"); + + // register the class icons + EditorIconRegistry::loadFromPath("tools/behaviorTreeEditor/images/classIcons/"); +} + +function destroyBehaviorTreeEditor() +{ +} diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditor.ed.cs b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditor.ed.cs new file mode 100644 index 0000000000..1b9683fe4f --- /dev/null +++ b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditor.ed.cs @@ -0,0 +1,390 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +$inBehaviorTreeEditor = false; + +//============================================================================== +// INIT +//============================================================================== +function BTEdit() +{ + if (!$InBehaviorTreeEditor) + { + if(!isObject(BTEditCanvas)) + new GuiControl(BTEditCanvas, EditorGuiGroup); + + BTEditor.startUp(Canvas.getContent()); + + $InBehaviorTreeEditor = true; + BehaviorTreeManager.onBehaviorTreeEditor(true); + } + else + { + BTEditCanvas.quit(); + } +} + +function toggleBehaviorTreeEditor( %make ) +{ + if( %make ) + { + BTEdit(); + cancel($Game::Schedule); + } +} + +GlobalActionMap.bind( keyboard, "f9", toggleBehaviorTreeEditor ); + + +function BTEditor::startUp(%this, %content) +{ + %this.lastContent=%content; + Canvas.setContent( BTEditor ); + + if(!isObject(BehaviorTreeManager)) + // This isn't pretty, but we need to load up existing trees + exec("scripts/server/BadBehavior/behaviorTreeManager.cs"); + + if(BehaviorTreeGroup.getCount() == 0) + { + %this.createTree(); + } + else + { + BTEditorContentList.refresh(); + if(BTEditorTabBook.getCount() == 0) + BTEditorContentList.setFirstSelected(); + } + + %this.updateUndoMenu(); + %this.updateNodeTypes(); +} + + +function BTEditor::updateNodeTypes(%this) +{ + if(isObject(BTNodeTypes)) + BTNodeTypes.delete(); + + new SimSet(BTNodeTypes); + %set = new SimSet() { + internalName = "Composite"; + }; + %set.add( new ScriptObject() { nodeType = "ActiveSelector"; } ); + %set.add( new ScriptObject() { nodeType = "Parallel"; } ); + %set.add( new ScriptObject() { nodeType = "RandomSelector"; } ); + %set.add( new ScriptObject() { nodeType = "Selector"; } ); + %set.add( new ScriptObject() { nodeType = "Sequence"; } ); + BTNodeTypes.add(%set); + + %set = new SimSet() { + internalName = "Decorator"; + }; + %set.add( new ScriptObject() { nodeType = "FailAlways"; }); + %set.add( new ScriptObject() { nodeType = "Inverter"; } ); + %set.add( new ScriptObject() { nodeType = "Loop"; } ); + %set.add( new ScriptObject() { nodeType = "Monitor"; } ); + %set.add( new ScriptObject() { nodeType = "SucceedAlways"; } ); + %set.add( new ScriptObject() { nodeType = "Ticker"; } ); + BTNodeTypes.add(%set); + + %set = new SimSet() { + internalName = "Leaf"; + }; + %set.add( new ScriptObject() { nodeType = "RandomWait"; } ); + %set.add( new ScriptObject() { nodeType = "ScriptedBehavior"; } ); + %set.add( new ScriptObject() { nodeType = "ScriptEval"; } ); + %set.add( new ScriptObject() { nodeType = "ScriptFunc"; } ); + %set.add( new ScriptObject() { nodeType = "SubTree"; } ); + %set.add( new ScriptObject() { nodeType = "Wait"; } ); + %set.add( new ScriptObject() { nodeType = "WaitForSignal"; } ); + BTNodeTypes.add(%set); +} + +function BTEditor::getBaseNodeType(%this, %type) +{ + foreach(%baseType in BTNodeTypes) + { + if(%baseType.internalName $= %type) // supplied basetype + return %type; + + foreach(%derivedType in %baseType) // supplied derived type + { + if(%derivedType.nodeType $= %type) + return %baseType.internalName; + } + } + return ""; +} + +function BTEditor::viewTree(%this, %tree) +{ + %viewPage = -1; + foreach(%page in BTEditorTabBook) + { + if(%page.rootNode == %tree) + { + %viewPage = %page; + break; + } + } + + if(isObject(%viewPage)) + { + %viewPage.select(); + } + else + { + %newPage = BTEditor::newPage(); + %newPage.setText(%tree.name); + %newPage.rootNode = %tree; + BTEditorTabBook.addGuiControl(%newPage); + %newPage-->BTView.open(%tree); + %newPage-->BTView.refresh(); + } + %this.updateUndoMenu(); +} + +function BTEditor::createTree(%this) +{ + %list = BTEditorCreatePrompt-->CopySourceDropdown; + %list.clear(); + foreach (%tree in BehaviorTreeGroup) + %list.add(%tree.getName(), %tree); + Canvas.pushDialog(BTEditorCreatePrompt); +} + +function BTEditor::getCurrentViewCtrl(%this) +{ + %pageId = BTEditorTabBook.getSelectedPage(); + if(%pageId >= 0) + return BTEditorTabBook.getObject(%pageId)-->BTView; + + return %pageId; +} + + +function BTEditor::getTreeRoot(%this, %node) +{ + %current = %node; + while(%current.getClassName() !$= "Root" && isObject(%current)) + { + %current = %current.getGroup(); + } + return %current; +} + +function BTEditor::getCurrentRootNode(%this) +{ + return %this.getCurrentViewCtrl().getRootNode(); +} + + +function BTEditor::createPromptNameCheck(%this) +{ + %name = BTEditorCreatePrompt-->CreateTreeName.getText(); + if( !Editor::validateObjectName( %name, true ) ) + return; + + // Fetch the copy source and clear the list. + + %copySource = BTEditorCreatePrompt-->copySourceDropdown.getText(); + BTEditorCreatePrompt-->copySourceDropdown.clear(); + + // Remove the dialog and create the tree. + + canvas.popDialog( BTEditorCreatePrompt ); + %this.createTreeFinish( %name, %copySource ); +} + +function BTEditor::createTreeFinish( %this, %name, %copySource ) +{ + %newTree = -1; + pushInstantGroup(BehaviorTreeGroup); + if(%copySource !$= "") + { + %newTree = %copySource.deepClone(); + } + else + { + %newTree = new Root(); + } + popInstantGroup(); + + %newTree.setName(%name); + %newTree.setFilename(""); + + BTEditorContentList.refresh(); + BTEditorContentList.setSelected(BTEditorContentList.findText(%newTree.name)); +} + +function BTEditor::saveTree(%this, %tree, %prompt) +{ + // check we actually have something to save + if(!isObject(%tree)) + return; + + if((%file = %tree.getFileName()) !$= "") + { + %path = filePath(%file); + } + else + { + %path = "scripts/server/BadBehavior/behaviorTrees"; + %file = %path @ "/" @ %tree.name; + + if(!isDirectory(%path)) + createPath(%path @ "/"); + + %prompt = true; + } + + if(%prompt || !isFile(%file)) + { + %dlg = new SaveFileDialog() + { + filters = "Torque script files (*.cs)|*.cs|"; + defaultPath = %path; + defaultFile = %file; + changePath = true; + overwritePrompt = true; + }; + + if(%dlg.execute()) + { + %file = %dlg.fileName; + %dlg.delete(); + } + else + { + return; + } + } + + %tree.save(%file); + %tree.setFileName(collapseFilename(%file)); + BTEditorStatusBar.setText("Saved '" @ %tree.name @ "' to file" SPC %tree.getFileName()); +} + +//============================================================================== +// VIEW +//============================================================================== + +function BTEditor::expandAll(%this) +{ + %this.getCurrentViewCtrl().expandAll(); +} + +function BTEditor::collapseAll(%this) +{ + %this.getCurrentViewCtrl().collapseAll(); +} + + +function BTEditorTabBook::onTabClose(%this, %index) +{ + if(%index == %this.selectedPage) + { + BehaviorTreeInspector.inspect(-1); + if(%this.getCount() > 1) + %this.getObject(0).select(); + else + BTEditor.ResetUndoMenu(); + } + + %this.getObject(%index).delete(); +} + +function BTEditorTabBook::onTabSelected(%this, %text, %index) +{ + //echo("onTabSelected" TAB %text); + BTEditor.updateUndoMenu(); + BehaviorTreeInspector.inspect(BTEditor.getCurrentViewCtrl().getSelectedObject()); +} + +function BTEditorTabBook::onTabRightClick(%this, %text, %index) +{ + if(isObject(BTEditorTabBookPopup)) + BTEditorTabBookPopup.delete(); + + %popup = new PopupMenu( BTEditorTabBookPopup ) + { + superClass = "MenuBuilder"; + isPopup = true; + item[ 0 ] = "Close" SPC %text SPC "tab" TAB "" TAB "BTEditorTabBook.onTabClose(" SPC %index SPC ");"; + }; + + %popup.showPopup( Canvas ); +} + +//============================================================================== +// UNDO +//============================================================================== +function BTEditor::getUndoManager( %this ) +{ + return %this.getCurrentViewCtrl().getUndoManager(); +} + + +function BTEditor::updateUndoMenu(%this) +{ + %uman = %this.getUndoManager(); + %nextUndo = %uman.getNextUndoName(); + %nextRedo = %uman.getNextRedoName(); + + %editMenu = BTEditCanvas.menuBar->editMenu; + + %editMenu.setItemName( 0, "Undo " @ %nextUndo ); + %editMenu.setItemName( 1, "Redo " @ %nextRedo ); + + %editMenu.enableItem( 0, %nextUndo !$= "" ); + %editMenu.enableItem( 1, %nextRedo !$= "" ); +} + +function BTEditor::ResetUndoMenu(%this) +{ + %editMenu = BTEditCanvas.menuBar->editMenu; + %editMenu.setItemName( 0, "Undo" ); + %editMenu.setItemName( 1, "Redo" ); + %editMenu.enableItem( 0, false ); + %editMenu.enableItem( 1, false ); +} + +function BTEditor::undo(%this) +{ + %action = %this.getUndoManager().getNextUndoName(); + + %this.getUndoManager().undo(); + %this.updateUndoMenu(); + + BTEditorStatusBar.print( "Undid '" @ %action @ "'" ); +} + +function BTEditor::redo(%this) +{ + %action = %this.getUndoManager().getNextRedoName(); + + %this.getUndoManager().redo(); + %this.updateUndoMenu(); + + BTEditorStatusBar.print( "Redid '" @ %action @ "'" ); +} \ No newline at end of file diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorCanvas.ed.cs b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorCanvas.ed.cs new file mode 100644 index 0000000000..93b45fe6e8 --- /dev/null +++ b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorCanvas.ed.cs @@ -0,0 +1,152 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +//============================================================================== +// INIT +//============================================================================== +function BTEditCanvas::onAdd( %this ) +{ + %this.onCreateMenu(); + + // close any invalid tab book pages + for( %i=0; %i < BTEditorTabBook.getCount(); %i++) + { + %page = BTEditorTabBook.getObject(%i); + if(!isObject(%page.rootNode) || %page.rootNode.getClassName() !$= "Root") + { + BTEditorTabBook.remove(%page); + %page.delete(); + %i--; + } + } +} + +function BTEditCanvas::onRemove( %this ) +{ + if( isObject( BehaviorTreeEditorGui.menuGroup ) ) + BehaviorTreeEditorGui.delete(); + + // cleanup + %this.onDestroyMenu(); + + //BTEditorTabBook.deleteAllObjects(); +} + +function BTEditCanvas::quit( %this ) +{ + // we must not delete a window while in its event handler, or we foul the event dispatch mechanism + %this.schedule(10, delete); + + Canvas.setContent(BTEditor.lastContent); + $InBehaviorTreeEditor = false; + BehaviorTreeManager.onBehaviorTreeEditor(false); +} + +//============================================================================== +// MENU +//============================================================================== +function BTEditCanvas::onCreateMenu(%this) +{ + if(isObject(%this.menuBar)) + return; + + //set up %cmdctrl variable so that it matches OS standards + if( $platform $= "macos" ) + { + %cmdCtrl = "cmd"; + %redoShortcut = "Cmd-Shift Z"; + } + else + { + %cmdCtrl = "Ctrl"; + %redoShortcut = "Ctrl Y"; + } + + // Menu bar + %this.menuBar = new MenuBar() + { + dynamicItemInsertPos = 3; + + new PopupMenu() + { + superClass = "MenuBuilder"; + barTitle = "File"; + internalName = "FileMenu"; + + item[0] = "New Tree..." TAB %cmdCtrl SPC "N" TAB "BTEditor.createTree();"; + item[1] = "Open..." TAB %cmdCtrl SPC "O" TAB %this @ ".open();"; + item[2] = "Save Tree" TAB %cmdCtrl SPC "S" TAB "BTEditor.saveTree( BTEditor.getCurrentRootNode(), false );"; + item[3] = "Save Tree As..." TAB %cmdCtrl @ "-Shift S" TAB "BTEditor.saveTree( BTEditor.getCurrentRootNode(), true );"; + item[4] = "-"; + item[5] = "Close Editor" TAB "F9" TAB %this @ ".quit();"; + item[6] = "Quit" TAB %cmdCtrl SPC "Q" TAB "quit();"; + }; + + new PopupMenu() + { + superClass = "MenuBuilder"; + barTitle = "Edit"; + internalName = "EditMenu"; + + item[0] = "Undo" TAB %cmdCtrl SPC "Z" TAB "BTEditor.undo();"; + item[1] = "Redo" TAB %redoShortcut TAB "BTEditor.redo();"; + item[2] = "-"; + item[3] = "Delete node" TAB "" TAB "BTEditor.getCurrentViewCtrl().deleteSelection();"; + item[4] = "Excise node" TAB "" TAB "BTEditor.getCurrentViewCtrl().exciseSelection();"; + }; + + new PopupMenu() + { + superClass = "MenuBuilder"; + barTitle = "View"; + internalName = "ViewMenu"; + + item[0] = "Expand All" TAB %cmdCtrl SPC "=" TAB "BTEditor.expandAll();"; + item[1] = "Collapse All" TAB %cmdCtrl SPC "-" TAB "BTEditor.collapseAll();"; + }; + + new PopupMenu() + { + superClass = "MenuBuilder"; + internalName = "HelpMenu"; + + barTitle = "Help"; + + Item[0] = "Help will arrive soon......"; + }; + }; + %this.menuBar.attachToCanvas(Canvas, 0); +} + +function BTEditCanvas::onDestroyMenu(%this) +{ + if( !isObject( %this.menuBar ) ) + return; + + // Destroy menus + while( %this.menuBar.getCount() != 0 ) + %this.menuBar.getObject( 0 ).delete(); + + %this.menuBar.removeFromCanvas(); + %this.menuBar.delete(); +} + diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorContentList.ed.cs b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorContentList.ed.cs new file mode 100644 index 0000000000..d59e37704b --- /dev/null +++ b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorContentList.ed.cs @@ -0,0 +1,133 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +function BTEditorContentList::refresh(%this) +{ + %this.clear(); + foreach(%tree in BehaviorTreeGroup) + %this.add(%tree.name, %tree); +} + +function BTEditorContentList::onSelect( %this, %tree ) +{ + BTEditor.viewTree( %tree ); +} + + + +function BTEditor::newPage() +{ + %page = new GuiTabPageCtrl() { + fitBook = "1"; + maxLength = "1024"; + docking = "client"; + margin = "-1 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0 "; + extent = "200 200"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiTabPageProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "0"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "alwaysOn"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "1 1"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "400 400"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "0"; + docking="client"; + canSaveDynamicFields = "0"; + + new GuiBehaviorTreeViewCtrl() { + internalName="BTView"; + tabSize = "17"; + textOffset = "8"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "0"; + mouseDragging = "1"; + multipleSelections = "0"; + deleteObjectAllowed = "1"; + dragToItemAllowed = "1"; + clearAllOnSingleSelection = "1"; + showRoot = "1"; + useInspectorTooltips = "0"; + tooltipOnWidthOnly = "0"; + showObjectIds = "0"; + showClassNames = "1"; + showObjectNames = "1"; + showInternalNames = "1"; + showClassNameForUnnamedObjects = "0"; + compareToObjectID = "1"; + canRenameObjects = "1"; + renameInternal = "0"; + position = "1 1"; + extent = "267 231"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiBehaviorTreeViewProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "0"; + canSaveDynamicFields = ""; + }; + }; + }; + return %page; +} \ No newline at end of file diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorInspector.ed.cs b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorInspector.ed.cs new file mode 100644 index 0000000000..7ae07a224b --- /dev/null +++ b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorInspector.ed.cs @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +//============================================================================== +// inspector field changed +//============================================================================== +function BehaviorTreeInspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ) +{ + //echo("onInspectorFieldModified" SPC %object SPC %fieldName SPC %arrayIndex SPC %oldValue SPC %newValue); + + // select the correct page + %treeRoot = BTEditor.getTreeRoot(%object); + %targetView = -1; + foreach(%page in BTEditorTabBook) + { + %view = %page-->BTView; + if(%view.getRootNode() == %treeRoot) + %targetView = %view; + } + + if(!isObject(%targetView)) + { + warn("OOPS - something went wrong with the inspector!"); + return; + } + + %action = BTInspectorUndoAction::create(%object, %fieldName, %arrayIndex, %oldValue, %newValue); + %action.addToManager( %targetView.getUndoManager() ); + + BTEditor.updateUndoMenu(); +} diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorProfiles.ed.cs b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorProfiles.ed.cs new file mode 100644 index 0000000000..80bee0fd12 --- /dev/null +++ b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorProfiles.ed.cs @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +singleton GuiControlProfile(GuiBehaviorTreeViewProfile : ToolsGuiTreeViewProfile) +{ + bitmap = "tools/behaviorTreeEditor/images/BehaviorTreeView.png"; +}; \ No newline at end of file diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorStatusBar.ed.cs b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorStatusBar.ed.cs new file mode 100644 index 0000000000..9ee9e78969 --- /dev/null +++ b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorStatusBar.ed.cs @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +function BTEditorStatusBar::print( %this, %message ) +{ + %this.setText( %message ); + + %sequenceNum = %this.sequenceNum + 1; + %this.sequenceNum = %sequenceNum; + + %this.schedule( 4 * 1000, "clearMessage", %sequenceNum ); +} + +//--------------------------------------------------------------------------------------------- + +function BTEditorStatusBar::clearMessage( %this, %sequenceNum ) +{ + // If we had no newer message in the meantime, clear + // out the current text. + + %this.setText( "" ); +} \ No newline at end of file diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorUndo.ed.cs b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorUndo.ed.cs new file mode 100644 index 0000000000..9cbbbb1a94 --- /dev/null +++ b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorUndo.ed.cs @@ -0,0 +1,174 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +function BTEditorUndoManager::onAddUndo( %this ) +{ + echo("Undo Added"); + BTEditor.updateUndoMenu(); +} + +//============================================================================== +// create a node +//============================================================================== +function BTCreateUndoAction::submit( %undoObject ) +{ + // The instant group will try to add our + // UndoAction if we don't disable it. + pushInstantGroup(); + + // Create the undo action. + %action = new MECreateUndoAction() + { + className = "BTCreateUndoAction"; + actionName = "Create " @ %undoObject.getClassName(); + }; + + // Restore the instant group. + popInstantGroup(); + + // Set the object to undo. + %action.addObject( %undoObject ); + + // Submit it. + %action.addToManager( BTEditor.getUndoManager() ); +} + +function BTCreateUndoAction::onUndone( %this ) +{ + //EWorldEditor.syncGui(); +} + +function BTCreateUndoAction::onRedone( %this ) +{ + //EWorldEditor.syncGui(); +} + + + +//============================================================================== +// delete a node +//============================================================================== + +/// A helper for submitting a delete undo action. +function BTDeleteUndoAction::submit( %deleteObject ) +{ + // The instant group will try to add our + // UndoAction if we don't disable it. + pushInstantGroup(); + + // Create the undo action. + %action = new BTDeleteUndoAction() + { + actionName = "delete node"; + }; + + // Restore the instant group. + popInstantGroup(); + + %action.deleteObject( %deleteObject ); + + // Submit it. + %action.addToManager( BTEditor.getUndoManager() ); +} + +function BTDeleteUndoAction::onUndone( %this ) +{ + // for some reason this doesn't restore the correct order + //BTEditor.getCurrentViewCtrl().buildVisibleTree(true); + // so re-open the tree instead + BTEditor.getCurrentViewCtrl().refresh(); +} + +function BTDeleteUndoAction::onRedone( %this ) +{ + BTEditor.getCurrentViewCtrl().buildVisibleTree(true); +} + +//============================================================================== +// reparent a node +//============================================================================== +function BTReparentUndoAction::create( %treeView ) +{ + pushInstantGroup(); + %action = new UndoScriptAction() + { + class = "BTReparentUndoAction"; + actionName = "move node"; + control = %treeView; + }; + popInstantGroup(); + + return %action; +} + +function BTReparentUndoAction::undo(%this) +{ + if(%this.newParent.isMember(%this.node)) + %this.newParent.remove(%this.node); + + %this.oldParent.add(%this.node); + + if(%this.oldPosition < %this.oldParent.getCount() - 1) + %this.oldParent.reorderChild(%this.node, %this.oldParent.getObject(%this.oldPosition)); + + %this.control.refresh(); +} + +function BTReparentUndoAction::redo(%this) +{ + if(%this.oldParent.isMember(%this.node)) + %this.oldParent.remove(%this.node); + + %this.newParent.add(%this.node); + + if(%this.newPosition < %this.newParent.getCount() - 1) + %this.newParent.reorderChild(%this.node, %this.newParent.getObject(%this.newPosition)); + + %this.control.refresh(); +} + +//============================================================================== +// Inspector field modified +//============================================================================== +function BTInspectorUndoAction::create(%object, %fieldName, %arrayIndex, %oldValue, %newValue ) +{ + %nameOrClass = %object.getName(); + if ( %nameOrClass $= "" ) + %nameOrClass = %object.getClassname(); + + pushInstantGroup(); + %action = new InspectorFieldUndoAction() + { + class = "BTInspectorUndoAction"; + actionName = %nameOrClass @ "." @ %fieldName @ " Change"; + + objectId = %object.getId(); + fieldName = %fieldName; + fieldValue = %oldValue; + arrayIndex = %arrayIndex; + + inspectorGui = BehaviorTreeInspector; + }; + popInstantGroup(); + + return %action; +} diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/guiBehaviorTreeViewCtrl.ed.cs b/Templates/Full/game/tools/behaviorTreeEditor/scripts/guiBehaviorTreeViewCtrl.ed.cs new file mode 100644 index 0000000000..63e5f23b53 --- /dev/null +++ b/Templates/Full/game/tools/behaviorTreeEditor/scripts/guiBehaviorTreeViewCtrl.ed.cs @@ -0,0 +1,433 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- + +function GuiBehaviorTreeViewCtrl::getRootNode(%this) +{ + %rootId = %this.getFirstRootItem(); + %rootObj = %rootId ? %this.getItemValue(%rootId) : -1; + return %rootObj; +} + +function GuiBehaviorTreeViewCtrl::getUndoManager( %this ) +{ + if( !isObject( %this.undoManager ) ) + { + %this.undoManager = new UndoManager(){ numLevels=20; }; + %this.undoManager.setClassNamespace("BTEditorUndoManager"); + } + + return %this.undoManager; +} + +function GuiBehaviorTreeViewCtrl::onRemove(%this) +{ + if(isObject(%this.undoManager)) + %this.undoManager.delete(); +} + +function GuiBehaviorTreeViewCtrl::expandAll(%this) +{ + for(%i=1; %i<=%this.getItemCount(); %i++) + { + %this.expandItem(%i); + %this.buildVisibleTree(); + } +} + +function GuiBehaviorTreeViewCtrl::collapseAll(%this) +{ + for(%i=1; %i<=%this.getItemCount(); %i++) + %this.expandItem(%i, false); + %this.buildVisibleTree(); +} + +function GuiBehaviorTreeViewCtrl::refresh(%this) +{ + %root = %this.getRootNode(); + %this.open(%root); + %this.expandAll(); + %this.selectItem(1); +} + +//============================================================================== +// DELETE +//============================================================================== +// onDeleteSelection is called prior to deleting the selected object. +function GuiBehaviorTreeViewCtrl::onDeleteSelection(%this) +{ + if(%this.getSelectedItem() > 1) // not root + BTDeleteUndoAction::submit(%this.getSelectedObject()); + + %this.clearSelection(); + + BTEditorStatusBar.print( "Node deleted" ); +} + +//============================================================================== +// SELECT +//============================================================================== +function GuiBehaviorTreeViewCtrl::onSelect(%this, %item) +{ + BehaviorTreeInspector.inspect(%item); +} + +function GuiBehaviorTreeViewCtrl::onBeginReparenting(%this) +{ + if( isObject( %this.reparentUndoAction ) ) + %this.reparentUndoAction.delete(); + + %action = BTReparentUndoAction::create( %this ); + %action.node = %this.getSelectedObject(); + %parent = %action.node.getGroup(); + %action.oldPosition = 0; + + if(isObject(%parent)) + %action.oldPosition = %parent.getObjectIndex(%action.node); + + %this.reparentUndoAction = %action; +} + +function GuiBehaviorTreeViewCtrl::onReparent(%this, %item, %old, %new) +{ + if( !isObject(%this.reparentUndoAction) || + %this.reparentUndoAction.node != %item ) + { + warn( "Reparenting undo is borked :(" ); + if(isObject(%this.reparentUndoAction)) + { + %this.reparentUndoAction.delete(); + %this.reparentUndoAction=""; + } + } + else + { + %this.reparentUndoAction.oldParent = %old; + %this.reparentUndoAction.newParent = %new; + %this.reparentUndoAction.newPosition = %new.getObjectIndex(%item); + } +} + +function GuiBehaviorTreeViewCtrl::onEndReparenting( %this ) +{ + %action = %this.reparentUndoAction; + %this.reparentUndoAction = ""; + + // Check that the reparenting went as planned, and undo it right now if not + if(%action.node.getGroup() != %action.newParent) + { + %action.undo(); + %action.delete(); + } + else + { + %action.addToManager( %this.getUndoManager() ); + BTEditorStatusBar.print( "Moved node" ); + } +} + +function GuiBehaviorTreeViewCtrl::canAdd(%this, %obj, %target) +{ + if(!isObject(%target)) + return false; + + if( !%target.isMemberOfClass( "SimGroup" ) ) + return false; + + return %target.acceptsAsChild(%obj); +} + +function GuiBehaviorTreeViewCtrl::isValidDragTarget(%this, %id, %obj) +{ + %selObj = %this.getSelectedObject(); + + if(!%selObj) + return false; + + return %this.canAdd(%selObj, %obj); +} + +function GuiBehaviorTreeViewCtrl::onMouseUp(%this, %itemid, %count) +{ + %this.mouseDragging=true; +} + +function GuiBehaviorTreeViewCtrl::onRightMouseDown(%this, %item, %pos, %obj) +{ + //echo("onRightMouseDown" TAB %item TAB %pos TAB %obj); + + %selObj = %this.getSelectedObject(); + if(!isObject(%selObj)) + return; + + %this.mouseDragging=false; + + if(isObject(BTEditPopup)) + BTEditPopup.delete(); + + // construct the popup + %popup = new PopupMenu(BTEditPopup) { + superClass = "MenuBuilder"; + isPopup = true; + }; + + // current node + %popup.addItem(0, %selObj.getClassName() SPC "[" @ %selObj.internalName @ "]" TAB "" TAB ""); + %popup.enableItem(0, false); + + // divider + %popup.addItem(1, "-"); + + // add node submenu + %popup.addItem(2, "Add node" TAB %this.makePopup(false, %selObj) TAB ""); + %popup.enableItem(2, %this.validate(false, %selObj)); + + // insert node submenu + %popup.addItem(3, "Insert node" TAB %this.makePopup(true, %selObj) TAB ""); + %popup.enableItem(3, %this.validate(true, %selObj)); + + // divider + %popup.addItem(4, "-"); + + // delete node + %popup.addItem(5, "Delete node" TAB "" TAB %this @ ".deleteSelection();"); + + // excise node (delete single node, reparenting children) + %popup.addItem(6, "Excise node" TAB "" TAB %this @ ".exciseSelection();"); + %popup.enableItem(6, (%this.getSelectedObject().getCount() == 1) && + (%this.getSelectedObject() != %this.getRootNode()) ); + + BTEditPopup.showPopup( Canvas ); +} + +function GuiBehaviorTreeViewCtrl::onRightMouseUp(%this, %item, %pos, %obj) +{ + //echo("onRightMouseUp" TAB %item TAB %pos TAB %obj); +} + +function GuiBehaviorTreeViewCtrl::makePopup(%this, %insert, %selObj) +{ + %popup = new PopupMenu() { + superClass = "MenuBuilder"; + isPopup = true; + }; + + for(%i=0; %i 0 : false; + %newTypeBaseType = BTEditor.getBaseNodeType(%newType); + + if(!%insert) + { + return !( (%selObjClass $= "Root" && %selObjHasChild) || + (%selObjBaseType $= "Leaf") || + (%selObjBaseType $= "Decorator" && %selObjHasChild) ); + } + else + { + return !( (%selObjClass $= "Root") || + (%newTypeBaseType $= "Leaf") ); + } +} + + +function GuiBehaviorTreeViewCtrl::addNode(%this, %selObj, %type) +{ + pushInstantGroup(%selObj); + %newNode = new(%type)(); + popInstantGroup(); + + %this.expandItem(%this.findItemByObjectId(%selObj.getId())); + %this.buildVisibleTree(); + + if(isObject(%newNode)) + BTCreateUndoAction::submit( %newNode ); + + // have to delay selection a bit + %this.schedule(10, selectItem, %this.findItemByObjectId(%newNode.getId())); +} + +function GuiBehaviorTreeViewCtrl::insertNode(%this, %selObj, %type) +{ + // steps are + // 1) Remove selected object from tree + // 2) Create the new object as a child of the selected object's parent + // 3) Move the new object into the correct position + // 4) Make the new object the parent of the old object + + // need a compound undo for this process + %this.getUndoManager().pushCompound( "Insert new" SPC %type ); + + // step 1 + // remove selected object from tree (add it to BehaviorTreeGroup) + %parent = %selObj.getGroup(); + %pos = %parent.getObjectIndex(%selObj); + BehaviorTreeGroup.add(%selObj); + + // Undo for step 1 + %action = BTReparentUndoAction::create(%this); + %action.node = %selObj; + %action.oldPosition = %pos; + %action.oldParent = %parent; + %action.newParent = BehaviorTreeGroup; + %action.newPosition = 0; + %action.addToManager(%this.getUndoManager()); + + // Step 2 + // Create new object as child of selected object's parent + pushInstantGroup(%parent); + %newNode = new(%type)(); + popInstantGroup(); + + // undo for step 2 + if(isObject(%newNode)) + BTCreateUndoAction::submit( %newNode ); + + // step 3 + // move new object to correct position + if(%pos < %parent.getCount() - 1) + { + %parent.reorderChild(%newNode, %parent.getObject(%pos)); + + // undo for step 3 + %action = BTReparentUndoAction::create(%this); + %action.node = %newNode; + %action.oldPosition = 0; + %action.oldParent = %parent; + %action.newParent = %parent; + %action.newPosition = %pos; + %action.addToManager(%this.getUndoManager()); + } + + // step 4 + // Make the new object the parent of the old object + %newNode.add(%selObj); + + // undo for step 4 + %action = BTReparentUndoAction::create(%this); + %action.node = %selObj; + %action.oldPosition = 0; + %action.oldParent = BehaviorTreeGroup; + %action.newParent = %newNode; + %action.newPosition = 0; + %action.addToManager(%this.getUndoManager()); + + // finalize the compound undo + %this.getUndoManager().popCompound(); + + // re-draw + %this.refresh(); +} + +// remove a single node from within the hierarchy, reparenting its children +function GuiBehaviorTreeViewCtrl::exciseSelection(%this) +{ + // steps are: + // 1) remove the selected node from the tree + // 2) re-parent the selected node's child to the old parent + // 3) delete the selected node + %selObj = %this.getSelectedObject(); + if(%selObj.getCount() != 1 || %selObj == %this.getRootNode()) + { + warn("Excision got messy"); + return; + } + + %child = %selObj.getObject(0); + %parent = %selObj.getGroup(); + %pos = %parent.getObjectIndex(%selObj); + + // need a compound undo for this process + %this.getUndoManager().pushCompound( "Excise" SPC %type ); + + // step 1 + // remove selected object from tree (add it to BehaviorTreeGroup) + BehaviorTreeGroup.add(%selObj); + + // Undo for step 1 + %action = BTReparentUndoAction::create(%this); + %action.node = %selObj; + %action.oldPosition = %pos; + %action.oldParent = %parent; + %action.newParent = BehaviorTreeGroup; + %action.newPosition = 0; + %action.addToManager(%this.getUndoManager()); + + // step 2 + // re-parent the selected node's child to the old parent + %parent.add(%child); + if(%pos < %parent.getCount() - 1) + { + %parent.reorderChild(%child, %parent.getObject(%pos)); + } + + // undo for step 2 + %action = BTReparentUndoAction::create(%this); + %action.node = %child; + %action.oldPosition = 0; + %action.oldParent = %selObj; + %action.newParent = %parent; + %action.newPosition = %pos; + %action.addToManager(%this.getUndoManager()); + + // step 3 + // delete the selected node + %action = new BTDeleteUndoAction(); + %action.deleteObject( %selObj ); + %action.addToManager(%this.getUndoManager()); + + // finalize the compound undo + %this.getUndoManager().popCompound(); + + // re-draw + %this.refresh(); + + %this.selectItem(%this.findItemByObjectId(%child.getId())); +} \ No newline at end of file From a7899a16dc2477feeb3b8eaad66096fbd07cce63 Mon Sep 17 00:00:00 2001 From: Marc Chapman Date: Sun, 12 Nov 2017 02:37:56 +0000 Subject: [PATCH 3/9] Execute BB --- .../Full/game/scripts/server/scriptExec.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Templates/Full/game/scripts/server/scriptExec.cs diff --git a/Templates/Full/game/scripts/server/scriptExec.cs b/Templates/Full/game/scripts/server/scriptExec.cs new file mode 100644 index 0000000000..80cf4b149a --- /dev/null +++ b/Templates/Full/game/scripts/server/scriptExec.cs @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Load up all scripts. This function is called when +// a server is constructed. +exec("./camera.cs"); +exec("./triggers.cs"); +exec("./VolumetricFog.cs"); +exec("./inventory.cs"); +exec("./shapeBase.cs"); +exec("./item.cs"); +exec("./health.cs"); +exec("./projectile.cs"); +exec("./radiusDamage.cs"); +exec("./teleporter.cs"); +exec("./physicsShape.cs"); + +exec('./BadBehavior/main.cs'); + +// Load our supporting weapon script, it contains methods used by all weapons. +exec("./weapon.cs"); + +// Load our weapon scripts +// We only need weapon scripts for those weapons that work differently from the +// class methods defined in weapon.cs +exec("./proximityMine.cs"); + +// Load our default player script +exec("./player.cs"); + +// Load our player scripts +exec("./aiPlayer.cs"); + +exec("./vehicle.cs"); +exec("./vehicleWheeled.cs"); +exec("./cheetah.cs"); + +// Load turret support scripts +exec("./turret.cs"); + +// Load our gametypes +exec("./gameCore.cs"); // This is the 'core' of the gametype functionality. +exec("./gameDM.cs"); // Overrides GameCore with DeathMatch functionality. From 5dd7b97fb302221f995405788a90ff22ce9d0d71 Mon Sep 17 00:00:00 2001 From: Marc Chapman Date: Sun, 12 Nov 2017 02:56:47 +0000 Subject: [PATCH 4/9] Linux compatibility fixes (inverted slashes) --- Engine/source/BadBehavior/core/Core.h | 2 +- Engine/source/BadBehavior/decorator/Root.h | 2 +- Engine/source/BadBehavior/decorator/Ticker.cpp | 2 +- Engine/source/BadBehavior/decorator/Ticker.h | 2 +- Engine/source/BadBehavior/leaf/Wait.cpp | 2 +- Engine/source/BadBehavior/leaf/WaitForSignal.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Engine/source/BadBehavior/core/Core.h b/Engine/source/BadBehavior/core/Core.h index a0c297e6f5..a171a012b5 100644 --- a/Engine/source/BadBehavior/core/Core.h +++ b/Engine/source/BadBehavior/core/Core.h @@ -24,7 +24,7 @@ #define _BBCORE_H_ #ifndef _ENGINEAPI_H_ -#include "console\engineAPI.h" +#include "console/engineAPI.h" #endif #ifndef _SIMSET_H_ #include "console/simSet.h" diff --git a/Engine/source/BadBehavior/decorator/Root.h b/Engine/source/BadBehavior/decorator/Root.h index 33f1be5614..4b9a971152 100644 --- a/Engine/source/BadBehavior/decorator/Root.h +++ b/Engine/source/BadBehavior/decorator/Root.h @@ -24,7 +24,7 @@ #define _BB_ROOT_H_ #ifndef _BB_BRANCH_H_ -#include "BadBehavior\core\Branch.h" +#include "BadBehavior/core/Branch.h" #endif #ifndef _BB_DECORATOR_H_ #include "BadBehavior/core/Decorator.h" diff --git a/Engine/source/BadBehavior/decorator/Ticker.cpp b/Engine/source/BadBehavior/decorator/Ticker.cpp index c094f6d786..ab6fd3d546 100644 --- a/Engine/source/BadBehavior/decorator/Ticker.cpp +++ b/Engine/source/BadBehavior/decorator/Ticker.cpp @@ -20,7 +20,7 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -#include "BadBehavior\core\Runner.h" +#include "BadBehavior/core/Runner.h" #include "Ticker.h" using namespace BadBehavior; diff --git a/Engine/source/BadBehavior/decorator/Ticker.h b/Engine/source/BadBehavior/decorator/Ticker.h index a9756d72f1..06b96ea076 100644 --- a/Engine/source/BadBehavior/decorator/Ticker.h +++ b/Engine/source/BadBehavior/decorator/Ticker.h @@ -27,7 +27,7 @@ #include "BadBehavior/core/Decorator.h" #endif #ifndef _BB_BRANCH_H_ -#include "BadBehavior\core\Branch.h" +#include "BadBehavior/core/Branch.h" #endif namespace BadBehavior diff --git a/Engine/source/BadBehavior/leaf/Wait.cpp b/Engine/source/BadBehavior/leaf/Wait.cpp index 0b67bd454c..1e7164ded0 100644 --- a/Engine/source/BadBehavior/leaf/Wait.cpp +++ b/Engine/source/BadBehavior/leaf/Wait.cpp @@ -22,7 +22,7 @@ #include "math/mMathFn.h" -#include "BadBehavior\core\Runner.h" +#include "BadBehavior/core/Runner.h" #include "Wait.h" using namespace BadBehavior; diff --git a/Engine/source/BadBehavior/leaf/WaitForSignal.cpp b/Engine/source/BadBehavior/leaf/WaitForSignal.cpp index c4ed4ad7fc..437ca6ab65 100644 --- a/Engine/source/BadBehavior/leaf/WaitForSignal.cpp +++ b/Engine/source/BadBehavior/leaf/WaitForSignal.cpp @@ -20,7 +20,7 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -#include "BadBehavior\core\Runner.h" +#include "BadBehavior/core/Runner.h" #include "WaitForSignal.h" using namespace BadBehavior; From 57f362aba57d64f67a12cacf41ae31bf99f79d8a Mon Sep 17 00:00:00 2001 From: Marc Chapman Date: Sun, 4 Feb 2018 02:13:45 +0000 Subject: [PATCH 5/9] Linux compatibility fixes (filenames) --- .../images/classIcons/ActiveSelector.png | Bin 0 -> 333 bytes .../images/classIcons/Behavior.png | Bin 0 -> 274 bytes .../images/classIcons/FailAlways.png | Bin 0 -> 364 bytes .../images/classIcons/Inverter.png | Bin 0 -> 376 bytes .../behaviorTreeEditor/images/classIcons/Loop.png | Bin 0 -> 366 bytes .../images/classIcons/Monitor.png | Bin 0 -> 367 bytes .../images/classIcons/Parallel.png | Bin 0 -> 293 bytes .../images/classIcons/RandomSelector.png | Bin 0 -> 354 bytes .../images/classIcons/RandomWait.png | Bin 0 -> 278 bytes .../behaviorTreeEditor/images/classIcons/Root.png | Bin 0 -> 317 bytes .../images/classIcons/ScriptEval.png | Bin 0 -> 286 bytes .../images/classIcons/ScriptFunc.png | Bin 0 -> 297 bytes .../images/classIcons/ScriptedBehavior.png | Bin 0 -> 293 bytes .../images/classIcons/Selector.png | Bin 0 -> 348 bytes .../images/classIcons/Sequence.png | Bin 0 -> 286 bytes .../images/classIcons/SubTree.png | Bin 0 -> 376 bytes .../images/classIcons/SucceedAlways.png | Bin 0 -> 376 bytes .../images/classIcons/Ticker.png | Bin 0 -> 387 bytes .../behaviorTreeEditor/images/classIcons/Wait.png | Bin 0 -> 281 bytes .../images/classIcons/WaitForSignal.png | Bin 0 -> 334 bytes 20 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ActiveSelector.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Behavior.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/FailAlways.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Inverter.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Loop.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Monitor.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Parallel.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/RandomSelector.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/RandomWait.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Root.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ScriptEval.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ScriptFunc.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ScriptedBehavior.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Selector.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Sequence.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/SubTree.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/SucceedAlways.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Ticker.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Wait.png create mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/WaitForSignal.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ActiveSelector.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ActiveSelector.png new file mode 100644 index 0000000000000000000000000000000000000000..c5d2010027c2c910fda2741293944aeefc619ae0 GIT binary patch literal 333 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPq508)_Yxs55<3OQXo-U3d7N?I+-pG5%K*Z(#LY_l> zJ`R&*BMx+Q8EGHja9MPY&m=@dWF`aWM%JgxJ!E#YeeSzA-Di68RXaBt z|JP-l>*D$F%(6M>oz^Q}_{Lds@r=q!<&QnOjjax%jdSYa4ITMywy~RdSEQeCTD7V1 zbk|;fp>qr~XVx)RH!W#h8MbNHDa$#}dHn;Ucsw)%7|&U*ozA~vwM@hBQt4+0a_-01 zJ)6TUl_S37?#G`K&E#kO@XDLc%aD50Y!6ez(N|Yiu2>zD`~I=@F^zwrv#0EfSKZ2A ZsDHexLfBxnxF67$44$rjF6*2UngDd~fbIYQ literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Behavior.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Behavior.png new file mode 100644 index 0000000000000000000000000000000000000000..c391b6ad0ff0fb5cfda730c03b8d11d2c0365db0 GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP~BP)lv_57I+F9C(-d%8G=Se!l@e4F>M0?!_2$tK%f z%+@80>yPo>Y_z>`=&wR3$Fa^?HCKc@W?1jHSh32&c+!qTf39;RL@9Yb4hijhtNVB1 zai_)NUnZFFNX8T~A1S*ZX&`B{Nm#{3_7Rgq`@i~43=OwSD$N<*g?xD0GD+Aa`bQ{ug6}i}QgO%^QH; PU|{fc^>bP0l+XkKleJ_c literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/FailAlways.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/FailAlways.png new file mode 100644 index 0000000000000000000000000000000000000000..01f998448f8ecc67fbf6f75bd7e3fd196ea0b2da GIT binary patch literal 364 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPqw~(NqdXu=?F`&>-PZ!4!i_=>t+xi_b5NX-ZDU;0o z?f_>-5xbVv;76K`s3#_v%fz+ zVP-#5}ET%`Q2xhl(%J>nZ&SaX_Ts@{Nh6!?cFymY1yq~s;lLH$;Z*; zkocl=CzX$N*?3G{dG4g`k1fZ27@kX36j_GHd}`Km*v6W%WXfdke-Uk7!GFE^uWU8- zUmyN*U*@c*3Q>Yi9#4MHHh*zTs^M^6@Tn(TlGgL>=2c`_@O)YOmmh!T&G`Lx*59(V zCq-C3eEoUw@Lkr$LTtPD)hgP%&)>c6nvmQ6A9H1whiu6|(K+>M=3!89GB9|$`njxg HN@xNAFE5lk literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Inverter.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Inverter.png new file mode 100644 index 0000000000000000000000000000000000000000..88578b2b855bcdf9de111a26d36654e50063becf GIT binary patch literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4Zm| zh9?j9to#gQaTa()7Bet#3xhBt!>l!1zPiy-28V4^rY>1<&+5zJdy_n? z?x>lXytc`_+--13J>>b5j)}@5Cz-C#uwhv4a-+DF>65m~>iOL)g>yXE8YXcxOzakp z>^wN-pRI91u;`8&o8O-|=}dQ>aaiK<$@aVI7eZJW7VMrEG`Ht;+Pih?63GlaG5?kK z?yId#iQL70(Y)*K0-AbW|YuPgf-_trZlsHdd8h7K ztMa5(9V|aOUYT1a_*@j_tT_<&{Mx<~k~=T6d`Vt)dctP!N4okYnxEdAOk~+;$oAf0 zme%2aCYOJ2(z&c;)N1H<^7T>Pj8!}g1-8#$wpMLi|6Jyn1cUrKyCruYt~=ws_u$fd z9lg>=>TKn2tUJcxR$v-0_wf5S>DgM7Mcml_oKu-uW07xhcaN~E?I$gu&lx;j{an^L HB{Ts5JcN*1 literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Monitor.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Monitor.png new file mode 100644 index 0000000000000000000000000000000000000000..83914b53aac389e90139f98ff91465ea4dfa9478 GIT binary patch literal 367 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*x37`TN&n2}-D90{Nxdx@v7EBhUGW`1dD#Z3;5K%w`ZE{-7e>G-Z}m3ohg}d}{+sVuul3}OYzcCjbCjqhRWXF0}- z?y0-8^z$a2=e}E1S|&VL{@Z`WYNm$6d6QQ^DL*=oe>blp%YxL)?O#5X`8=uHowh$V z+r)wKPfh*DKNW`;wCaWNzyI?6uP5Q|jdtof6FuOb3Jj3RAb&D#AGj8ep0RMQ3BVz zWwK_42gLYR#V=gHXjT*Z0_oR}pVsU)f17aee(%=Tzw72Ti3_-I&0x-`Ts2AOtGAJH guublRRAw87FMB=L>duT`0(3Bgr>mdKI;Vst09FWRAOHXW literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/RandomSelector.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/RandomSelector.png new file mode 100644 index 0000000000000000000000000000000000000000..f2c366c60f529f2b02d051f4b6d2e7cd1101487f GIT binary patch literal 354 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rl| zd|+T`d(5v46yYrJh%9Dc;1&j9Muu5)B!GhKC7!;n?048%g_#-i=BTU!3O(|4aSX9I zeKdGC?;!`CTK!WD=1kTGjJ{%=cA|f)Afz!~*$}9K|`*Q(~B2Cy-|b?Q|y_Dt$wNn+$()U$R=3U8lp zn%r*{VUc@|;ZG)F|@HQ<55y2=sZ|`Rb`_Cr4Q+T~EyTC&KkVedG&t)PP z81Gf*h4C+VFU4@+@2<<$3Pv^aLO$PP=D4xPC*>b^-oj{?*o-U3d7N?H}-{w86z_Z6$QmMLx zz3c^(oU&xQ>@Mc&67KKJw}hmFOn;v6^hl_A&fd1F%XY#M{rTCB40lC5troAkG|hZ# z`!#W^eiw7c=1v0@@%Ei}%sKR&RP|RUL>_&+*2CiV5zdBLzy3Qh9Jm!LCwgEakKLR( zN-jt29#&js>*6Tta$WE}q56q=QP+LPBH>fZ`_#&lB~lu0|9zNa8$a*wD*cRO5q!yv Si2*?8GI+ZBxvX)^mS#w!_LCTE!B`@zyTE6>*?YcVsU!w z;;(5MpfjcFUw`S)FOCHrFxE zKT95*;Izy&*c=>qU+4X6%d{DBE=*dOCvQctc cc>PuJ75WJV=h!BR0iDg@>FVdQ&MBb@0H`}|rvLx| literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ScriptFunc.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ScriptFunc.png new file mode 100644 index 0000000000000000000000000000000000000000..e2e2cac37a6bdb9f545ba5f3dada2e29804a940b GIT binary patch literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgfSn=^#NYh2_kjD8W@Ss)6|Z)R=k7Ev>J1K^80=!Jwrhhy-Q77wUC)(xrB5Z@dlo0- m$6ArM{-F8cPuG|KV%t{jz2{&2&19gX89ZJ6T-G@yGywn*6mEt9 literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ScriptedBehavior.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ScriptedBehavior.png new file mode 100644 index 0000000000000000000000000000000000000000..64a3ada632dc8838e6f1d8a03d01f1d415aea253 GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPqx1=zSuBY~g)j*+5o-U3d7N?I+KF!?Rtm$!LUC1)+! zSn;aynydY>k5c}-ovz#|e>Nw>oqgjbvrT$I(bC&gJ0E`AA~l7QL70(Y)*K0-AbW|YuPgf=bP&}xk10`re3i-vX>#Nw`njxgN@xNAS`>qt literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Sequence.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Sequence.png new file mode 100644 index 0000000000000000000000000000000000000000..d7006db7b5abbe68778ebebb08a9c032328e8d1c GIT binary patch literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*x37`TN&n2}-D90{Nxdx@v7EBhTbAx>7cn#*D3K%se_E{-7OFsLs~>&5;%cgA?DhVAnk*Bh>ozRrU+{B<*C!LbliNhyr6p<` Z)NSUPuW~=j&kb}bgQu&X%Q~loCICM^WJUl0 literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/SubTree.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/SubTree.png new file mode 100644 index 0000000000000000000000000000000000000000..18e3f4c3f1d825548f81a607668a4ecd0c801e6a GIT binary patch literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgf8Z9J{an^LB{Ts5y}O)> literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/SucceedAlways.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/SucceedAlways.png new file mode 100644 index 0000000000000000000000000000000000000000..da6ed8621f28a8f07c3ffdf2de6b782c27b73e69 GIT binary patch literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4Zm| zh9?j9to#gQaTa()7Bet#3xhBt!>l{!J)*WZ|y&iFbD88?9s1c$PLb<&InHHf#5Y zpjEr)MMZ_k-SqJ_Suy`b6(@fOZ{=jA;Mg|n2{X#y9oTs`vSLoRAD6>6R*jWUy0{G< zx5o5rWMW>i(mU>6e$c*EtA562ICCf->@SzUa7C42`Qf}%C*O;fms-8yP-KvuZMWq4 z^DhBe!|+`b<_-^Mn^P-0>K?ce5~iff)N(=!cUZ2#w6Uw)=h9fOnq^d0}w SJT?HsfWgz%&t;ucLK6TH`jwaUKmMhnMg&>!Qa~L@Vd0WfFUT>Xy z!QfJs?c$mL|DRctnigZ^8FTulys>fmw$)slo_u+GG$Nlb=ov$Z_g*ifHT^t)9~@8e z3}*fL_DtaK0(aHavsWZf%iUEy@y24#&9jV?;u}IP7Pgu6f71Q&ai)a_>xnZg9Bb8d zpGJFrRP-0WJ;Nh@M&qt+&%Qp^W1Y3EP@w1bmbgNuwH%5n&zAr8e{oBy;c=by`Xl}+ zcJH>g2sA7-vt2TM`}&Z|8@U#5%rey~&p)4^KTkgYnC_0X^7l^umDs#)?ba!a?f;w; d+isG*zi!I!)MJ(hDu5xx;OXk;vd$@?2>{!Wo0R|n literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Wait.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Wait.png new file mode 100644 index 0000000000000000000000000000000000000000..66b01e21808a777e7f40e9f7d29436ac8af21579 GIT binary patch literal 281 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPqw~(MELzROqJ5Xqur;B5V#p$EL7X=S1aMYeZEpSuW zdWW-kf@%2+zIPLKU$A?ccD=k>H^tiafVvIi%2hjrC;e#oxn7{bdt%D-kkD=Cqq7zt zbGjaQ!TZvM6p1*q16j}cwpwl0W&ZXnaW_v|3gd#azxPjKXmBsJlx8sIIcKf7oI_Bv z_~71%obbSNR#SEUVelF{r5}E)=OK4*N literal 0 HcmV?d00001 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/WaitForSignal.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/WaitForSignal.png new file mode 100644 index 0000000000000000000000000000000000000000..b3a4fdada3dd42f5640accc6fb81444d96823166 GIT binary patch literal 334 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP~qX0jf1kZY=89<@io-U3d7N?g^e%O0hfv0tUyOqoH z9bV>6Wjk1Z%$Hswt887;;`LJI?xVZ>3Mw((Q@(k!HRq<-OXpmkD){}4qmV7*u5S)) z?mo>$*Ee$>IQK1H^=XdhFZq=bcAb?zujD#846YU#{Ya;`POcj?2Aha=P7UdypY<9$Qk+R_)HGxAujdkL@rt z@@LMBygEgFjnO`_Nqc=B+VIN6rFO{GRh=(e6KeEhS8VNnIWZ$Y`|D@rFD(6)_s9P4 ax%yA84>m3Jvw8#cCWEJ|pUXO@geCw9#DydP literal 0 HcmV?d00001 From b1db72a29feb2428939a7c048d32c8d296c9da04 Mon Sep 17 00:00:00 2001 From: Lukas Aldershaab Date: Sat, 3 Oct 2020 10:56:06 +0200 Subject: [PATCH 6/9] Remove files with wrong casing --- .../images/classIcons/ActiveSelector.png | Bin 333 -> 0 bytes .../images/classIcons/Behavior.png | Bin 274 -> 0 bytes .../images/classIcons/FailAlways.png | Bin 364 -> 0 bytes .../images/classIcons/Inverter.png | Bin 376 -> 0 bytes .../images/classIcons/RandomSelector.png | Bin 354 -> 0 bytes .../images/classIcons/RandomWait.png | Bin 278 -> 0 bytes .../images/classIcons/Sequence.png | Bin 286 -> 0 bytes .../images/classIcons/SubTree.png | Bin 376 -> 0 bytes .../images/classIcons/WaitForSignal.png | Bin 334 -> 0 bytes .../behaviorTreeEditor/images/classIcons/loop.png | Bin 366 -> 0 bytes .../images/classIcons/monitor.png | Bin 367 -> 0 bytes .../images/classIcons/parallel.png | Bin 293 -> 0 bytes .../behaviorTreeEditor/images/classIcons/root.png | Bin 317 -> 0 bytes .../images/classIcons/scriptEval.png | Bin 286 -> 0 bytes .../images/classIcons/scriptFunc.png | Bin 297 -> 0 bytes .../images/classIcons/scriptedBehavior.png | Bin 293 -> 0 bytes .../images/classIcons/selector.png | Bin 348 -> 0 bytes .../images/classIcons/succeedAlways.png | Bin 376 -> 0 bytes .../images/classIcons/ticker.png | Bin 387 -> 0 bytes .../behaviorTreeEditor/images/classIcons/wait.png | Bin 281 -> 0 bytes 20 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ActiveSelector.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Behavior.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/FailAlways.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Inverter.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/RandomSelector.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/RandomWait.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Sequence.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/SubTree.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/WaitForSignal.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/loop.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/monitor.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/parallel.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/root.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptEval.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptFunc.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptedBehavior.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/selector.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/succeedAlways.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ticker.png delete mode 100644 Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/wait.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ActiveSelector.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ActiveSelector.png deleted file mode 100644 index c5d2010027c2c910fda2741293944aeefc619ae0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 333 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPq508)_Yxs55<3OQXo-U3d7N?I+-pG5%K*Z(#LY_l> zJ`R&*BMx+Q8EGHja9MPY&m=@dWF`aWM%JgxJ!E#YeeSzA-Di68RXaBt z|JP-l>*D$F%(6M>oz^Q}_{Lds@r=q!<&QnOjjax%jdSYa4ITMywy~RdSEQeCTD7V1 zbk|;fp>qr~XVx)RH!W#h8MbNHDa$#}dHn;Ucsw)%7|&U*ozA~vwM@hBQt4+0a_-01 zJ)6TUl_S37?#G`K&E#kO@XDLc%aD50Y!6ez(N|Yiu2>zD`~I=@F^zwrv#0EfSKZ2A ZsDHexLfBxnxF67$44$rjF6*2UngDd~fbIYQ diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Behavior.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Behavior.png deleted file mode 100644 index c391b6ad0ff0fb5cfda730c03b8d11d2c0365db0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP~BP)lv_57I+F9C(-d%8G=Se!l@e4F>M0?!_2$tK%f z%+@80>yPo>Y_z>`=&wR3$Fa^?HCKc@W?1jHSh32&c+!qTf39;RL@9Yb4hijhtNVB1 zai_)NUnZFFNX8T~A1S*ZX&`B{Nm#{3_7Rgq`@i~43=OwSD$N<*g?xD0GD+Aa`bQ{ug6}i}QgO%^QH; PU|{fc^>bP0l+XkKleJ_c diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/FailAlways.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/FailAlways.png deleted file mode 100644 index 01f998448f8ecc67fbf6f75bd7e3fd196ea0b2da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 364 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPqw~(NqdXu=?F`&>-PZ!4!i_=>t+xi_b5NX-ZDU;0o z?f_>-5xbVv;76K`s3#_v%fz+ zVP-#5}ET%`Q2xhl(%J>nZ&SaX_Ts@{Nh6!?cFymY1yq~s;lLH$;Z*; zkocl=CzX$N*?3G{dG4g`k1fZ27@kX36j_GHd}`Km*v6W%WXfdke-Uk7!GFE^uWU8- zUmyN*U*@c*3Q>Yi9#4MHHh*zTs^M^6@Tn(TlGgL>=2c`_@O)YOmmh!T&G`Lx*59(V zCq-C3eEoUw@Lkr$LTtPD)hgP%&)>c6nvmQ6A9H1whiu6|(K+>M=3!89GB9|$`njxg HN@xNAFE5lk diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Inverter.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Inverter.png deleted file mode 100644 index 88578b2b855bcdf9de111a26d36654e50063becf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4Zm| zh9?j9to#gQaTa()7Bet#3xhBt!>l!1zPiy-28V4^rY>1<&+5zJdy_n? z?x>lXytc`_+--13J>>b5j)}@5Cz-C#uwhv4a-+DF>65m~>iOL)g>yXE8YXcxOzakp z>^wN-pRI91u;`8&o8O-|=}dQ>aaiK<$@aVI7eZJW7VMrEG`Ht;+Pih?63GlaG5?kK z?yId#i%=cA|f)Afz!~*$}9K|`*Q(~B2Cy-|b?Q|y_Dt$wNn+$()U$R=3U8lp zn%r*{VUc@|;ZG)F|@HQ<55y2=sZ|`Rb`_Cr4Q+T~EyTC&KkVedG&t)PP z81Gf*h4C+VFU4@+@2<<$3Pv^aLO$PP=D4xPC*>b^-oj{?*o-U3d7N?H}-{w86z_Z6$QmMLx zz3c^(oU&xQ>@Mc&67KKJw}hmFOn;v6^hl_A&fd1F%XY#M{rTCB40lC5troAkG|hZ# z`!#W^eiw7c=1v0@@%Ei}%sKR&RP|RUL>_&+*2CiV5zdBLzy3Qh9Jm!LCwgEakKLR( zN-jt29#&js>*6Tta$WE}q56q=QP+LPBH>fZ`_#&lB~lu0|9zNa8$a*wD*cRO5q!yv Si2*?8GI+ZBxvX7cn#*D3K%se_E{-7OFsLs~>&5;%cgA?DhVAnk*Bh>ozRrU+{B<*C!LbliNhyr6p<` Z)NSUPuW~=j&kb}bgQu&X%Q~loCICM^WJUl0 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/SubTree.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/SubTree.png deleted file mode 100644 index 18e3f4c3f1d825548f81a607668a4ecd0c801e6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgf8Z9J{an^LB{Ts5y}O)> diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/WaitForSignal.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/WaitForSignal.png deleted file mode 100644 index b3a4fdada3dd42f5640accc6fb81444d96823166..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP~qX0jf1kZY=89<@io-U3d7N?g^e%O0hfv0tUyOqoH z9bV>6Wjk1Z%$Hswt887;;`LJI?xVZ>3Mw((Q@(k!HRq<-OXpmkD){}4qmV7*u5S)) z?mo>$*Ee$>IQK1H^=XdhFZq=bcAb?zujD#846YU#{Ya;`POcj?2Aha=P7UdypY<9$Qk+R_)HGxAujdkL@rt z@@LMBygEgFjnO`_Nqc=B+VIN6rFO{GRh=(e6KeEhS8VNnIWZ$Y`|D@rFD(6)_s9P4 ax%yA84>m3Jvw8#cCWEJ|pUXO@geCw9#DydP diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/loop.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/loop.png deleted file mode 100644 index bc7f5c54b0b92339af0c8561c7f87465013207bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 366 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgf-_trZlsHdd8h7K ztMa5(9V|aOUYT1a_*@j_tT_<&{Mx<~k~=T6d`Vt)dctP!N4okYnxEdAOk~+;$oAf0 zme%2aCYOJ2(z&c;)N1H<^7T>Pj8!}g1-8#$wpMLi|6Jyn1cUrKyCruYt~=ws_u$fd z9lg>=>TKn2tUJcxR$v-0_wf5S>DgM7Mcml_oKu-uW07xhcaN~E?I$gu&lx;j{an^L HB{Ts5JcN*1 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/monitor.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/monitor.png deleted file mode 100644 index 83914b53aac389e90139f98ff91465ea4dfa9478..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 367 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*x37`TN&n2}-D90{Nxdx@v7EBhUGW`1dD#Z3;5K%w`ZE{-7e>G-Z}m3ohg}d}{+sVuul3}OYzcCjbCjqhRWXF0}- z?y0-8^z$a2=e}E1S|&VL{@Z`WYNm$6d6QQ^DL*=oe>blp%YxL)?O#5X`8=uHowh$V z+r)wKPfh*DKNW`;wCaWNzyI?6uP5Q|jdtof6FuOb3Jj3RAb&D#AGj8ep0RMQ3BVz zWwK_42gLYR#V=gHXjT*Z0_oR}pVsU)f17aee(%=Tzw72Ti3_-I&0x-`Ts2AOtGAJH guublRRAw87FMB=L>duT`0(3Bgr>mdKI;Vst09FWRAOHXW diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/root.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/root.png deleted file mode 100644 index 62b0c0739c6be0ce2ca94e40307d0f9305d2b2aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 317 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^RlY zSq#SNHJ^bLXMsm#F#`j)FbFd;%$g$s6l5>)^mS#w!_LCTE!B`@zyTE6>*?YcVsU!w z;;(5MpfjcFUw`S)FOCHrFxE zKT95*;Izy&*c=>qU+4X6%d{DBE=*dOCvQctc cc>PuJ75WJV=h!BR0iDg@>FVdQ&MBb@0H`}|rvLx| diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptFunc.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptFunc.png deleted file mode 100644 index e2e2cac37a6bdb9f545ba5f3dada2e29804a940b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgfSn=^#NYh2_kjD8W@Ss)6|Z)R=k7Ev>J1K^80=!Jwrhhy-Q77wUC)(xrB5Z@dlo0- m$6ArM{-F8cPuG|KV%t{jz2{&2&19gX89ZJ6T-G@yGywn*6mEt9 diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptedBehavior.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/scriptedBehavior.png deleted file mode 100644 index 64a3ada632dc8838e6f1d8a03d01f1d415aea253..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPqx1=zSuBY~g)j*+5o-U3d7N?I+KF!?Rtm$!LUC1)+! zSn;aynydY>k5c}-ovz#|e>Nw>oqgjbvrT$I(bC&gJ0E`AA~l7QL70(Y)*K0-AbW|YuPgf=bP&}xk10`re3i-vX>#Nw`njxgN@xNAS`>qt diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/succeedAlways.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/succeedAlways.png deleted file mode 100644 index da6ed8621f28a8f07c3ffdf2de6b782c27b73e69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4Zm| zh9?j9to#gQaTa()7Bet#3xhBt!>l{!J)*WZ|y&iFbD88?9s1c$PLb<&InHHf#5Y zpjEr)MMZ_k-SqJ_Suy`b6(@fOZ{=jA;Mg|n2{X#y9oTs`vSLoRAD6>6R*jWUy0{G< zx5o5rWMW>i(mU>6e$c*EtA562ICCf->@SzUa7C42`Qf}%C*O;fms-8yP-KvuZMWq4 z^DhBe!|+`b<_-^Mn^P-0>K?ce5~iff)N(=!cUZ2#w6Uw)=h9fOnq^d0}w SJT?HsfWgz%&t;ucLK6TH`jwaUKmMhnMg&>!Qa~L@Vd0WfFUT>Xy z!QfJs?c$mL|DRctnigZ^8FTulys>fmw$)slo_u+GG$Nlb=ov$Z_g*ifHT^t)9~@8e z3}*fL_DtaK0(aHavsWZf%iUEy@y24#&9jV?;u}IP7Pgu6f71Q&ai)a_>xnZg9Bb8d zpGJFrRP-0WJ;Nh@M&qt+&%Qp^W1Y3EP@w1bmbgNuwH%5n&zAr8e{oBy;c=by`Xl}+ zcJH>g2sA7-vt2TM`}&Z|8@U#5%rey~&p)4^KTkgYnC_0X^7l^umDs#)?ba!a?f;w; d+isG*zi!I!)MJ(hDu5xx;OXk;vd$@?2>{!Wo0R|n diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/wait.png b/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/wait.png deleted file mode 100644 index 66b01e21808a777e7f40e9f7d29436ac8af21579..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 281 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPqw~(MELzROqJ5Xqur;B5V#p$EL7X=S1aMYeZEpSuW zdWW-kf@%2+zIPLKU$A?ccD=k>H^tiafVvIi%2hjrC;e#oxn7{bdt%D-kkD=Cqq7zt zbGjaQ!TZvM6p1*q16j}cwpwl0W&ZXnaW_v|3gd#azxPjKXmBsJlx8sIIcKf7oI_Bv z_~71%obbSNR#SEUVelF{r5}E)=OK4*N From 036fa6adca01a4154ee7975a04c1725b85b8e88a Mon Sep 17 00:00:00 2001 From: Areloch Date: Mon, 5 Feb 2018 16:17:04 -0600 Subject: [PATCH 7/9] bb fixes from jeff --- .../scripts/behaviorTreeEditor.ed.cs | 11 +++++++---- .../scripts/behaviorTreeEditorCanvas.ed.cs | 15 +++++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditor.ed.cs b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditor.ed.cs index 1b9683fe4f..10fb9a583e 100644 --- a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditor.ed.cs +++ b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditor.ed.cs @@ -52,13 +52,16 @@ function toggleBehaviorTreeEditor( %make ) } } -GlobalActionMap.bind( keyboard, "f9", toggleBehaviorTreeEditor ); +GlobalActionMap.bind( keyboard, "Shift F9", toggleBehaviorTreeEditor ); function BTEditor::startUp(%this, %content) { %this.lastContent=%content; Canvas.setContent( BTEditor ); + //Canavs.pushDialog(BTEditor); + + BadBehaviorMenubar.attachToCanvas(Canvas,0); if(!isObject(BehaviorTreeManager)) // This isn't pretty, but we need to load up existing trees @@ -271,7 +274,7 @@ function toggleBehaviorTreeEditor( %make ) if(%dlg.execute()) { - %file = %dlg.fileName; + %file = %dlg.fileNamfe; %dlg.delete(); } else @@ -351,7 +354,7 @@ function toggleBehaviorTreeEditor( %make ) %nextUndo = %uman.getNextUndoName(); %nextRedo = %uman.getNextRedoName(); - %editMenu = BTEditCanvas.menuBar->editMenu; + %editMenu = BTEditCanvas.menuBar.findMenu("Edit"); %editMenu.setItemName( 0, "Undo " @ %nextUndo ); %editMenu.setItemName( 1, "Redo " @ %nextRedo ); @@ -362,7 +365,7 @@ function toggleBehaviorTreeEditor( %make ) function BTEditor::ResetUndoMenu(%this) { - %editMenu = BTEditCanvas.menuBar->editMenu; + %editMenu = BTEditCanvas.menuBar.findMenu("Edit"); %editMenu.setItemName( 0, "Undo" ); %editMenu.setItemName( 1, "Redo" ); %editMenu.enableItem( 0, false ); diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorCanvas.ed.cs b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorCanvas.ed.cs index 93b45fe6e8..8cf37a4536 100644 --- a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorCanvas.ed.cs +++ b/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorCanvas.ed.cs @@ -44,9 +44,6 @@ { if( isObject( BehaviorTreeEditorGui.menuGroup ) ) BehaviorTreeEditorGui.delete(); - - // cleanup - %this.onDestroyMenu(); //BTEditorTabBook.deleteAllObjects(); } @@ -59,6 +56,12 @@ Canvas.setContent(BTEditor.lastContent); $InBehaviorTreeEditor = false; BehaviorTreeManager.onBehaviorTreeEditor(false); + + // cleanup + %this.onDestroyMenu(); + + //Re-establish the main editor's menubar + EditorGui.attachMenus(); } //============================================================================== @@ -82,9 +85,13 @@ } // Menu bar - %this.menuBar = new MenuBar() + %this.menuBar = new GuiMenuBar(BadBehaviorMenubar) { dynamicItemInsertPos = 3; + extent = "1024 20"; + minExtent = "320 20"; + horizSizing = "width"; + profile = "GuiMenuBarProfile"; new PopupMenu() { From 417a33ce67dfa6b43aa6de1f75e2c69d5e1c7430 Mon Sep 17 00:00:00 2001 From: Lukas Aldershaab Date: Sat, 3 Oct 2020 11:06:51 +0200 Subject: [PATCH 8/9] Implement as BadBehaviour module --- .../gui/BTEditorCreatePrompt.ed.gui | 0 .../gui/behaviorTreeEditor.ed.gui | 0 .../images/BehaviorTreeView.png | Bin .../images/classIcons/ActiveSelector.png} | Bin .../images/classIcons/Behavior.png} | Bin .../images/classIcons/FailAlways.png} | Bin .../images/classIcons/Inverter.png} | Bin .../images/classIcons/Loop.png | Bin .../images/classIcons/Monitor.png | Bin .../images/classIcons/Parallel.png | Bin .../images/classIcons/RandomSelector.png} | Bin .../images/classIcons/RandomWait.png} | Bin .../images/classIcons/Root.png | Bin .../images/classIcons/ScriptEval.png | Bin .../images/classIcons/ScriptFunc.png | Bin .../images/classIcons/ScriptedBehavior.png | Bin .../images/classIcons/Selector.png | Bin .../images/classIcons/Sequence.png} | Bin .../images/classIcons/SubTree.png} | Bin .../images/classIcons/SucceedAlways.png | Bin .../images/classIcons/Ticker.png | Bin .../images/classIcons/Wait.png | Bin .../images/classIcons/WaitForSignal.png} | Bin .../game/tools/behaviorTreeEditor/main.cs | 0 .../scripts/behaviorTreeEditor.ed.cs | 0 .../scripts/behaviorTreeEditorCanvas.ed.cs | 0 .../behaviorTreeEditorContentList.ed.cs | 0 .../scripts/behaviorTreeEditorInspector.ed.cs | 0 .../scripts/behaviorTreeEditorProfiles.ed.cs | 0 .../scripts/behaviorTreeEditorStatusBar.ed.cs | 0 .../scripts/behaviorTreeEditorUndo.ed.cs | 0 .../scripts/guiBehaviorTreeViewCtrl.ed.cs | 0 .../game/scripts/server/BadBehavior/main.cs | 25 ------- .../Full/game/scripts/server/scriptExec.cs | 62 ------------------ .../Modules/badBehaviour/badBehaviour.cs | 20 ++++++ .../Modules/badBehaviour/badBehaviour.module | 9 +++ .../badBehaviour}/levels/BehaviorTestbed.mis | 0 .../levels/BehaviorTestbed_preview.png | Bin .../badBehaviour/scripts/server/badBot.cs} | 0 .../scripts/server}/behaviorTreeManager.cs | 6 +- .../server}/behaviorTrees/botMatchTree.cs | 0 .../scripts/server}/behaviorTrees/botTree.cs | 0 .../server}/behaviorTrees/combatTree.cs | 0 .../server/behaviorTrees/followTree.cs} | 0 .../server}/behaviorTrees/getHealthTree.cs | 0 .../server/behaviorTrees/patrolTree.cs} | 0 .../server/behaviorTrees/wanderTree.cs} | 0 .../badBehaviour/scripts/server}/botMatch.cs | 0 48 files changed, 32 insertions(+), 90 deletions(-) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/gui/BTEditorCreatePrompt.ed.gui (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/gui/behaviorTreeEditor.ed.gui (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/images/BehaviorTreeView.png (100%) rename Templates/{Full/game/tools/behaviorTreeEditor/images/classIcons/activeSelector.png => BaseGame/game/tools/behaviorTreeEditor/images/classIcons/ActiveSelector.png} (100%) rename Templates/{Full/game/tools/behaviorTreeEditor/images/classIcons/behavior.png => BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Behavior.png} (100%) rename Templates/{Full/game/tools/behaviorTreeEditor/images/classIcons/failAlways.png => BaseGame/game/tools/behaviorTreeEditor/images/classIcons/FailAlways.png} (100%) rename Templates/{Full/game/tools/behaviorTreeEditor/images/classIcons/inverter.png => BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Inverter.png} (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/images/classIcons/Loop.png (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/images/classIcons/Monitor.png (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/images/classIcons/Parallel.png (100%) rename Templates/{Full/game/tools/behaviorTreeEditor/images/classIcons/randomSelector.png => BaseGame/game/tools/behaviorTreeEditor/images/classIcons/RandomSelector.png} (100%) rename Templates/{Full/game/tools/behaviorTreeEditor/images/classIcons/Randomwait.png => BaseGame/game/tools/behaviorTreeEditor/images/classIcons/RandomWait.png} (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/images/classIcons/Root.png (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/images/classIcons/ScriptEval.png (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/images/classIcons/ScriptFunc.png (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/images/classIcons/ScriptedBehavior.png (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/images/classIcons/Selector.png (100%) rename Templates/{Full/game/tools/behaviorTreeEditor/images/classIcons/sequence.png => BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Sequence.png} (100%) rename Templates/{Full/game/tools/behaviorTreeEditor/images/classIcons/subTree.png => BaseGame/game/tools/behaviorTreeEditor/images/classIcons/SubTree.png} (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/images/classIcons/SucceedAlways.png (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/images/classIcons/Ticker.png (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/images/classIcons/Wait.png (100%) rename Templates/{Full/game/tools/behaviorTreeEditor/images/classIcons/waitForSignal.png => BaseGame/game/tools/behaviorTreeEditor/images/classIcons/WaitForSignal.png} (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/main.cs (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditor.ed.cs (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorCanvas.ed.cs (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorContentList.ed.cs (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorInspector.ed.cs (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorProfiles.ed.cs (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorStatusBar.ed.cs (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorUndo.ed.cs (100%) rename Templates/{Full => BaseGame}/game/tools/behaviorTreeEditor/scripts/guiBehaviorTreeViewCtrl.ed.cs (100%) delete mode 100644 Templates/Full/game/scripts/server/BadBehavior/main.cs delete mode 100644 Templates/Full/game/scripts/server/scriptExec.cs create mode 100644 Templates/Modules/badBehaviour/badBehaviour.cs create mode 100644 Templates/Modules/badBehaviour/badBehaviour.module rename Templates/{Full/game => Modules/badBehaviour}/levels/BehaviorTestbed.mis (100%) rename Templates/{Full/game => Modules/badBehaviour}/levels/BehaviorTestbed_preview.png (100%) rename Templates/{Full/game/scripts/server/BadBehavior/BadBot.cs => Modules/badBehaviour/scripts/server/badBot.cs} (100%) rename Templates/{Full/game/scripts/server/BadBehavior => Modules/badBehaviour/scripts/server}/behaviorTreeManager.cs (96%) rename Templates/{Full/game/scripts/server/BadBehavior => Modules/badBehaviour/scripts/server}/behaviorTrees/botMatchTree.cs (100%) rename Templates/{Full/game/scripts/server/BadBehavior => Modules/badBehaviour/scripts/server}/behaviorTrees/botTree.cs (100%) rename Templates/{Full/game/scripts/server/BadBehavior => Modules/badBehaviour/scripts/server}/behaviorTrees/combatTree.cs (100%) rename Templates/{Full/game/scripts/server/BadBehavior/behaviorTrees/FollowTree.cs => Modules/badBehaviour/scripts/server/behaviorTrees/followTree.cs} (100%) rename Templates/{Full/game/scripts/server/BadBehavior => Modules/badBehaviour/scripts/server}/behaviorTrees/getHealthTree.cs (100%) rename Templates/{Full/game/scripts/server/BadBehavior/behaviorTrees/PatrolTree.cs => Modules/badBehaviour/scripts/server/behaviorTrees/patrolTree.cs} (100%) rename Templates/{Full/game/scripts/server/BadBehavior/behaviorTrees/WanderTree.cs => Modules/badBehaviour/scripts/server/behaviorTrees/wanderTree.cs} (100%) rename Templates/{Full/game/scripts/server/BadBehavior => Modules/badBehaviour/scripts/server}/botMatch.cs (100%) diff --git a/Templates/Full/game/tools/behaviorTreeEditor/gui/BTEditorCreatePrompt.ed.gui b/Templates/BaseGame/game/tools/behaviorTreeEditor/gui/BTEditorCreatePrompt.ed.gui similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/gui/BTEditorCreatePrompt.ed.gui rename to Templates/BaseGame/game/tools/behaviorTreeEditor/gui/BTEditorCreatePrompt.ed.gui diff --git a/Templates/Full/game/tools/behaviorTreeEditor/gui/behaviorTreeEditor.ed.gui b/Templates/BaseGame/game/tools/behaviorTreeEditor/gui/behaviorTreeEditor.ed.gui similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/gui/behaviorTreeEditor.ed.gui rename to Templates/BaseGame/game/tools/behaviorTreeEditor/gui/behaviorTreeEditor.ed.gui diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/BehaviorTreeView.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/BehaviorTreeView.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/BehaviorTreeView.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/BehaviorTreeView.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/activeSelector.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/ActiveSelector.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/activeSelector.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/ActiveSelector.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/behavior.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Behavior.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/behavior.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Behavior.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/failAlways.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/FailAlways.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/failAlways.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/FailAlways.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/inverter.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Inverter.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/inverter.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Inverter.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Loop.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Loop.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Loop.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Loop.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Monitor.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Monitor.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Monitor.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Monitor.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Parallel.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Parallel.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Parallel.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Parallel.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/randomSelector.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/RandomSelector.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/randomSelector.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/RandomSelector.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Randomwait.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/RandomWait.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Randomwait.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/RandomWait.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Root.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Root.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Root.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Root.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ScriptEval.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/ScriptEval.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ScriptEval.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/ScriptEval.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ScriptFunc.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/ScriptFunc.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ScriptFunc.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/ScriptFunc.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ScriptedBehavior.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/ScriptedBehavior.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/ScriptedBehavior.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/ScriptedBehavior.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Selector.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Selector.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Selector.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Selector.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/sequence.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Sequence.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/sequence.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Sequence.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/subTree.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/SubTree.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/subTree.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/SubTree.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/SucceedAlways.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/SucceedAlways.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/SucceedAlways.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/SucceedAlways.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Ticker.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Ticker.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Ticker.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Ticker.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Wait.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Wait.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/Wait.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/Wait.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/waitForSignal.png b/Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/WaitForSignal.png similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/images/classIcons/waitForSignal.png rename to Templates/BaseGame/game/tools/behaviorTreeEditor/images/classIcons/WaitForSignal.png diff --git a/Templates/Full/game/tools/behaviorTreeEditor/main.cs b/Templates/BaseGame/game/tools/behaviorTreeEditor/main.cs similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/main.cs rename to Templates/BaseGame/game/tools/behaviorTreeEditor/main.cs diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditor.ed.cs b/Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditor.ed.cs similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditor.ed.cs rename to Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditor.ed.cs diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorCanvas.ed.cs b/Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorCanvas.ed.cs similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorCanvas.ed.cs rename to Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorCanvas.ed.cs diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorContentList.ed.cs b/Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorContentList.ed.cs similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorContentList.ed.cs rename to Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorContentList.ed.cs diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorInspector.ed.cs b/Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorInspector.ed.cs similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorInspector.ed.cs rename to Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorInspector.ed.cs diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorProfiles.ed.cs b/Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorProfiles.ed.cs similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorProfiles.ed.cs rename to Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorProfiles.ed.cs diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorStatusBar.ed.cs b/Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorStatusBar.ed.cs similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorStatusBar.ed.cs rename to Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorStatusBar.ed.cs diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorUndo.ed.cs b/Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorUndo.ed.cs similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorUndo.ed.cs rename to Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/behaviorTreeEditorUndo.ed.cs diff --git a/Templates/Full/game/tools/behaviorTreeEditor/scripts/guiBehaviorTreeViewCtrl.ed.cs b/Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/guiBehaviorTreeViewCtrl.ed.cs similarity index 100% rename from Templates/Full/game/tools/behaviorTreeEditor/scripts/guiBehaviorTreeViewCtrl.ed.cs rename to Templates/BaseGame/game/tools/behaviorTreeEditor/scripts/guiBehaviorTreeViewCtrl.ed.cs diff --git a/Templates/Full/game/scripts/server/BadBehavior/main.cs b/Templates/Full/game/scripts/server/BadBehavior/main.cs deleted file mode 100644 index 85361f0f99..0000000000 --- a/Templates/Full/game/scripts/server/BadBehavior/main.cs +++ /dev/null @@ -1,25 +0,0 @@ -//----------------------------------------------------------------------------- -// 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. -//----------------------------------------------------------------------------- - -exec("./behaviorTreeManager.cs"); -exec("./BadBot.cs"); -exec("./botMatch.cs"); \ No newline at end of file diff --git a/Templates/Full/game/scripts/server/scriptExec.cs b/Templates/Full/game/scripts/server/scriptExec.cs deleted file mode 100644 index 80cf4b149a..0000000000 --- a/Templates/Full/game/scripts/server/scriptExec.cs +++ /dev/null @@ -1,62 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// 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. -//----------------------------------------------------------------------------- - -// Load up all scripts. This function is called when -// a server is constructed. -exec("./camera.cs"); -exec("./triggers.cs"); -exec("./VolumetricFog.cs"); -exec("./inventory.cs"); -exec("./shapeBase.cs"); -exec("./item.cs"); -exec("./health.cs"); -exec("./projectile.cs"); -exec("./radiusDamage.cs"); -exec("./teleporter.cs"); -exec("./physicsShape.cs"); - -exec('./BadBehavior/main.cs'); - -// Load our supporting weapon script, it contains methods used by all weapons. -exec("./weapon.cs"); - -// Load our weapon scripts -// We only need weapon scripts for those weapons that work differently from the -// class methods defined in weapon.cs -exec("./proximityMine.cs"); - -// Load our default player script -exec("./player.cs"); - -// Load our player scripts -exec("./aiPlayer.cs"); - -exec("./vehicle.cs"); -exec("./vehicleWheeled.cs"); -exec("./cheetah.cs"); - -// Load turret support scripts -exec("./turret.cs"); - -// Load our gametypes -exec("./gameCore.cs"); // This is the 'core' of the gametype functionality. -exec("./gameDM.cs"); // Overrides GameCore with DeathMatch functionality. diff --git a/Templates/Modules/badBehaviour/badBehaviour.cs b/Templates/Modules/badBehaviour/badBehaviour.cs new file mode 100644 index 0000000000..50b85de86c --- /dev/null +++ b/Templates/Modules/badBehaviour/badBehaviour.cs @@ -0,0 +1,20 @@ +//----------------------------------------------------------------------------- +// Module creation functions. +//----------------------------------------------------------------------------- + +function badBehaviour::create( %this ) +{ +} + +function badBehaviour::destroy( %this ) +{ + +} + +function badBehaviour::initClient( %this ) +{ + %this.queueExec("scripts/server/behaviorTreeManager.cs"); + %this.queueExec("scripts/server/badBot.cs"); + %this.queueExec("scripts/server/botMatch.cs"); +} + diff --git a/Templates/Modules/badBehaviour/badBehaviour.module b/Templates/Modules/badBehaviour/badBehaviour.module new file mode 100644 index 0000000000..cd6fc196f4 --- /dev/null +++ b/Templates/Modules/badBehaviour/badBehaviour.module @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/Templates/Full/game/levels/BehaviorTestbed.mis b/Templates/Modules/badBehaviour/levels/BehaviorTestbed.mis similarity index 100% rename from Templates/Full/game/levels/BehaviorTestbed.mis rename to Templates/Modules/badBehaviour/levels/BehaviorTestbed.mis diff --git a/Templates/Full/game/levels/BehaviorTestbed_preview.png b/Templates/Modules/badBehaviour/levels/BehaviorTestbed_preview.png similarity index 100% rename from Templates/Full/game/levels/BehaviorTestbed_preview.png rename to Templates/Modules/badBehaviour/levels/BehaviorTestbed_preview.png diff --git a/Templates/Full/game/scripts/server/BadBehavior/BadBot.cs b/Templates/Modules/badBehaviour/scripts/server/badBot.cs similarity index 100% rename from Templates/Full/game/scripts/server/BadBehavior/BadBot.cs rename to Templates/Modules/badBehaviour/scripts/server/badBot.cs diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTreeManager.cs b/Templates/Modules/badBehaviour/scripts/server/behaviorTreeManager.cs similarity index 96% rename from Templates/Full/game/scripts/server/BadBehavior/behaviorTreeManager.cs rename to Templates/Modules/badBehaviour/scripts/server/behaviorTreeManager.cs index ca6798af87..dc51db5758 100644 --- a/Templates/Full/game/scripts/server/BadBehavior/behaviorTreeManager.cs +++ b/Templates/Modules/badBehaviour/scripts/server/behaviorTreeManager.cs @@ -59,17 +59,17 @@ function BehaviorTreeManager::loadTrees(%this) { - if(!isDirectory("./BehaviorTrees")) + if(!isDirectory("./behaviorTrees")) return; pushInstantGroup(BehaviorTreeGroup); - %pattern = "./BehaviorTrees/*.cs"; + %pattern = "./behaviorTrees/*.cs"; %file = findFirstFile( %pattern ); if ( %file $= "" ) { // Try for DSOs next. - %pattern = "./BehaviorTrees/*.cs.dso"; + %pattern = "./behaviorTrees/*.cs.dso"; %file = findFirstFile( %pattern ); } diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/botMatchTree.cs b/Templates/Modules/badBehaviour/scripts/server/behaviorTrees/botMatchTree.cs similarity index 100% rename from Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/botMatchTree.cs rename to Templates/Modules/badBehaviour/scripts/server/behaviorTrees/botMatchTree.cs diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/botTree.cs b/Templates/Modules/badBehaviour/scripts/server/behaviorTrees/botTree.cs similarity index 100% rename from Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/botTree.cs rename to Templates/Modules/badBehaviour/scripts/server/behaviorTrees/botTree.cs diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/combatTree.cs b/Templates/Modules/badBehaviour/scripts/server/behaviorTrees/combatTree.cs similarity index 100% rename from Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/combatTree.cs rename to Templates/Modules/badBehaviour/scripts/server/behaviorTrees/combatTree.cs diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/FollowTree.cs b/Templates/Modules/badBehaviour/scripts/server/behaviorTrees/followTree.cs similarity index 100% rename from Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/FollowTree.cs rename to Templates/Modules/badBehaviour/scripts/server/behaviorTrees/followTree.cs diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/getHealthTree.cs b/Templates/Modules/badBehaviour/scripts/server/behaviorTrees/getHealthTree.cs similarity index 100% rename from Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/getHealthTree.cs rename to Templates/Modules/badBehaviour/scripts/server/behaviorTrees/getHealthTree.cs diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/PatrolTree.cs b/Templates/Modules/badBehaviour/scripts/server/behaviorTrees/patrolTree.cs similarity index 100% rename from Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/PatrolTree.cs rename to Templates/Modules/badBehaviour/scripts/server/behaviorTrees/patrolTree.cs diff --git a/Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/WanderTree.cs b/Templates/Modules/badBehaviour/scripts/server/behaviorTrees/wanderTree.cs similarity index 100% rename from Templates/Full/game/scripts/server/BadBehavior/behaviorTrees/WanderTree.cs rename to Templates/Modules/badBehaviour/scripts/server/behaviorTrees/wanderTree.cs diff --git a/Templates/Full/game/scripts/server/BadBehavior/botMatch.cs b/Templates/Modules/badBehaviour/scripts/server/botMatch.cs similarity index 100% rename from Templates/Full/game/scripts/server/BadBehavior/botMatch.cs rename to Templates/Modules/badBehaviour/scripts/server/botMatch.cs From 992c58efc806622caabb4872f6d801162ae8e0b8 Mon Sep 17 00:00:00 2001 From: Lukas Aldershaab Date: Sat, 3 Oct 2020 23:50:27 +0200 Subject: [PATCH 9/9] Convert ConsoleMethod to DefineEngineMethod --- Engine/source/BadBehavior/tools/BTUndoActions.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Engine/source/BadBehavior/tools/BTUndoActions.cpp b/Engine/source/BadBehavior/tools/BTUndoActions.cpp index b0d735b7e4..8358f5e8ef 100644 --- a/Engine/source/BadBehavior/tools/BTUndoActions.cpp +++ b/Engine/source/BadBehavior/tools/BTUndoActions.cpp @@ -92,11 +92,11 @@ void BTDeleteUndoAction::deleteObject( SimObject *object ) object->deleteObject(); } -ConsoleMethod( BTDeleteUndoAction, deleteObject, void, 3, 3, "( SimObject obj )") +DefineEngineMethod( BTDeleteUndoAction, deleteObject, void, ( SimObject* obj ),, "") { - SimObject *obj = NULL; - if ( Sim::findObject( argv[2], obj ) && obj ) - object->deleteObject( obj ); + if (obj != NULL) { + object->deleteObject(obj); + } } void BTDeleteUndoAction::undo()