feat: add measureBehaviorTree and behaviorRoleName to Behavior
This commit is contained in:
1 parent
0f96fb55ff
commit
f1278f9dcc
1 file changed
+158
@@ -309,3 +309,161 @@ BehaviorStatus InverterNode::onTick(BehaviorContext& ctx) {
|
|||||||
return BehaviorStatus::Running;
|
return BehaviorStatus::Running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RepeaterNode::RepeaterNode(std::string name, std::unique_ptr<BehaviorNode> child, int limit,
|
||||||
|
bool stopWhenChildFails)
|
||||||
|
: DecoratorNode(std::move(name), std::move(child)),
|
||||||
|
repeatLimit(limit),
|
||||||
|
stopOnFailure(stopWhenChildFails) {}
|
||||||
|
|
||||||
|
BehaviorStatus RepeaterNode::onTick(BehaviorContext& ctx) {
|
||||||
|
BehaviorStatus status = child().tick(ctx);
|
||||||
|
if (status == BehaviorStatus::Running) return BehaviorStatus::Running;
|
||||||
|
if (status == BehaviorStatus::Failure && stopOnFailure) {
|
||||||
|
completed = 0;
|
||||||
|
return BehaviorStatus::Failure;
|
||||||
|
}
|
||||||
|
++completed;
|
||||||
|
if (completed >= repeatLimit) {
|
||||||
|
completed = 0;
|
||||||
|
return BehaviorStatus::Success;
|
||||||
|
}
|
||||||
|
return BehaviorStatus::Running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeaterNode::onReset() { completed = 0; }
|
||||||
|
|
||||||
|
RetryUntilSuccessNode::RetryUntilSuccessNode(std::string name, std::unique_ptr<BehaviorNode> child,
|
||||||
|
int attemptLimit)
|
||||||
|
: DecoratorNode(std::move(name), std::move(child)), maxAttempts(attemptLimit) {}
|
||||||
|
|
||||||
|
BehaviorStatus RetryUntilSuccessNode::onTick(BehaviorContext& ctx) {
|
||||||
|
BehaviorStatus status = child().tick(ctx);
|
||||||
|
if (status == BehaviorStatus::Running) return BehaviorStatus::Running;
|
||||||
|
if (status == BehaviorStatus::Success) {
|
||||||
|
attempts = 0;
|
||||||
|
return BehaviorStatus::Success;
|
||||||
|
}
|
||||||
|
++attempts;
|
||||||
|
if (attempts >= maxAttempts) {
|
||||||
|
attempts = 0;
|
||||||
|
return BehaviorStatus::Failure;
|
||||||
|
}
|
||||||
|
child().reset();
|
||||||
|
return BehaviorStatus::Running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RetryUntilSuccessNode::onReset() { attempts = 0; }
|
||||||
|
|
||||||
|
CooldownNode::CooldownNode(std::string name, std::unique_ptr<BehaviorNode> child, int ticks)
|
||||||
|
: DecoratorNode(std::move(name), std::move(child)), cooldownTicks(ticks) {}
|
||||||
|
|
||||||
|
BehaviorStatus CooldownNode::onTick(BehaviorContext& ctx) {
|
||||||
|
if (remaining > 0) {
|
||||||
|
--remaining;
|
||||||
|
return BehaviorStatus::Failure;
|
||||||
|
}
|
||||||
|
BehaviorStatus status = child().tick(ctx);
|
||||||
|
if (status != BehaviorStatus::Running) remaining = cooldownTicks;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CooldownNode::onReset() { remaining = 0; }
|
||||||
|
|
||||||
|
TimeLimitNode::TimeLimitNode(std::string name, std::unique_ptr<BehaviorNode> child, int ticks)
|
||||||
|
: DecoratorNode(std::move(name), std::move(child)), limitTicks(ticks) {}
|
||||||
|
|
||||||
|
BehaviorStatus TimeLimitNode::onTick(BehaviorContext& ctx) {
|
||||||
|
++elapsed;
|
||||||
|
BehaviorStatus status = child().tick(ctx);
|
||||||
|
if (status != BehaviorStatus::Running) {
|
||||||
|
elapsed = 0;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
if (elapsed >= limitTicks) {
|
||||||
|
elapsed = 0;
|
||||||
|
child().reset();
|
||||||
|
return BehaviorStatus::Failure;
|
||||||
|
}
|
||||||
|
return BehaviorStatus::Running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeLimitNode::onReset() { elapsed = 0; }
|
||||||
|
|
||||||
|
BehaviorTreeStats measureBehaviorTree(const BehaviorNode* root) {
|
||||||
|
BehaviorTreeStats stats;
|
||||||
|
if (root == nullptr) return stats;
|
||||||
|
|
||||||
|
std::vector<std::pair<const BehaviorNode*, int>> pending;
|
||||||
|
pending.push_back({root, 1});
|
||||||
|
while (!pending.empty()) {
|
||||||
|
std::pair<const BehaviorNode*, int> entry = pending.back();
|
||||||
|
pending.pop_back();
|
||||||
|
++stats.nodeCount;
|
||||||
|
if (entry.second > stats.depth) stats.depth = entry.second;
|
||||||
|
for (int i = 0; i < entry.first->childCount(); ++i) {
|
||||||
|
pending.push_back({entry.first->childAt(i), entry.second + 1});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stats.withinDepthLimit = stats.depth <= maxBehaviorDepth;
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
BehaviorTree::BehaviorTree(std::unique_ptr<BehaviorNode> newRoot) { setRoot(std::move(newRoot)); }
|
||||||
|
|
||||||
|
void BehaviorTree::setRoot(std::unique_ptr<BehaviorNode> newRoot) {
|
||||||
|
rootNode = std::move(newRoot);
|
||||||
|
treeStats = measureBehaviorTree(rootNode.get());
|
||||||
|
ticks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BehaviorStatus BehaviorTree::tick(BehaviorContext& ctx) {
|
||||||
|
if (!structureValid()) return BehaviorStatus::Failure;
|
||||||
|
++ticks;
|
||||||
|
++ctx.tick;
|
||||||
|
return rootNode->tick(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BehaviorTree::reset() {
|
||||||
|
if (rootNode) rootNode->reset();
|
||||||
|
ticks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* behaviorRoleName(BehaviorRole role) {
|
||||||
|
switch (role) {
|
||||||
|
case BehaviorRole::HostileHunter: return "hostileHunter";
|
||||||
|
case BehaviorRole::GrazingAnimal: return "grazingAnimal";
|
||||||
|
case BehaviorRole::SunAvoider: return "sunAvoider";
|
||||||
|
case BehaviorRole::Count: break;
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
BehaviorRole roleForSpecies(Species species) {
|
||||||
|
switch (species) {
|
||||||
|
case Species::Zombie:
|
||||||
|
case Species::Creeper:
|
||||||
|
return BehaviorRole::HostileHunter;
|
||||||
|
case Species::Skeleton:
|
||||||
|
return BehaviorRole::SunAvoider;
|
||||||
|
default:
|
||||||
|
return BehaviorRole::GrazingAnimal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemId breedingFoodFor(Species species) {
|
||||||
|
switch (species) {
|
||||||
|
case Species::Sheep:
|
||||||
|
case Species::Cow:
|
||||||
|
return ItemId::Bread;
|
||||||
|
case Species::Pig:
|
||||||
|
return ItemId::Apple;
|
||||||
|
default:
|
||||||
|
return ItemId::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isBreedingFood(Species species, ItemId held) {
|
||||||
|
ItemId wanted = breedingFoodFor(species);
|
||||||
|
return wanted != ItemId::None && held == wanted;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user