621 lines
19 KiB
C
621 lines
19 KiB
C
#include "behaviour_tree.h"
|
|
|
|
#include "../memory/memory.h"
|
|
#include "../util/object_pool.h"
|
|
|
|
//#define GET_NODE(idx) ((BzAIBTNode *) bzObjectPoolGetObject(bt->nodePool, idx))
|
|
|
|
struct BzBTNode {
|
|
BzBTNode *parent;
|
|
// Children
|
|
BzBTNode *first;
|
|
BzBTNode *last;
|
|
// Siblings
|
|
BzBTNode *prev;
|
|
BzBTNode *next;
|
|
|
|
BzBTNodeType type;
|
|
const char *name;
|
|
union {
|
|
struct {
|
|
i32 n;
|
|
} repeat;
|
|
struct {
|
|
f32 ms;
|
|
} delay;
|
|
struct {
|
|
BzBTActionFn fn;
|
|
} action;
|
|
} as;
|
|
};
|
|
|
|
struct BzBTNodeState {
|
|
const BzBTNode *node;
|
|
BzBTNodeState *next;
|
|
BzBTNodeState *prev;
|
|
|
|
union {
|
|
struct {
|
|
BzBTNode *running;
|
|
} composite;
|
|
struct {
|
|
i32 iter;
|
|
} repeat;
|
|
struct {
|
|
f32 elapsed;
|
|
} delay;
|
|
} as;
|
|
};
|
|
|
|
|
|
size_t bzBTGetNodeSize() {
|
|
return sizeof(BzBTNode);
|
|
}
|
|
size_t bzBTGetNodeStateSize() {
|
|
return sizeof(BzBTNodeState);
|
|
}
|
|
|
|
const char *bzBTNodeTypeToStr(BzBTNodeType type) {
|
|
switch (type) {
|
|
case BZ_BT_COMP_SELECTOR:
|
|
return "SELECTOR";
|
|
case BZ_BT_COMP_SEQUENCE:
|
|
return "SEQUENCE";
|
|
case BZ_BT_COMP_PARALLEL_SELECTOR:
|
|
return "P_SELECTOR";
|
|
case BZ_BT_COMP_PARALLEL_SEQUENCE:
|
|
return "P_SEQUENCE";
|
|
case BZ_BT_DECOR_DUMMY:
|
|
return "DUMMY";
|
|
case BZ_BT_DECOR_SUCCESS:
|
|
return "RETURN_SUCCESS";
|
|
case BZ_BT_DECOR_FAIL:
|
|
return "RETURN_FAIL";
|
|
case BZ_BT_DECOR_INVERT:
|
|
return "INVERT";
|
|
case BZ_BT_DECOR_UNTIL_SUCCESS:
|
|
return "UNTIL_SUCCESS";
|
|
case BZ_BT_DECOR_UNTIL_FAIL:
|
|
return "UNTIL_FAIL";
|
|
case BZ_BT_DECOR_REPEAT:
|
|
return "REPEAT";
|
|
case BZ_BT_DECOR_DELAY:
|
|
return "DELAY";
|
|
case BZ_BT_ACTION:
|
|
return "ACTION";
|
|
}
|
|
}
|
|
|
|
static BzBTNode *bzBTNodeMake(BzObjectPool *nodePool, BzBTNode *parent, BzBTNodeType type) {
|
|
BZ_ASSERT(nodePool);
|
|
BZ_ASSERT(bzObjectPoolGetObjectSize(nodePool) == bzBTGetNodeSize());
|
|
BzBTNode *node = bzObjectPool(nodePool);
|
|
bzMemSet(node, 0, sizeof(*node));
|
|
|
|
node->type = type;
|
|
|
|
if (parent && parent->last) {
|
|
parent->last->next = node;
|
|
node->prev = parent->last;
|
|
parent->last = node;
|
|
} else if (parent) {
|
|
parent->first = node;
|
|
parent->last = node;
|
|
}
|
|
node->parent = parent;
|
|
|
|
return node;
|
|
}
|
|
|
|
BzBTNode *bzBTMakeRoot(BzObjectPool *nodePool) {
|
|
return bzBTNodeMake(nodePool, NULL, BZ_BT_DECOR_DUMMY);
|
|
}
|
|
|
|
void bzBTDestroyRoot(BzObjectPool *nodePool, BzBTNode *node) {
|
|
BZ_ASSERT(node);
|
|
BzBTNode *pNode = node;
|
|
|
|
while (pNode) {
|
|
BzBTNode *next = pNode->next;
|
|
bzBTDestroyRoot(nodePool, pNode);
|
|
pNode = next;
|
|
}
|
|
|
|
bzObjectPoolRelease(nodePool, node);
|
|
}
|
|
|
|
BzBTNode *bzBTCompSelector(BzObjectPool *nodePool, BzBTNode *parent, bool parallel) {
|
|
BzBTNodeType type = parallel ?
|
|
BZ_BT_COMP_PARALLEL_SELECTOR :
|
|
BZ_BT_COMP_SELECTOR;
|
|
return bzBTNodeMake(nodePool, parent, type);
|
|
}
|
|
BzBTNode *bzBTCompSequence(BzObjectPool *nodePool, BzBTNode *parent, bool parallel) {
|
|
BzBTNodeType type = parallel ?
|
|
BZ_BT_COMP_PARALLEL_SEQUENCE :
|
|
BZ_BT_COMP_SEQUENCE;
|
|
return bzBTNodeMake(nodePool, parent, type);
|
|
}
|
|
|
|
BzBTNode *bzBTDecorDummy(BzObjectPool *nodePool, BzBTNode *parent) {
|
|
return bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_DUMMY);
|
|
}
|
|
BzBTNode *bzBTDecorSuccess(BzObjectPool *nodePool, BzBTNode *parent) {
|
|
return bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_SUCCESS);
|
|
}
|
|
BzBTNode *bzBTDecorFail(BzObjectPool *nodePool, BzBTNode *parent) {
|
|
return bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_FAIL);
|
|
}
|
|
BzBTNode *bzBTDecorInvert(BzObjectPool *nodePool, BzBTNode *parent) {
|
|
return bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_INVERT);
|
|
}
|
|
BzBTNode *bzBTDecorUntilSuccess(BzObjectPool *nodePool, BzBTNode *parent) {
|
|
return bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_UNTIL_SUCCESS);
|
|
}
|
|
BzBTNode *bzBTDecorUntilFail(BzObjectPool *nodePool, BzBTNode *parent) {
|
|
return bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_UNTIL_FAIL);
|
|
}
|
|
BzBTNode *bzBTDecorRepeat(BzObjectPool *nodePool, BzBTNode *parent, i32 n) {
|
|
BzBTNode *node = bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_REPEAT);
|
|
node->as.repeat.n = n;
|
|
return node;
|
|
}
|
|
BzBTNode *bzBTDecorDelay(BzObjectPool *nodePool, BzBTNode *parent, f32 ms) {
|
|
BzBTNode *node = bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_DELAY);
|
|
node->as.delay.ms = ms;
|
|
return node;
|
|
}
|
|
|
|
BzBTNode *bzBTAction(BzObjectPool *nodePool, BzBTNode *parent, BzBTActionFn fn) {
|
|
BzBTNode *node = bzBTNodeMake(nodePool, parent, BZ_BT_ACTION);
|
|
node->as.action.fn = fn;
|
|
return node;
|
|
}
|
|
|
|
void bzBTNodeSetName(BzBTNode *node, const char *name) {
|
|
node->name = name;
|
|
}
|
|
const char *bzBTNodeGetName(const BzBTNode *node) {
|
|
return node->name;
|
|
}
|
|
|
|
i32 bzBTDecorGetRepeat(const BzBTNode *node) {
|
|
BZ_ASSERT(node->type == BZ_BT_DECOR_REPEAT);
|
|
return node->as.repeat.n;
|
|
}
|
|
f32 bzBTDecorGetDelay(const BzBTNode *node) {
|
|
BZ_ASSERT(node->type == BZ_BT_DECOR_DELAY);
|
|
return node->as.delay.ms;
|
|
}
|
|
|
|
BzBTActionFn bzBTActionGetFn(const BzBTNode *node) {
|
|
BZ_ASSERT(node->type == BZ_BT_ACTION);
|
|
return node->as.action.fn;
|
|
}
|
|
|
|
BzBTNodeType bzBTGetNodeType(const BzBTNode *node) {
|
|
return node->type;
|
|
}
|
|
BzBTNode *bzBTNodeChild(const BzBTNode *node) {
|
|
return node->first;
|
|
}
|
|
BzBTNode *bzBTNodeNext(const BzBTNode *node) {
|
|
return node->next;
|
|
}
|
|
|
|
const BzBTNodeState *bzBTNodeStateNext(const BzBTNodeState *state) {
|
|
BZ_ASSERT(state);
|
|
return state->next;
|
|
}
|
|
bool bzBTNodeMatchesState(const BzBTNode *node, const BzBTNodeState *state) {
|
|
return state && state->node == node;
|
|
}
|
|
|
|
BzBTNode *bzBTCompStateGetRunningChild(const BzBTNodeState *state) {
|
|
BZ_ASSERT(state->node);
|
|
BzBTNodeType type = state->node->type;
|
|
bool isComposite = type == BZ_BT_COMP_SELECTOR ||
|
|
type == BZ_BT_COMP_PARALLEL_SELECTOR ||
|
|
type == BZ_BT_COMP_SEQUENCE ||
|
|
type == BZ_BT_COMP_PARALLEL_SEQUENCE;
|
|
BZ_ASSERT(isComposite);
|
|
return state->as.composite.running;
|
|
}
|
|
|
|
i32 bzBTRepeatStateGetIter(const BzBTNodeState *state) {
|
|
BZ_ASSERT(state->node && state->node->type == BZ_BT_DECOR_REPEAT);
|
|
return state->as.repeat.iter;
|
|
}
|
|
f32 bzBTDelayStateGetElapsed(const BzBTNodeState *state) {
|
|
BZ_ASSERT(state->node && state->node->type == BZ_BT_DECOR_DELAY);
|
|
return state->as.delay.elapsed;
|
|
}
|
|
|
|
BzBTState bzBTCreateState(const BzBTStateDesc *desc) {
|
|
BZ_ASSERT(desc->pool);
|
|
BZ_ASSERT(bzObjectPoolGetObjectSize(desc->pool) == bzBTGetNodeStateSize());
|
|
BZ_ASSERT(desc->root);
|
|
return (BzBTState) {
|
|
.root = desc->root,
|
|
._first = NULL,
|
|
._last = NULL,
|
|
.nodeStatePool = desc->pool,
|
|
.userData = desc->userData
|
|
};
|
|
}
|
|
void bzBTDestroyState(BzBTState *state) {
|
|
BzBTNodeState *pNodeState = state->_first;
|
|
while (pNodeState) {
|
|
BzBTNodeState *next = pNodeState->next;
|
|
bzObjectPoolRelease(state->nodeStatePool, pNodeState);
|
|
pNodeState = next;
|
|
}
|
|
bzMemSet(state, 0, sizeof(*state));
|
|
}
|
|
|
|
typedef struct BzBTExecState {
|
|
BzBTNodeState *first;
|
|
BzBTNodeState *last;
|
|
void *userData;
|
|
} BzBTExecState;
|
|
typedef struct BzBTExecReturn {
|
|
BzBTStatus status;
|
|
BzBTExecState state;
|
|
} BzBTExecReturn;
|
|
|
|
BzBTNodeState *execStatePopFront(BzBTExecState *state) {
|
|
BZ_ASSERT(state->first);
|
|
BzBTNodeState *popped = state->first;
|
|
|
|
if (state->first)
|
|
state->first = state->first->next;
|
|
if (state->first == NULL)
|
|
state->last = NULL;
|
|
|
|
popped->prev = NULL;
|
|
popped->next = NULL;
|
|
return popped;
|
|
}
|
|
void execStatePushFront(BzBTExecState *state, BzBTNodeState *nodeState) {
|
|
BZ_ASSERT(state);
|
|
|
|
nodeState->prev = NULL;
|
|
nodeState->next = state->first;
|
|
|
|
if (state->first) {
|
|
state->first->prev = nodeState;
|
|
} else {
|
|
state->first = nodeState;
|
|
state->last = nodeState;
|
|
}
|
|
state->first = nodeState;
|
|
}
|
|
void execStatePushBack(BzBTExecState *state, BzBTNodeState *nodeState) {
|
|
BZ_ASSERT(state);
|
|
|
|
nodeState->next = NULL;
|
|
nodeState->prev = state->last;
|
|
|
|
if (state->last) {
|
|
state->last->next = nodeState;
|
|
} else {
|
|
state->first = nodeState;
|
|
state->last = nodeState;
|
|
}
|
|
state->last = nodeState;
|
|
}
|
|
void execStateClear(BzBTExecState *state, BzObjectPool *pool) {
|
|
BzBTNodeState *pState = state->first;
|
|
while (pState) {
|
|
BzBTNodeState *next = pState->next;
|
|
bzObjectPoolRelease(pool, pState);
|
|
pState = next;
|
|
}
|
|
state->first = NULL;
|
|
state->last = NULL;
|
|
}
|
|
void execStateMerge(BzBTExecState *state, BzBTExecState *toMerge) {
|
|
BzBTNodeState *pState = toMerge->first;
|
|
while (pState) {
|
|
BzBTNodeState *next = pState->next;
|
|
execStatePushBack(state, pState);
|
|
pState = next;
|
|
}
|
|
|
|
toMerge->first = NULL;
|
|
toMerge->last = NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* @param node
|
|
* @param state
|
|
* @return
|
|
*/
|
|
bool nodeMatchesState(const BzBTNode *node, const BzBTExecState *state) {
|
|
return state->first && state->first->node == node;
|
|
}
|
|
/**
|
|
* Return BzBTNodeState for given node, if node matches first state node.
|
|
*/
|
|
static BzBTNodeState *getNodeState(const BzBTNode *node, BzBTExecState *state) {
|
|
if (nodeMatchesState(node, state)) {
|
|
return execStatePopFront(state);
|
|
}
|
|
return NULL;
|
|
}
|
|
static BzBTNodeState *ensureValidNodeState(const BzBTNode *node, BzObjectPool *pool, BzBTNodeState *existing) {
|
|
if (existing)
|
|
return existing;
|
|
|
|
existing = bzObjectPool(pool);
|
|
existing->node = node;
|
|
existing->prev = NULL;
|
|
existing->next = NULL;
|
|
bzMemSet(&existing->as, 0, sizeof(existing->as));
|
|
return existing;
|
|
}
|
|
static void releaseNodeState(BzObjectPool *pool, BzBTNodeState *nodeState) {
|
|
bzObjectPoolRelease(pool, nodeState);
|
|
}
|
|
|
|
static inline BzBTExecReturn bzBTExecuteNode(const BzBTNode *node, f32 dt,
|
|
BzBTExecState *nodeState, BzObjectPool *statePool);
|
|
static inline BzBTExecReturn bzBTExecuteComposite(const BzBTNode *node, f32 dt,
|
|
BzBTExecState *state, BzObjectPool *statePool) {
|
|
BzBTNodeState *nodeState = getNodeState(node, state);
|
|
|
|
bool isParallel = node->type == BZ_BT_COMP_PARALLEL_SEQUENCE ||
|
|
node->type == BZ_BT_COMP_PARALLEL_SELECTOR;
|
|
|
|
BzBTNode *start = node->first;
|
|
if (!isParallel && nodeState)
|
|
start = nodeState->as.composite.running;
|
|
|
|
i32 numRunning = 0;
|
|
i32 numSuccessful = 0;
|
|
i32 numFailed = 0;
|
|
i32 numChildren = 0;
|
|
BzBTExecReturn execReturn = { .status = BZ_BT_ERROR };
|
|
BzBTNode *child = start;
|
|
for (;child; child = child->next) {
|
|
BzBTExecReturn childReturn = bzBTExecuteNode(child, dt, state, statePool);
|
|
if (childReturn.status == BZ_BT_RUNNING) {
|
|
execStateMerge(&execReturn.state, &childReturn.state);
|
|
}
|
|
BZ_ASSERT(childReturn.state.first == NULL);
|
|
numChildren++;
|
|
switch (childReturn.status) {
|
|
case BZ_BT_RUNNING:
|
|
numRunning++;
|
|
break;
|
|
case BZ_BT_SUCCESS:
|
|
numSuccessful++;
|
|
break;
|
|
case BZ_BT_FAIL:
|
|
numFailed++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
switch (node->type) {
|
|
case BZ_BT_COMP_SELECTOR:
|
|
case BZ_BT_COMP_PARALLEL_SELECTOR:
|
|
if (childReturn.status == BZ_BT_SUCCESS)
|
|
execReturn.status = BZ_BT_SUCCESS;
|
|
break;
|
|
case BZ_BT_COMP_SEQUENCE:
|
|
case BZ_BT_COMP_PARALLEL_SEQUENCE:
|
|
if (childReturn.status == BZ_BT_FAIL)
|
|
execReturn.status = BZ_BT_FAIL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (execReturn.status == BZ_BT_FAIL || execReturn.status == BZ_BT_SUCCESS)
|
|
break;
|
|
if (numRunning > 0 && !isParallel) {
|
|
execReturn.status = BZ_BT_RUNNING;
|
|
break;
|
|
}
|
|
}
|
|
switch (node->type) {
|
|
case BZ_BT_COMP_SELECTOR:
|
|
case BZ_BT_COMP_PARALLEL_SELECTOR:
|
|
if (numFailed == numChildren)
|
|
execReturn.status = BZ_BT_FAIL;
|
|
break;
|
|
case BZ_BT_COMP_SEQUENCE:
|
|
case BZ_BT_COMP_PARALLEL_SEQUENCE:
|
|
if (numSuccessful == numChildren)
|
|
execReturn.status = BZ_BT_SUCCESS;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Propagate ERROR up
|
|
if (execReturn.status == BZ_BT_ERROR) {
|
|
execStateClear(&execReturn.state, statePool);
|
|
execReturn.status = BZ_BT_ERROR;
|
|
return execReturn;
|
|
}
|
|
|
|
bool finished = execReturn.status == BZ_BT_SUCCESS ||
|
|
execReturn.status == BZ_BT_FAIL;
|
|
if (finished) {
|
|
BZ_ASSERT(execReturn.state.first == NULL);
|
|
if (nodeState) releaseNodeState(statePool, nodeState);
|
|
return execReturn;
|
|
}
|
|
BZ_ASSERT(execReturn.status == BZ_BT_RUNNING);
|
|
nodeState = ensureValidNodeState(node, statePool, nodeState);
|
|
nodeState->as.composite.running = child;
|
|
execStatePushFront(&execReturn.state, nodeState);
|
|
|
|
return execReturn;
|
|
}
|
|
static inline BzBTExecReturn bzBTExecuteDecorator(const BzBTNode *node, f32 dt,
|
|
BzBTExecState *state, BzObjectPool *statePool) {
|
|
// Ensure decorator has only one child, if any
|
|
BZ_ASSERT(!node->first || node->first == node->last);
|
|
|
|
BzBTNodeState *nodeState = getNodeState(node, state);
|
|
|
|
BzBTExecReturn execReturn = { .status = BZ_BT_ERROR };
|
|
|
|
switch (node->type) {
|
|
case BZ_BT_DECOR_REPEAT:
|
|
nodeState = ensureValidNodeState(node, statePool, nodeState);
|
|
execStatePushBack(&execReturn.state, nodeState);
|
|
break;
|
|
case BZ_BT_DECOR_DELAY:
|
|
nodeState = ensureValidNodeState(node, statePool, nodeState);
|
|
nodeState->as.delay.elapsed += dt;
|
|
execStatePushBack(&execReturn.state, nodeState);
|
|
if (nodeState->as.delay.elapsed < node->as.delay.ms) {
|
|
execReturn.status = BZ_BT_RUNNING;
|
|
return execReturn;
|
|
}
|
|
BZ_ASSERT(nodeState == execStatePopFront(&execReturn.state));
|
|
releaseNodeState(statePool, nodeState);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Implicit success, if no children are present
|
|
BzBTExecReturn childReturn = { .status = BZ_BT_SUCCESS };
|
|
if (node->first)
|
|
childReturn = bzBTExecuteNode(node->first, dt, state, statePool);
|
|
|
|
// Propagate ERROR, RUNNING up
|
|
if (childReturn.status == BZ_BT_ERROR) {
|
|
execStateClear(&childReturn.state, statePool);
|
|
execStateClear(&execReturn.state, statePool);
|
|
execReturn.status = BZ_BT_ERROR;
|
|
return execReturn;
|
|
|
|
}
|
|
if (childReturn.status == BZ_BT_RUNNING) {
|
|
execStateMerge(&execReturn.state, &childReturn.state);
|
|
execReturn.status = BZ_BT_RUNNING;
|
|
return execReturn;
|
|
}
|
|
BZ_ASSERT(childReturn.state.first == NULL);
|
|
|
|
switch (node->type) {
|
|
case BZ_BT_DECOR_DUMMY:
|
|
case BZ_BT_DECOR_DELAY: // Delay already handled
|
|
execReturn.status = childReturn.status;
|
|
break;
|
|
case BZ_BT_DECOR_SUCCESS:
|
|
execReturn.status = BZ_BT_SUCCESS;
|
|
break;
|
|
case BZ_BT_DECOR_FAIL:
|
|
execReturn.status = BZ_BT_FAIL;
|
|
break;
|
|
case BZ_BT_DECOR_INVERT:
|
|
if (childReturn.status == BZ_BT_FAIL)
|
|
execReturn.status = BZ_BT_SUCCESS;
|
|
if (childReturn.status == BZ_BT_SUCCESS)
|
|
execReturn.status = BZ_BT_FAIL;
|
|
break;
|
|
case BZ_BT_DECOR_UNTIL_SUCCESS:
|
|
if (childReturn.status == BZ_BT_SUCCESS)
|
|
execReturn.status = BZ_BT_SUCCESS;
|
|
else
|
|
execReturn.status = BZ_BT_RUNNING;
|
|
break;
|
|
case BZ_BT_DECOR_UNTIL_FAIL:
|
|
if (childReturn.status == BZ_BT_FAIL)
|
|
execReturn.status = BZ_BT_SUCCESS;
|
|
else
|
|
execReturn.status = BZ_BT_RUNNING;
|
|
break;
|
|
case BZ_BT_DECOR_REPEAT:
|
|
BZ_ASSERT(nodeMatchesState(node, &execReturn.state));
|
|
BZ_ASSERT(nodeState);
|
|
nodeState->as.repeat.iter++;
|
|
if (nodeState->as.repeat.iter >= node->as.repeat.n) {
|
|
BZ_ASSERT(nodeState == execStatePopFront(&execReturn.state));
|
|
releaseNodeState(statePool, nodeState);
|
|
execReturn.status = childReturn.status;
|
|
break;
|
|
}
|
|
execReturn.status = BZ_BT_RUNNING;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return execReturn;
|
|
}
|
|
static inline BzBTExecReturn bzBTExecuteNode(const BzBTNode *node, f32 dt,
|
|
BzBTExecState *nodeState, BzObjectPool *statePool) {
|
|
BzBTExecReturn execReturn = { .status = BZ_BT_ERROR };
|
|
switch (node->type) {
|
|
case BZ_BT_COMP_SELECTOR:
|
|
case BZ_BT_COMP_SEQUENCE:
|
|
case BZ_BT_COMP_PARALLEL_SELECTOR:
|
|
case BZ_BT_COMP_PARALLEL_SEQUENCE:
|
|
execReturn = bzBTExecuteComposite(node, dt, nodeState, statePool);
|
|
break;
|
|
case BZ_BT_DECOR_DUMMY:
|
|
case BZ_BT_DECOR_SUCCESS:
|
|
case BZ_BT_DECOR_FAIL:
|
|
case BZ_BT_DECOR_INVERT:
|
|
case BZ_BT_DECOR_UNTIL_SUCCESS:
|
|
case BZ_BT_DECOR_UNTIL_FAIL:
|
|
case BZ_BT_DECOR_REPEAT:
|
|
case BZ_BT_DECOR_DELAY:
|
|
execReturn = bzBTExecuteDecorator(node, dt, nodeState, statePool);
|
|
break;
|
|
case BZ_BT_ACTION:
|
|
BZ_ASSERT(node->as.action.fn);
|
|
execReturn.status = node->as.action.fn(nodeState->userData, dt);
|
|
break;
|
|
}
|
|
return execReturn;
|
|
}
|
|
|
|
BzBTStatus bzBTExecute(BzBTState *state, f32 dt) {
|
|
BZ_ASSERT(state->nodeStatePool);
|
|
BZ_ASSERT(bzObjectPoolGetObjectSize(state->nodeStatePool) == bzBTGetNodeStateSize());
|
|
BZ_ASSERT(state);
|
|
if (state->root == NULL) {
|
|
return BZ_BT_FAIL;
|
|
}
|
|
|
|
BzBTNodeState *first = state->_first;
|
|
const BzBTNode *firstNode = first ? first->node : state->root;
|
|
BzBTExecState execState = {
|
|
.first = state->_first,
|
|
.last = state->_last,
|
|
.userData = state->userData
|
|
};
|
|
BzBTExecReturn execReturn = bzBTExecuteNode(firstNode, dt, &execState, state->nodeStatePool);
|
|
if (execReturn.status == BZ_BT_ERROR)
|
|
execStateClear(&execReturn.state, state->nodeStatePool);
|
|
BZ_ASSERT(execState.first == NULL && execState.last == NULL);
|
|
|
|
state->_first = execReturn.state.first;
|
|
state->_last = execReturn.state.last;
|
|
|
|
switch (execReturn.status) {
|
|
case BZ_BT_SUCCESS:
|
|
state->onSuccess && state->onSuccess(state->userData, dt);
|
|
break;
|
|
case BZ_BT_FAIL:
|
|
state->onFailure && state->onFailure(state->userData, dt);
|
|
break;
|
|
case BZ_BT_ERROR:
|
|
state->onError && state->onError(state->userData, dt);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return execReturn.status;
|
|
}
|