feat(vf): add BehaviorTreeStats to Behavior
This commit is contained in:
1 parent
0c2bf7857b
commit
20527b0a95
1 file changed
+127
@@ -313,3 +313,130 @@ private:
|
||||
int completed = 0;
|
||||
};
|
||||
|
||||
// Reports Running while the child keeps failing, up to `maxAttempts` tries.
|
||||
class RetryUntilSuccessNode : public DecoratorNode {
|
||||
public:
|
||||
RetryUntilSuccessNode(std::string nodeName, std::unique_ptr<BehaviorNode> child,
|
||||
int maxAttempts);
|
||||
|
||||
int attemptsSpent() const { return attempts; }
|
||||
|
||||
protected:
|
||||
BehaviorStatus onTick(BehaviorContext& ctx) override;
|
||||
void onReset() override;
|
||||
|
||||
private:
|
||||
int maxAttempts;
|
||||
int attempts = 0;
|
||||
};
|
||||
|
||||
// After the child settles, the next `cooldownTicks` ticks report Failure
|
||||
// without touching the child at all.
|
||||
class CooldownNode : public DecoratorNode {
|
||||
public:
|
||||
CooldownNode(std::string nodeName, std::unique_ptr<BehaviorNode> child, int cooldownTicks);
|
||||
|
||||
int remainingTicks() const { return remaining; }
|
||||
|
||||
protected:
|
||||
BehaviorStatus onTick(BehaviorContext& ctx) override;
|
||||
void onReset() override;
|
||||
|
||||
private:
|
||||
int cooldownTicks;
|
||||
int remaining = 0;
|
||||
};
|
||||
|
||||
// Gives the child `limitTicks` consecutive ticks to settle. On the tick that
|
||||
// exceeds the budget the child is reset and the decorator fails.
|
||||
class TimeLimitNode : public DecoratorNode {
|
||||
public:
|
||||
TimeLimitNode(std::string nodeName, std::unique_ptr<BehaviorNode> child, int limitTicks);
|
||||
|
||||
int elapsedTicks() const { return elapsed; }
|
||||
|
||||
protected:
|
||||
BehaviorStatus onTick(BehaviorContext& ctx) override;
|
||||
void onReset() override;
|
||||
|
||||
private:
|
||||
int limitTicks;
|
||||
int elapsed = 0;
|
||||
};
|
||||
|
||||
struct BehaviorTreeStats {
|
||||
int nodeCount = 0;
|
||||
int depth = 0;
|
||||
bool withinDepthLimit = true;
|
||||
};
|
||||
|
||||
// Owns a root and the structural measurement taken when it was installed.
|
||||
// Ticking an over-deep tree is refused rather than attempted.
|
||||
class BehaviorTree {
|
||||
public:
|
||||
BehaviorTree() = default;
|
||||
explicit BehaviorTree(std::unique_ptr<BehaviorNode> newRoot);
|
||||
|
||||
BehaviorTree(BehaviorTree&&) = default;
|
||||
BehaviorTree& operator=(BehaviorTree&&) = default;
|
||||
|
||||
void setRoot(std::unique_ptr<BehaviorNode> newRoot);
|
||||
BehaviorNode* root() const { return rootNode.get(); }
|
||||
|
||||
const BehaviorTreeStats& stats() const { return treeStats; }
|
||||
bool structureValid() const { return rootNode != nullptr && treeStats.withinDepthLimit; }
|
||||
|
||||
// Advances ctx.tick, then traverses. Returns Failure without traversing
|
||||
// when the structure was rejected.
|
||||
BehaviorStatus tick(BehaviorContext& ctx);
|
||||
void reset();
|
||||
|
||||
long long tickCount() const { return ticks; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<BehaviorNode> rootNode;
|
||||
BehaviorTreeStats treeStats;
|
||||
long long ticks = 0;
|
||||
};
|
||||
|
||||
// Measures a subtree without recursing, so a pathological tree is diagnosed
|
||||
// instead of crashing the measurement.
|
||||
BehaviorTreeStats measureBehaviorTree(const BehaviorNode* root);
|
||||
|
||||
enum class BehaviorRole : uint8_t {
|
||||
HostileHunter = 0,
|
||||
GrazingAnimal = 1,
|
||||
SunAvoider = 2,
|
||||
Count,
|
||||
};
|
||||
|
||||
const char* behaviorRoleName(BehaviorRole role);
|
||||
|
||||
// The role a freshly spawned species walks with. Spawning.h hands its results
|
||||
// straight to this so spawned mobs are driven by a tree, not by a state enum.
|
||||
BehaviorRole roleForSpecies(Species species);
|
||||
|
||||
// Wheat-equivalent lure and breeding item per species.
|
||||
ItemId breedingFoodFor(Species species);
|
||||
bool isBreedingFood(Species species, ItemId held);
|
||||
|
||||
// Search -> path in -> strike -> walk home when the target is lost.
|
||||
BehaviorTree makeHostileHunterTree(const BehaviorTuning& tuning = {});
|
||||
// Flee -> breed on a cooldown -> follow held food -> graze -> wander.
|
||||
BehaviorTree makeGrazingAnimalTree(const BehaviorTuning& tuning = {});
|
||||
// Run for shade in daylight, hold there, patrol at night.
|
||||
BehaviorTree makeSunAvoiderTree(const BehaviorTuning& tuning = {});
|
||||
BehaviorTree makeTreeForRole(BehaviorRole role, const BehaviorTuning& tuning = {});
|
||||
|
||||
// Movement primitives the stock actions are built from; exported because the
|
||||
// boss fights steer their own bodies with the same rules.
|
||||
bool stepTowardPoint(BehaviorAgent& agent, const Vec3& goal, float arriveEpsilon);
|
||||
BehaviorStatus followRouteStep(BehaviorContext& ctx);
|
||||
bool planRouteTo(BehaviorContext& ctx, const Vec3i& goal, bool force);
|
||||
|
||||
// Nearest standable cell with something solid overhead, scanned in a fixed
|
||||
// order so the answer never depends on iteration luck.
|
||||
bool findShadedCell(const World& world, const Vec3i& from, int radius, const PathConfig& config,
|
||||
Vec3i& outCell);
|
||||
|
||||
} // namespace vf
|
||||
Reference in new issue
Block a user