14#include "../ECS_Components_AI.h"
15#include "../ECS_Components.h"
17#include "../system/system_utils.h"
18#include "../system/EventQueue.h"
19#include "../json_helper.h"
20#include "../CollisionMap.h"
21#include "../GameEngine.h"
33 std::cout <<
"\n[BehaviorTreeManager] ========================================" << std::endl;
34 std::cout <<
"[BehaviorTreeManager] Loading: " << filepath << std::endl;
39 std::cout <<
"[BehaviorTreeManager] Step 1: Loading JSON file..." << std::endl;
43 std::cerr <<
"[BehaviorTreeManager] ERROR: Failed to load file: " << filepath << std::endl;
44 std::cout <<
"[BehaviorTreeManager] ========================================+n" << std::endl;
47 std::cout <<
"[BehaviorTreeManager] JSON loaded successfully" << std::endl;
50 std::cout <<
"[BehaviorTreeManager] Step 2: Detecting format version..." << std::endl;
51 bool isV2 =
j.contains(
"schema_version") &&
j[
"schema_version"].get<
int>() == 2;
52 std::cout <<
"[BehaviorTreeManager] Version: " << (
isV2 ?
"v2" :
"v1") << std::endl;
55 std::cout <<
"[BehaviorTreeManager] Step 3: Extracting tree metadata..." << std::endl;
65 if (!
j.contains(
"data"))
67 std::cerr <<
"[BehaviorTreeManager] ERROR: v2 format but no 'data' section" << std::endl;
68 std::cout <<
"[BehaviorTreeManager] ========================================+n" << std::endl;
73 std::cout <<
"[BehaviorTreeManager] Extracted 'data' section from v2 format" << std::endl;
78 std::cout <<
"[BehaviorTreeManager] Using root as data section (v1 format)" << std::endl;
82 std::cout <<
"[BehaviorTreeManager] Tree name: " <<
tree.name << std::endl;
83 std::cout <<
"[BehaviorTreeManager] Root node ID: " <<
tree.rootNodeId << std::endl;
86 std::cout <<
"[BehaviorTreeManager] Step 4: Parsing nodes..." << std::endl;
89 std::cerr <<
"[BehaviorTreeManager] ERROR: No 'nodes' array in data section" << std::endl;
90 std::cerr <<
"[BehaviorTreeManager] This may be an empty or invalid tree" << std::endl;
93 std::cout <<
"[BehaviorTreeManager] JSON structure:" << std::endl;
94 std::cout <<
j.dump(2) << std::endl;
96 std::cout <<
"[BehaviorTreeManager] ========================================+n" << std::endl;
103 std::cout <<
"[BehaviorTreeManager] Found " << nodeCount <<
" nodes to parse" << std::endl;
119 node.editorPosX = 0.0f;
120 node.editorPosY = 0.0f;
151 else if (
condStr ==
"TargetInRange" ||
condStr ==
"IsTargetInAttackRange")
153 else if (
condStr ==
"HealthBelow")
155 else if (
condStr ==
"HasMoveGoal")
157 else if (
condStr ==
"CanAttack")
159 else if (
condStr ==
"HeardNoise")
162 else if (
condStr ==
"IsWaitTimerExpired")
164 else if (
condStr ==
"HasNavigableDestination")
166 else if (
condStr ==
"HasValidPath")
168 else if (
condStr ==
"HasReachedDestination")
181 if (
it.value().is_string())
182 node.stringParams[
it.key()] =
it.value().get<std::string>();
183 else if (
it.value().is_number_integer())
184 node.intParams[
it.key()] =
it.value().get<
int>();
185 else if (
it.value().is_number_float())
186 node.floatParams[
it.key()] =
it.value().get<
float>();
200 if (
actStr ==
"SetMoveGoalToLastKnownTargetPos")
202 else if (
actStr ==
"SetMoveGoalToTarget")
204 else if (
actStr ==
"SetMoveGoalToPatrolPoint")
208 else if (
actStr ==
"AttackIfClose" ||
actStr ==
"AttackMelee")
210 else if (
actStr ==
"PatrolPickNextPoint")
212 else if (
actStr ==
"ClearTarget")
214 else if (
actStr ==
"Idle")
217 else if (
actStr ==
"WaitRandomTime")
219 else if (
actStr ==
"ChooseRandomNavigablePoint")
221 else if (
actStr ==
"RequestPathfinding")
223 else if (
actStr ==
"FollowPath")
248 if (
nodeJson.contains(
"subgraphInputs") &&
nodeJson[
"subgraphInputs"].is_object())
251 for (
auto it = inputs.begin();
it != inputs.end(); ++
it)
253 node.subgraphInputs[
it.key()] =
it.value().get<std::string>();
258 if (
nodeJson.contains(
"subgraphOutputs") &&
nodeJson[
"subgraphOutputs"].is_object())
261 for (
auto it = outputs.begin();
it != outputs.end(); ++
it)
263 node.subgraphOutputs[
it.key()] =
it.value().get<std::string>();
267 std::cout <<
"[BehaviorTreeManager] SubGraph loaded: " <<
node.subgraphPath
268 <<
" with " <<
node.subgraphInputs.size() <<
" inputs, "
269 <<
node.subgraphOutputs.size() <<
" outputs" << std::endl;
285 std::cout <<
"[BehaviorTreeManager] Node " <<
node.id <<
": " <<
node.name
286 <<
" (" <<
typeStr <<
") children: " <<
node.childIds.size() << std::endl;
289 std::cout <<
"[BehaviorTreeManager] Parsed " <<
tree.nodes.size() <<
" nodes successfully" << std::endl;
292 std::cout <<
"[BehaviorTreeManager] Step 5: Validating tree structure..." << std::endl;
297 std::cerr <<
"[BehaviorTreeManager] WARNING: Tree validation failed: " <<
validationError << std::endl;
302 std::cout <<
"[BehaviorTreeManager] Tree validation: OK" << std::endl;
306 std::cout <<
"[BehaviorTreeManager] Step 6: Ensuring Root node exists..." << std::endl;
307 tree.EnsureRootNodeExists();
310 std::cout <<
"[BehaviorTreeManager] Step 7: Registering tree..." << std::endl;
316 std::cout <<
"[BehaviorTreeManager] SUCCESS: Loaded '" <<
tree.name <<
"' (ID=" << treeId <<
") with "
317 <<
tree.nodes.size() <<
" nodes" << std::endl;
318 std::cout <<
"[BehaviorTreeManager] Registered path mapping: " << filepath <<
" -> ID " << treeId <<
"\n";
319 std::cout <<
"[BehaviorTreeManager] ========================================+n" << std::endl;
323 catch (
const std::exception&
e)
325 std::cerr <<
"\n[BehaviorTreeManager] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
326 std::cerr <<
"[BehaviorTreeManager] EXCEPTION: " <<
e.what() << std::endl;
327 std::cerr <<
"[BehaviorTreeManager] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl;
328 std::cout <<
"[BehaviorTreeManager] ========================================+n" << std::endl;
337 if (
tree.id == treeId)
354 if (
tree.id == treeId)
358 std::string filepath =
"Blueprints/AI/" +
tree.
name +
".json";
369 SYSTEM_LOG <<
"BehaviorTreeManager: Hot-reloaded tree ID=" << treeId <<
"\n";
375 SYSTEM_LOG <<
"BehaviorTreeManager: Cannot reload tree ID=" << treeId <<
" (not found)\n";
381 errorMessage.clear();
384 if (
tree.nodes.empty())
386 errorMessage =
"Tree has no nodes";
394 errorMessage =
"Root node ID " + std::to_string(
tree.rootNodeId) +
" not found";
404 if (
node.childIds.empty())
406 errorMessage =
"Composite node '" +
node.
name +
"' (ID=" + std::to_string(
node.id) +
") has no children";
413 if (!
tree.GetNode(childId))
415 errorMessage =
"Node '" +
node.name +
"' references missing child ID " + std::to_string(childId);
424 if (!
tree.GetNode(
node.decoratorChildId))
426 errorMessage =
"Decorator node '" +
node.name +
"' references missing child ID " + std::to_string(
node.decoratorChildId);
440 errorMessage =
"Duplicate node ID " + std::to_string(
node.id);
460 if (!
child)
continue;
477 if (!
child)
continue;
491 if (!
node.conditionTypeString.empty() &&
node.conditionTypeString ==
"CheckBlackboardValue")
494 std::string
key =
node.GetParameterString(
"key");
495 std::string op =
node.GetParameterString(
"operator");
512 if (op ==
"Equals" || op ==
"equals" || op ==
"==")
514 else if (op ==
"NotEquals" || op ==
"notequals" || op ==
"!=")
516 else if (op ==
"GreaterThan" || op ==
"greaterthan" || op ==
">")
518 else if (op ==
"LessThan" || op ==
"lessthan" || op ==
"<")
520 else if (op ==
"GreaterOrEqual" || op ==
"greaterorequal" || op ==
">=")
522 else if (op ==
"LessOrEqual" || op ==
"lessorequal" || op ==
"<=")
661 int index =
static_cast<int>(param1);
679 intent.desiredSpeed = (param1 > 0.0f) ? param1 : 1.0f;
701 float range = (param1 > 0.0f) ? param1 : 50.0f;
711 intent.damage = (param2 > 0.0f) ? param2 : 10.0f;
747 float minWait = (param1 > 0.0f) ? param1 : 2.0f;
748 float maxWait = (param2 > 0.0f) ? param2 : 6.0f;
810 intent.desiredSpeed = 1.0f;
812 intent.usePathfinding =
true;
813 intent.avoidObstacles =
true;
834 intent.desiredSpeed = 1.0f;
836 intent.usePathfinding =
true;
873 msg.targetUid = entity;
916 if (
tree.id == treeId)
931 if (
entry.second == treeId)
938 if (
tree.id == treeId)
947 std::cout <<
"[BehaviorTreeManager] Loaded trees (" <<
m_trees.size() <<
"):" << std::endl;
950 std::cout <<
" - ID=" <<
tree.id <<
" Name='" <<
tree.name <<
"' Nodes=" <<
tree.nodes.size() << std::endl;
953 std::cout <<
"[BehaviorTreeManager] Path-to-ID registry (" <<
m_pathToIdMap.size() <<
"):" << std::endl;
956 std::cout <<
" - '" <<
entry.first <<
"' -> ID=" <<
entry.second << std::endl;
966 std::vector<BTValidationMessage>
messages;
985 msg.message =
"No root node found (rootNodeId=" + std::to_string(
rootNodeId) +
")";
994 msg.message =
"Multiple nodes with root ID found";
1022 msg.message =
"Node references non-existent child ID " + std::to_string(childId);
1031 if (
node.decoratorChildId != 0)
1042 msg.message =
"Decorator references non-existent child ID " + std::to_string(
node.decoratorChildId);
1052 if (
entry.second > 1)
1057 msg.message =
"Node has multiple parents (count=" + std::to_string(
entry.second) +
")";
1068 msg.message =
"Root node has parent(s)";
1080 msg.message =
"Orphan node (no parent, not root)";
1091 if (
node.decoratorChildId == 0)
1096 msg.message =
"Decorator has no child (decoratorChildId=0)";
1103 if (
node.childIds.empty())
1108 msg.message =
"Composite has no children";
1122 msg.message =
"Cycle detected starting from this node";
1146 std::cerr <<
"[BehaviorTreeAsset::EnsureRootNodeExists] "
1147 <<
"Root node ID " <<
rootNodeId <<
" not found in nodes list, creating new Root" << std::endl;
1151 std::cout <<
"[BehaviorTreeAsset::EnsureRootNodeExists] Creating default Root node" << std::endl;
1163 std::cout <<
"[BehaviorTreeAsset::EnsureRootNodeExists] "
1164 <<
"Created Root node with ID=" <<
rootNode.id <<
", total nodes=" <<
nodes.size() << std::endl;
1210 node->decoratorChildId != 0)
1212 if (
dfs(
node->decoratorChildId))
1261 newNode.conditionParam = 0.0f;
1275 std::cout <<
"[BehaviorTreeAsset] Added node ID=" <<
newNode.id
1276 <<
" type=" <<
static_cast<int>(type) <<
" name='" <<
name <<
"'"
1277 <<
" pos=(" << position.
x <<
"," << position.
y <<
")" << std::endl;
1288 if (
it->id == nodeId)
1314 if (
node.decoratorChildId == nodeId)
1316 node.decoratorChildId = 0;
1320 std::cout <<
"[BehaviorTreeAsset] Removed node ID=" << nodeId << std::endl;
1329 if (!parent || !
child)
1331 std::cerr <<
"[BehaviorTreeAsset] ConnectNodes failed: invalid node IDs" << std::endl;
1343 std::cerr <<
"[BehaviorTreeAsset] Connection already exists" << std::endl;
1348 parent->
childIds.push_back(childId);
1349 std::cout <<
"[BehaviorTreeAsset] Connected composite node " << parentId <<
" -> " << childId << std::endl;
1356 std::cerr <<
"[BehaviorTreeAsset] Decorator already has a child" << std::endl;
1361 std::cout <<
"[BehaviorTreeAsset] Connected decorator node " << parentId <<
" -> " << childId << std::endl;
1366 std::cerr <<
"[BehaviorTreeAsset] Cannot connect from leaf node" << std::endl;
1387 std::cout <<
"[BehaviorTreeAsset] Disconnected " << parentId <<
" -X-> " << childId << std::endl;
1397 std::cout <<
"[BehaviorTreeAsset] Disconnected decorator " << parentId <<
" -X-> " << childId << std::endl;
1413 SYSTEM_LOG <<
"[BehaviorTreeManager] ValidateSubGraphPath: Path is empty\n";
1418 std::ifstream
fileTest(path.c_str());
1421 SYSTEM_LOG <<
"[BehaviorTreeManager] ValidateSubGraphPath: File not found or not readable: " << path <<
"\n";
1436 SYSTEM_LOG <<
"[BehaviorTreeManager] ValidateSubGraphPath: Not a .bt.json file: " << path <<
"\n";
1446 SYSTEM_LOG <<
"[BehaviorTreeManager] ValidateSubGraphPath: Failed to load JSON: " << path <<
"\n";
1450 catch (
const std::exception&
e)
1452 SYSTEM_LOG <<
"[BehaviorTreeManager] ValidateSubGraphPath: JSON parse error: " <<
e.what() <<
"\n";
1456 SYSTEM_LOG <<
"[BehaviorTreeManager] ValidateSubGraphPath: Valid - " << path <<
"\n";
1483 SYSTEM_LOG <<
"[BehaviorTreeManager] SubGraph path invalid for circular check: " <<
node->subgraphPath <<
"\n";
1498 SYSTEM_LOG <<
"[BehaviorTreeManager] Failed to load SubGraph for circular check: " <<
node->subgraphPath <<
"\n";
1529 std::vector<std::string> errors;
1534 errors.push_back(
"Graph not found with ID: " + std::to_string(graphId));
1539 std::set<std::string>
visited;
1546 if (
node.subgraphPath.empty())
1548 errors.push_back(
"SubGraph node '" +
node.name +
"' (ID:" + std::to_string(
node.id) +
") has no path specified");
1555 errors.push_back(
"SubGraph node '" +
node.name +
"' (ID:" + std::to_string(
node.id) +
") path invalid: " +
node.subgraphPath);
1563 errors.push_back(
"SubGraph node '" +
node.name +
"' (ID:" + std::to_string(
node.id) +
") creates circular reference: " +
node.subgraphPath);
1578 if (
tree.m_eventRootIds.empty())
1582 const std::vector<Message>& events =
eventQueue.GetEvents();
1604 switch (
msg.msg_type)
1607 msgTypeStr =
"Olympe_EventType_AI_Explosion";
1613 msgTypeStr =
"Olympe_EventType_AI_DamageDealt";
1616 msgTypeStr =
"Olympe_EventType_Object_Create";
1619 msgTypeStr =
"Olympe_EventType_Object_Destroy";
1634 std::cout <<
"[TickEventRoots] Entity " << entity <<
": Executing OnEvent root " <<
eventRootId
1635 <<
" for event '" <<
msgTypeStr <<
"'" << std::endl;
BTStatus ExecuteBTAction(BTActionType actionType, float param1, float param2, EntityID entity, AIBlackboard_data &blackboard)
BTStatus ExecuteBTNode(const BTNode &node, EntityID entity, AIBlackboard_data &blackboard, const BehaviorTreeAsset &tree)
BTStatus ExecuteBTCondition(BTConditionType condType, float param, EntityID entity, const AIBlackboard_data &blackboard)
void TickEventRoots(EventQueue &eventQueue, const BehaviorTreeAsset &tree, EntityID entity, AIBlackboard_data &blackboard)
Data-driven behavior tree system for AI decision making.
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)
@ 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.
@ SetMoveGoalToPatrolPoint
Move to next patrol waypoint.
@ RequestPathfinding
Request pathfinding to moveGoal via MoveIntent.
@ FollowPath
Follow the path (check progression)
BTStatus
Behavior tree node execution status.
@ Success
Node completed successfully.
@ Running
Node is currently executing.
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.
@ HasNavigableDestination
Navigable destination chosen?
@ HealthBelow
Health below threshold.
@ HasReachedDestination
Reached destination?
@ TargetInRange
Target within specified range.
ComponentTypeID GetComponentTypeID_Static()
const EntityID INVALID_ENTITY_ID
static uint32_t GenerateTreeIdFromPath(const std::string &treePath)
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
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)
const BehaviorTreeAsset * GetTreeByPath(const std::string &treePath) const
const BehaviorTreeAsset * GetTreeByAnyId(uint32_t treeId) const
void Push(const Message &msg)
static EventQueue & Get()
static float fDt
Delta time between frames in seconds.
bool GetRandomNavigablePoint(float centerX, float centerY, float radius, int maxAttempts, float &outX, float &outY, CollisionLayer layer=CollisionLayer::Ground) const
static NavigationMap & Get()
static World & Get()
Get singleton instance (short form)
bool HasComponent(EntityID entity) const
T & GetComponent(EntityID entity)
std::string GetString(const json &j, const std::string &key, const std::string &defaultValue="")
Safely get a string value from JSON.
uint32_t GetUInt(const json &j, const std::string &key, uint32_t defaultValue=0)
Safely get an unsigned integer value from JSON.
bool LoadJsonFromFile(const std::string &filepath, json &j)
Load and parse a JSON file.
int GetInt(const json &j, const std::string &key, int defaultValue=0)
Safely get an integer value from JSON.
void ForEachInArray(const json &j, const std::string &key, std::function< void(const json &, size_t)> callback)
Iterate over an array with a callback function.
float GetFloat(const json &j, const std::string &key, float defaultValue=0.0f)
Safely get a float value from JSON.
bool IsArray(const json &j, const std::string &key)
Check if a key contains an array.
Represents a single node in a behavior tree.
std::vector< uint32_t > childIds
IDs of child nodes.
uint32_t id
Unique node ID within tree.
std::string subgraphPath
Path to external .bt.json or .ats file.
uint32_t decoratorChildId
BTNodeType type
Node type.
Validation message for behavior tree structure checking.
uint32_t AddNode(BTNodeType type, const std::string &name, const Vector &position)
bool RemoveNode(uint32_t nodeId)
std::vector< BTValidationMessage > ValidateTreeFull() const
bool ConnectNodes(uint32_t parentId, uint32_t childId)
uint32_t GenerateNextNodeId() const
std::vector< BTNode > nodes
BTNode * GetNode(uint32_t nodeId)
void EnsureRootNodeExists()
bool DetectCycle(uint32_t startNodeId) const
bool DisconnectNodes(uint32_t parentId, uint32_t childId)
Position component for spatial location.
Vector position
2D/3D position vector
@ Olympe_EventType_AI_DamageDealt
@ Olympe_EventType_AI_Noise
@ Olympe_EventType_Object_Destroy
@ Olympe_EventType_Object_Create
@ Olympe_EventType_AI_Explosion