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;
|
||||
}
|
||||
|
||||
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