Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
BehaviorTree.h
Go to the documentation of this file.
1/**
2 * @file BehaviorTree.h
3 * @brief Data-driven behavior tree system for AI decision making
4 * @author Nicolas Chereau
5 * @date 2025
6 * @version 2.0
7 *
8 * @details
9 * This file implements a behavior tree system for creating complex AI behaviors.
10 * Behavior trees are hierarchical structures that make decisions based on conditions
11 * and execute actions based on those decisions.
12 *
13 * Key features:
14 * - Composite nodes: Selector (OR), Sequence (AND)
15 * - Decorator nodes: Inverter, Repeater
16 * - Condition nodes: State checking (health, target, etc.)
17 * - Action nodes: Behaviors (move, attack, patrol, etc.)
18 * - JSON-based tree definitions
19 * - Per-entity tree execution
20 *
21 * @note Behavior Tree purpose: Data-driven behavior tree system for AI decision making.
22 *
23 * @example
24 * @code
25 * // Load behavior tree from JSON
26 * BehaviorTree::LoadTreeForEntity(npcEntity, "Blueprints/BehaviorTrees/Patrol.json");
27 *
28 * // Update tree each frame
29 * BehaviorTree::UpdateEntity(npcEntity, deltaTime);
30 * @endcode
31 */
32
33#pragma once
34
35#include "../ECS_Entity.h"
36#include "../vector.h"
37#include <vector>
38#include <string>
39#include <cstdint>
40#include <map>
41#include <algorithm>
42#include <set>
43
44// Forward declarations
46
47/**
48 * @enum BTNodeType
49 * @brief Behavior tree node types
50 *
51 * Defines the different types of nodes that can exist in a behavior tree.
52 */
53enum class BTNodeType : uint8_t
54{
55 Selector = 0, ///< OR node - succeeds if any child succeeds
56 Sequence, ///< AND node - succeeds if all children succeed
57 Condition, ///< Leaf node - checks a condition
58 Action, ///< Leaf node - performs an action
59 Inverter, ///< Decorator - inverts child result
60 Repeater, ///< Decorator - repeats child N times
61 Root, ///< Phase 38b: Root node - entry point of behavior tree (green, fixed position)
62 OnEvent, ///< Phase 38b: OnEvent root - event-driven entry point (orange, event-triggered)
63 SubGraph = 8 ///< Phase 39: SubGraph - external graph reference (recursive BT or ATS)
64};
65
66/**
67 * @enum BTStatus
68 * @brief Behavior tree node execution status
69 *
70 * Represents the current state of a behavior tree node.
71 */
72enum class BTStatus : uint8_t
73{
74 Idle = 0, ///< Node waiting for execution (not yet started)
75 Running = 1, ///< Node is currently executing
76 Success = 2, ///< Node completed successfully
77 Failure = 3, ///< Node failed
78 Aborted = 4 ///< Node execution interrupted (e.g., entity destroyed)
79};
80
81/**
82 * @enum BTConditionType
83 * @brief Built-in condition types for behavior trees
84 *
85 * Predefined conditions that can be checked during tree execution.
86 */
88{
89 TargetVisible = 0, ///< Can see target entity
90 TargetInRange, ///< Target within specified range
91 HealthBelow, ///< Health below threshold
92 HasMoveGoal, ///< Movement goal is set
93 CanAttack, ///< Attack is available
94 HeardNoise, ///< Detected noise
95 // NEW: Wander behavior conditions
96 IsWaitTimerExpired, ///< Wait timer expired?
97 HasNavigableDestination, ///< Navigable destination chosen?
98 HasValidPath, ///< Valid path calculated?
99 HasReachedDestination, ///< Reached destination?
100 // Catalog aliases for better readability
101 HasTarget = TargetVisible, ///< Alias for HasTarget condition
102 IsTargetInAttackRange = TargetInRange ///< Alias for range check
103};
104
105/**
106 * @enum BTActionType
107 * @brief Built-in action types for behavior trees
108 *
109 * Predefined actions that can be executed during tree execution.
110 */
112{
113 SetMoveGoalToLastKnownTargetPos = 0, ///< Move to last seen target position
114 SetMoveGoalToTarget, ///< Move towards current target
115 SetMoveGoalToPatrolPoint, ///< Move to next patrol waypoint
116 MoveToGoal, ///< Execute movement to goal
117 AttackIfClose, ///< Attack if in range
118 PatrolPickNextPoint, ///< Select next patrol point
119 ClearTarget, ///< Clear current target
120 Idle, ///< Do nothing
121 // NEW: Wander behavior actions
122 WaitRandomTime, ///< Initialize random timer (param1=min, param2=max)
123 ChooseRandomNavigablePoint, ///< Choose navigable point (param1=searchRadius, param2=maxAttempts)
124 RequestPathfinding, ///< Request pathfinding to moveGoal via MoveIntent
125 FollowPath, ///< Follow the path (check progression)
126 SendMessage, ///< Emit event to EventQueue (param1=EventType enum)
127 // Catalog aliases for better readability
128 MoveTo = MoveToGoal, ///< Alias for MoveTo action
129 AttackMelee = AttackIfClose ///< Alias for melee attack
130 };
131
132/**
133 * @struct BTNode
134 * @brief Represents a single node in a behavior tree
135 *
136 * Can be a composite, decorator, condition, or action node.
137 * Stores node type, parameters, and child references.
138 */
139struct BTNode
140{
142 uint32_t id = 0; ///< Unique node ID within tree
143
144 // For composite nodes (Selector, Sequence)
145 std::vector<uint32_t> childIds; ///< IDs of child nodes
146
147 // For condition nodes
149 std::string conditionTypeString; ///< Condition type as string (for flexible conditions like CheckBlackboardValue)
150 float conditionParam = 0.0f; ///< Generic parameter for conditions
151
152 // For action nodes
154 float actionParam1 = 0.0f; ///< Generic parameter 1 for actions
155 float actionParam2 = 0.0f; ///< Generic parameter 2 for actions
156
157 // For decorator nodes
159 int repeatCount = 1; // For Repeater decorator
160
161 // Debug info
162 std::string name;
163
164 // Editor positioning (for visual layout in BehaviorTree editor)
165 float editorPosX = 0.0f; ///< Horizontal position in editor canvas (pixels)
166 float editorPosY = 0.0f; ///< Vertical position in editor canvas (pixels) - used for execution order sorting
167
168 // Flexible parameters (for new condition/action types that need structured data)
169 std::map<std::string, std::string> stringParams; ///< String parameters
170 std::map<std::string, int> intParams; ///< Integer parameters
171 std::map<std::string, float> floatParams; ///< Float parameters
172
173 // Phase 38b: Event-driven execution fields
174 std::string eventType; ///< Event type listener (e.g., "Olympe_EventType_AI_Explosion")
175 std::string eventMessage; ///< Optional event message filter (for future event filtering)
176 uint32_t onEventRootIndex = 0; ///< Index into BehaviorTreeAsset::m_eventRootIds for backreference
177
178 // Phase 39: SubGraph support
179 std::string subgraphPath; ///< Path to external .bt.json or .ats file
180 std::map<std::string, std::string> subgraphInputs; ///< Input bindings: "childVar" → "parentVar"
181 std::map<std::string, std::string> subgraphOutputs; ///< Output bindings: "childVar" → "parentVar"
182
183 // Helper methods for parameter access
184 std::string GetParameterString(const std::string& key, const std::string& defaultValue = "") const
185 {
186 auto it = stringParams.find(key);
187 return (it != stringParams.end()) ? it->second : defaultValue;
188 }
189
190 int GetParameterInt(const std::string& key, int defaultValue = 0) const
191 {
192 auto it = intParams.find(key);
193 return (it != intParams.end()) ? it->second : defaultValue;
194 }
195
196 float GetParameterFloat(const std::string& key, float defaultValue = 0.0f) const
197 {
198 auto it = floatParams.find(key);
199 return (it != floatParams.end()) ? it->second : defaultValue;
200 }
201};
202
203/**
204 * @struct BTValidationMessage
205 * @brief Validation message for behavior tree structure checking
206 */
208{
209 enum class Severity : uint8_t
210 {
211 Info = 0,
212 Warning = 1,
213 Error = 2
214 };
215
218 std::string message;
219};
220
221// --- Behavior Tree Asset ---
223{
224 uint32_t id = 0; // Unique tree ID
225 std::string name;
226 std::vector<BTNode> nodes;
228
229 // Phase 38b: Event-driven execution roots (separate from main Root)
230 std::vector<uint32_t> m_eventRootIds; // IDs of OnEvent root nodes
231
232 // Helper: get node by ID
234 {
235 for (auto& node : nodes)
236 {
237 if (node.id == nodeId)
238 return &node;
239 }
240 return nullptr;
241 }
242
243 const BTNode* GetNode(uint32_t nodeId) const
244 {
245 for (const auto& node : nodes)
246 {
247 if (node.id == nodeId)
248 return &node;
249 }
250 return nullptr;
251 }
252
253 // Get children of a node sorted by Y-position (execution order)
254 // Returns vector of child IDs sorted by editorPosY (lowest Y = executes first = index 1)
255 std::vector<uint32_t> GetChildrenSortedByY(uint32_t parentId) const
256 {
257 const BTNode* parent = GetNode(parentId);
258 if (!parent) return {};
259
260 // Create pairs of (editorPosY, childId)
261 std::vector<std::pair<float, uint32_t>> childrenWithY;
262 for (uint32_t childId : parent->childIds)
263 {
264 const BTNode* child = GetNode(childId);
265 if (child)
266 {
267 childrenWithY.push_back({child->editorPosY, childId});
268 }
269 }
270
271 // Sort by Y-position (ascending: top node = first to execute)
272 std::sort(childrenWithY.begin(), childrenWithY.end(),
273 [](const std::pair<float, uint32_t>& a, const std::pair<float, uint32_t>& b)
274 {
275 return a.first < b.first;
276 });
277
278 // Extract sorted child IDs
279 std::vector<uint32_t> sortedIds;
280 for (const auto& pair : childrenWithY)
281 {
282 sortedIds.push_back(pair.second);
283 }
284 return sortedIds;
285 }
286
287 // Get execution index of a child within its parent (1-based)
288 // Returns 0 if not found or parent is not composite
290 {
291 std::vector<uint32_t> sortedChildren = GetChildrenSortedByY(parentId);
292 for (size_t i = 0; i < sortedChildren.size(); ++i)
293 {
294 if (sortedChildren[i] == childId)
295 {
296 return static_cast<uint32_t>(i + 1); // 1-based indexing
297 }
298 }
299 return 0; // Not found
300 }
301
302 // Validation methods
303 std::vector<BTValidationMessage> ValidateTreeFull() const;
304 bool DetectCycle(uint32_t startNodeId) const;
305
306 // Phase 38b: Ensure a Root node exists (auto-create if missing)
308
309 // Editor CRUD operations
310 uint32_t AddNode(BTNodeType type, const std::string& name, const Vector& position);
311 bool RemoveNode(uint32_t nodeId);
312 bool ConnectNodes(uint32_t parentId, uint32_t childId);
313 bool DisconnectNodes(uint32_t parentId, uint32_t childId);
315};
316
317// --- Phase 39: SubGraph Call Stack ---
318// Tracks active SubGraph paths to prevent infinite recursion and cycles
320{
321 std::vector<std::string> pathStack; ///< Stack of active subgraph paths
322 int depth = 0; ///< Current recursion depth
323
324 static const int MAX_DEPTH = 32; ///< Maximum recursion depth limit
325
326 /// Push a subgraph path onto the stack
327 void Push(const std::string& path)
328 {
329 pathStack.push_back(path);
330 depth++;
331 }
332
333 /// Pop a subgraph path from the stack
334 void Pop()
335 {
336 if (!pathStack.empty())
337 {
338 pathStack.pop_back();
339 depth--;
340 }
341 }
342
343 /// Check if a path is already on the stack (cycle detection)
344 bool Contains(const std::string& path) const
345 {
346 for (const auto& p : pathStack)
347 {
348 if (p == path)
349 return true;
350 }
351 return false;
352 }
353
354 /// Check if maximum depth has been reached
355 bool IsFull() const
356 {
357 return depth >= MAX_DEPTH;
358 }
359
360 /// Clear the stack
361 void Clear()
362 {
363 pathStack.clear();
364 depth = 0;
365 }
366
367 /// Get current depth
368 int GetDepth() const
369 {
370 return depth;
371 }
372};
373
374// --- Behavior Tree Manager ---
375// Singleton manager for loading and caching behavior tree assets
377{
378public:
380 {
382 return instance;
383 }
384
385 // Load a behavior tree from JSON file
386 bool LoadTreeFromFile(const std::string& filepath, uint32_t treeId);
387
388 // Reload a behavior tree from JSON file (hot-reload support)
389 bool ReloadTree(uint32_t treeId);
390
391 // Validate a behavior tree structure
392 bool ValidateTree(const BehaviorTreeAsset& tree, std::string& errorMessage) const;
393
394 // Get a loaded tree by ID
395 const BehaviorTreeAsset* GetTree(uint32_t treeId) const;
396
397 // Clear all loaded trees
398 void Clear();
399
400 // NEW: Get tree ID from path (for prefab instantiation)
401 uint32_t GetTreeIdFromPath(const std::string& treePath) const;
402
403 // NEW: Check if tree is already loaded by path
404 bool IsTreeLoadedByPath(const std::string& treePath) const;
405
406 // NEW: Get loaded tree by path
407 const BehaviorTreeAsset* GetTreeByPath(const std::string& treePath) const;
408
409 // NEW: Enhanced lookup that tries multiple strategies
410 const BehaviorTreeAsset* GetTreeByAnyId(uint32_t treeId) const;
411
412 // NEW: Get tree path from ID (reverse lookup)
413 std::string GetTreePathFromId(uint32_t treeId) const;
414
415 // NEW: Debug method to list all loaded trees
416 void DebugPrintLoadedTrees() const;
417
418 // Phase 39c Step 6: SubGraph Validation
419 // Validates that a SubGraph path points to a valid, loadable .bt.json file
420 bool ValidateSubGraphPath(const std::string& path) const;
421
422 // Phase 39c Step 6: Detects circular references by checking if SubGraph creates a cycle
423 // Returns true if circular dependency detected, false otherwise
424 // graphId: ID of the parent graph, nodeId: ID of the SubGraph node
425 bool DetectCircularDependencies(uint32_t graphId, uint32_t nodeId, const BehaviorTreeAsset* parentTree, std::set<std::string>& visited);
426
427 // Phase 39c Step 6: Collect all validation errors for a specific graph
428 // Returns vector of error strings describing all validation issues
429 std::vector<std::string> GetValidationErrors(uint32_t graphId);
430
431private:
433 std::vector<BehaviorTreeAsset> m_trees;
434
435 // NEW: Registry to map file paths to tree IDs
436 std::map<std::string, uint32_t> m_pathToIdMap;
437};
438
439// --- Behavior Tree Execution ---
440// Execute a single node of a behavior tree
442
443// Execute built-in condition nodes
445
446// Execute built-in action nodes
447BTStatus ExecuteBTAction(BTActionType actionType, float param1, float param2, EntityID entity, AIBlackboard_data& blackboard);
448
449// Phase 38b: Process EventQueue events and activate matching OnEvent root nodes
450// Called once per frame for each entity with active behavior tree
451// Scans EventQueue for events matching OnEvent nodes' eventType field
452// Executes matching OnEvent roots in parallel with main Root node
BTStatus ExecuteBTAction(BTActionType actionType, float param1, float param2, EntityID entity, AIBlackboard_data &blackboard)
void TickEventRoots(class EventQueue &eventQueue, const BehaviorTreeAsset &tree, EntityID entity, AIBlackboard_data &blackboard)
BTActionType
Built-in action types for behavior trees.
@ SetMoveGoalToTarget
Move towards current target.
@ SetMoveGoalToLastKnownTargetPos
Move to last seen target position.
@ WaitRandomTime
Initialize random timer (param1=min, param2=max)
@ AttackMelee
Alias for melee attack.
@ MoveToGoal
Execute movement to goal.
@ ClearTarget
Clear current target.
@ SendMessage
Emit event to EventQueue (param1=EventType enum)
@ PatrolPickNextPoint
Select next patrol point.
@ ChooseRandomNavigablePoint
Choose navigable point (param1=searchRadius, param2=maxAttempts)
@ AttackIfClose
Attack if in range.
@ MoveTo
Alias for MoveTo action.
@ Idle
Do nothing.
@ SetMoveGoalToPatrolPoint
Move to next patrol waypoint.
@ RequestPathfinding
Request pathfinding to moveGoal via MoveIntent.
@ FollowPath
Follow the path (check progression)
BTStatus ExecuteBTNode(const BTNode &node, EntityID entity, AIBlackboard_data &blackboard, const BehaviorTreeAsset &tree)
BTStatus
Behavior tree node execution status.
@ Success
Node completed successfully.
@ Running
Node is currently executing.
@ Aborted
Node execution interrupted (e.g., entity destroyed)
@ Failure
Node failed.
@ Idle
Node waiting for execution (not yet started)
BTNodeType
Behavior tree node types.
@ Action
Leaf node - performs an action.
@ Selector
OR node - succeeds if any child succeeds.
@ OnEvent
Phase 38b: OnEvent root - event-driven entry point (orange, event-triggered)
@ Sequence
AND node - succeeds if all children succeed.
@ SubGraph
Phase 39: SubGraph - external graph reference (recursive BT or ATS)
@ Inverter
Decorator - inverts child result.
@ Condition
Leaf node - checks a condition.
@ Repeater
Decorator - repeats child N times.
@ Root
Phase 38b: Root node - entry point of behavior tree (green, fixed position)
BTConditionType
Built-in condition types for behavior trees.
@ HasValidPath
Valid path calculated?
@ CanAttack
Attack is available.
@ IsWaitTimerExpired
Wait timer expired?
@ HeardNoise
Detected noise.
@ TargetVisible
Can see target entity.
@ HasMoveGoal
Movement goal is set.
@ IsTargetInAttackRange
Alias for range check.
@ HasTarget
Alias for HasTarget condition.
@ HasNavigableDestination
Navigable destination chosen?
@ HealthBelow
Health below threshold.
@ HasReachedDestination
Reached destination?
@ TargetInRange
Target within specified range.
BTStatus ExecuteBTCondition(BTConditionType condType, float param, EntityID entity, const AIBlackboard_data &blackboard)
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
std::uint64_t EntityID
Definition ECS_Entity.h:21
bool LoadTreeFromFile(const std::string &filepath, uint32_t treeId)
std::string GetTreePathFromId(uint32_t treeId) const
void DebugPrintLoadedTrees() const
uint32_t GetTreeIdFromPath(const std::string &treePath) const
bool DetectCircularDependencies(uint32_t graphId, uint32_t nodeId, const BehaviorTreeAsset *parentTree, std::set< std::string > &visited)
const BehaviorTreeAsset * GetTree(uint32_t treeId) const
static BehaviorTreeManager & Get()
bool ValidateSubGraphPath(const std::string &path) const
std::vector< BehaviorTreeAsset > m_trees
bool IsTreeLoadedByPath(const std::string &treePath) const
bool ValidateTree(const BehaviorTreeAsset &tree, std::string &errorMessage) const
std::map< std::string, uint32_t > m_pathToIdMap
std::vector< std::string > GetValidationErrors(uint32_t graphId)
bool ReloadTree(uint32_t treeId)
BehaviorTreeManager()=default
const BehaviorTreeAsset * GetTreeByPath(const std::string &treePath) const
const BehaviorTreeAsset * GetTreeByAnyId(uint32_t treeId) const
Represents a single node in a behavior tree.
std::map< std::string, int > intParams
Integer parameters.
std::string conditionTypeString
Condition type as string (for flexible conditions like CheckBlackboardValue)
uint32_t onEventRootIndex
Index into BehaviorTreeAsset::m_eventRootIds for backreference.
std::vector< uint32_t > childIds
IDs of child nodes.
float GetParameterFloat(const std::string &key, float defaultValue=0.0f) const
BTConditionType conditionType
Condition type (enum)
std::string eventType
Event type listener (e.g., "Olympe_EventType_AI_Explosion")
std::string GetParameterString(const std::string &key, const std::string &defaultValue="") const
float actionParam1
Generic parameter 1 for actions.
std::string name
std::string subgraphPath
Path to external .bt.json or .ats file.
uint32_t decoratorChildId
BTActionType actionType
Action type.
int GetParameterInt(const std::string &key, int defaultValue=0) const
float editorPosX
Horizontal position in editor canvas (pixels)
float actionParam2
Generic parameter 2 for actions.
float editorPosY
Vertical position in editor canvas (pixels) - used for execution order sorting.
std::map< std::string, std::string > stringParams
String parameters.
std::string eventMessage
Optional event message filter (for future event filtering)
std::map< std::string, std::string > subgraphInputs
Input bindings: "childVar" → "parentVar".
int repeatCount
BTNodeType type
Node type.
std::map< std::string, float > floatParams
Float parameters.
float conditionParam
Generic parameter for conditions.
std::map< std::string, std::string > subgraphOutputs
Output bindings: "childVar" → "parentVar".
Validation message for behavior tree structure checking.
uint32_t AddNode(BTNodeType type, const std::string &name, const Vector &position)
const BTNode * GetNode(uint32_t nodeId) const
uint32_t GetChildExecutionIndex(uint32_t parentId, uint32_t childId) const
bool RemoveNode(uint32_t nodeId)
std::vector< BTValidationMessage > ValidateTreeFull() const
bool ConnectNodes(uint32_t parentId, uint32_t childId)
uint32_t GenerateNextNodeId() const
std::vector< uint32_t > GetChildrenSortedByY(uint32_t parentId) const
std::vector< BTNode > nodes
BTNode * GetNode(uint32_t nodeId)
std::vector< uint32_t > m_eventRootIds
bool DetectCycle(uint32_t startNodeId) const
bool DisconnectNodes(uint32_t parentId, uint32_t childId)
int depth
Current recursion depth.
void Clear()
Clear the stack.
std::vector< std::string > pathStack
Stack of active subgraph paths.
void Pop()
Pop a subgraph path from the stack.
static const int MAX_DEPTH
Maximum recursion depth limit.
bool Contains(const std::string &path) const
Check if a path is already on the stack (cycle detection)
void Push(const std::string &path)
Push a subgraph path onto the stack.
int GetDepth() const
Get current depth.
bool IsFull() const
Check if maximum depth has been reached.