14#include "../ECS_Components_AI.h"
15#include "../ECS_Components.h"
17#include "../system/system_utils.h"
18#include "../json_helper.h"
19#include "../CollisionMap.h"
20#include "../GameEngine.h"
31 std::cout <<
"\n[BehaviorTreeManager] ========================================" << std::endl;
32 std::cout <<
"[BehaviorTreeManager] Loading: " << filepath << std::endl;
37 std::cout <<
"[BehaviorTreeManager] Step 1: Loading JSON file..." << std::endl;
41 std::cerr <<
"[BehaviorTreeManager] ERROR: Failed to load file: " << filepath << std::endl;
42 std::cout <<
"[BehaviorTreeManager] ========================================+n" << std::endl;
45 std::cout <<
"[BehaviorTreeManager] JSON loaded successfully" << std::endl;
48 std::cout <<
"[BehaviorTreeManager] Step 2: Detecting format version..." << std::endl;
49 bool isV2 =
j.contains(
"schema_version") &&
j[
"schema_version"].get<
int>() == 2;
50 std::cout <<
"[BehaviorTreeManager] Version: " << (
isV2 ?
"v2" :
"v1") << std::endl;
53 std::cout <<
"[BehaviorTreeManager] Step 3: Extracting tree metadata..." << std::endl;
63 if (!
j.contains(
"data"))
65 std::cerr <<
"[BehaviorTreeManager] ERROR: v2 format but no 'data' section" << std::endl;
66 std::cout <<
"[BehaviorTreeManager] ========================================+n" << std::endl;
71 std::cout <<
"[BehaviorTreeManager] Extracted 'data' section from v2 format" << std::endl;
76 std::cout <<
"[BehaviorTreeManager] Using root as data section (v1 format)" << std::endl;
80 std::cout <<
"[BehaviorTreeManager] Tree name: " <<
tree.name << std::endl;
81 std::cout <<
"[BehaviorTreeManager] Root node ID: " <<
tree.rootNodeId << std::endl;
84 std::cout <<
"[BehaviorTreeManager] Step 4: Parsing nodes..." << std::endl;
87 std::cerr <<
"[BehaviorTreeManager] ERROR: No 'nodes' array in data section" << std::endl;
88 std::cerr <<
"[BehaviorTreeManager] This may be an empty or invalid tree" << std::endl;
91 std::cout <<
"[BehaviorTreeManager] JSON structure:" << std::endl;
92 std::cout <<
j.dump(2) << std::endl;
94 std::cout <<
"[BehaviorTreeManager] ========================================+n" << std::endl;
101 std::cout <<
"[BehaviorTreeManager] Found " << nodeCount <<
" nodes to parse" << std::endl;
134 else if (
condStr ==
"TargetInRange" ||
condStr ==
"IsTargetInAttackRange")
136 else if (
condStr ==
"HealthBelow")
138 else if (
condStr ==
"HasMoveGoal")
140 else if (
condStr ==
"CanAttack")
142 else if (
condStr ==
"HeardNoise")
145 else if (
condStr ==
"IsWaitTimerExpired")
147 else if (
condStr ==
"HasNavigableDestination")
149 else if (
condStr ==
"HasValidPath")
151 else if (
condStr ==
"HasReachedDestination")
164 if (
it.value().is_string())
165 node.stringParams[
it.key()] =
it.value().get<std::string>();
166 else if (
it.value().is_number_integer())
167 node.intParams[
it.key()] =
it.value().get<
int>();
168 else if (
it.value().is_number_float())
169 node.floatParams[
it.key()] =
it.value().get<
float>();
183 if (
actStr ==
"SetMoveGoalToLastKnownTargetPos")
185 else if (
actStr ==
"SetMoveGoalToTarget")
187 else if (
actStr ==
"SetMoveGoalToPatrolPoint")
191 else if (
actStr ==
"AttackIfClose" ||
actStr ==
"AttackMelee")
193 else if (
actStr ==
"PatrolPickNextPoint")
195 else if (
actStr ==
"ClearTarget")
197 else if (
actStr ==
"Idle")
200 else if (
actStr ==
"WaitRandomTime")
202 else if (
actStr ==
"ChooseRandomNavigablePoint")
204 else if (
actStr ==
"RequestPathfinding")
206 else if (
actStr ==
"FollowPath")
237 std::cout <<
"[BehaviorTreeManager] Node " <<
node.id <<
": " <<
node.name
238 <<
" (" <<
typeStr <<
") children: " <<
node.childIds.size() << std::endl;
241 std::cout <<
"[BehaviorTreeManager] Parsed " <<
tree.nodes.size() <<
" nodes successfully" << std::endl;
244 std::cout <<
"[BehaviorTreeManager] Step 5: Validating tree structure..." << std::endl;
249 std::cerr <<
"[BehaviorTreeManager] WARNING: Tree validation failed: " <<
validationError << std::endl;
254 std::cout <<
"[BehaviorTreeManager] Tree validation: OK" << std::endl;
258 std::cout <<
"[BehaviorTreeManager] Step 6: Registering tree..." << std::endl;
264 std::cout <<
"[BehaviorTreeManager] SUCCESS: Loaded '" <<
tree.name <<
"' (ID=" << treeId <<
") with "
265 <<
tree.nodes.size() <<
" nodes" << std::endl;
266 std::cout <<
"[BehaviorTreeManager] Registered path mapping: " << filepath <<
" -> ID " << treeId <<
"\n";
267 std::cout <<
"[BehaviorTreeManager] ========================================+n" << std::endl;
271 catch (
const std::exception&
e)
273 std::cerr <<
"\n[BehaviorTreeManager] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
274 std::cerr <<
"[BehaviorTreeManager] EXCEPTION: " <<
e.what() << std::endl;
275 std::cerr <<
"[BehaviorTreeManager] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" << std::endl;
276 std::cout <<
"[BehaviorTreeManager] ========================================+n" << std::endl;
285 if (
tree.id == treeId)
302 if (
tree.id == treeId)
306 std::string filepath =
"Blueprints/AI/" +
tree.
name +
".json";
317 SYSTEM_LOG <<
"BehaviorTreeManager: Hot-reloaded tree ID=" << treeId <<
"\n";
323 SYSTEM_LOG <<
"BehaviorTreeManager: Cannot reload tree ID=" << treeId <<
" (not found)\n";
329 errorMessage.clear();
332 if (
tree.nodes.empty())
334 errorMessage =
"Tree has no nodes";
342 errorMessage =
"Root node ID " + std::to_string(
tree.rootNodeId) +
" not found";
352 if (
node.childIds.empty())
354 errorMessage =
"Composite node '" +
node.
name +
"' (ID=" + std::to_string(
node.id) +
") has no children";
361 if (!
tree.GetNode(childId))
363 errorMessage =
"Node '" +
node.name +
"' references missing child ID " + std::to_string(childId);
372 if (!
tree.GetNode(
node.decoratorChildId))
374 errorMessage =
"Decorator node '" +
node.name +
"' references missing child ID " + std::to_string(
node.decoratorChildId);
388 errorMessage =
"Duplicate node ID " + std::to_string(
node.id);
408 if (!
child)
continue;
425 if (!
child)
continue;
439 if (!
node.conditionTypeString.empty() &&
node.conditionTypeString ==
"CheckBlackboardValue")
442 std::string
key =
node.GetParameterString(
"key");
443 std::string op =
node.GetParameterString(
"operator");
460 if (op ==
"Equals" || op ==
"equals" || op ==
"==")
462 else if (op ==
"NotEquals" || op ==
"notequals" || op ==
"!=")
464 else if (op ==
"GreaterThan" || op ==
"greaterthan" || op ==
">")
466 else if (op ==
"LessThan" || op ==
"lessthan" || op ==
"<")
468 else if (op ==
"GreaterOrEqual" || op ==
"greaterorequal" || op ==
">=")
470 else if (op ==
"LessOrEqual" || op ==
"lessorequal" || op ==
"<=")
609 int index =
static_cast<int>(param1);
627 intent.desiredSpeed = (param1 > 0.0f) ? param1 : 1.0f;
649 float range = (param1 > 0.0f) ? param1 : 50.0f;
659 intent.damage = (param2 > 0.0f) ? param2 : 10.0f;
695 float minWait = (param1 > 0.0f) ? param1 : 2.0f;
696 float maxWait = (param2 > 0.0f) ? param2 : 6.0f;
758 intent.desiredSpeed = 1.0f;
760 intent.usePathfinding =
true;
761 intent.avoidObstacles =
true;
782 intent.desiredSpeed = 1.0f;
784 intent.usePathfinding =
true;
844 if (
tree.id == treeId)
859 if (
entry.second == treeId)
866 if (
tree.id == treeId)
875 std::cout <<
"[BehaviorTreeManager] Loaded trees (" <<
m_trees.size() <<
"):" << std::endl;
878 std::cout <<
" - ID=" <<
tree.id <<
" Name='" <<
tree.name <<
"' Nodes=" <<
tree.nodes.size() << std::endl;
881 std::cout <<
"[BehaviorTreeManager] Path-to-ID registry (" <<
m_pathToIdMap.size() <<
"):" << std::endl;
884 std::cout <<
" - '" <<
entry.first <<
"' -> ID=" <<
entry.second << std::endl;
894 std::vector<BTValidationMessage>
messages;
913 msg.message =
"No root node found (rootNodeId=" + std::to_string(
rootNodeId) +
")";
922 msg.message =
"Multiple nodes with root ID found";
950 msg.message =
"Node references non-existent child ID " + std::to_string(childId);
959 if (
node.decoratorChildId != 0)
970 msg.message =
"Decorator references non-existent child ID " + std::to_string(
node.decoratorChildId);
980 if (
entry.second > 1)
985 msg.message =
"Node has multiple parents (count=" + std::to_string(
entry.second) +
")";
996 msg.message =
"Root node has parent(s)";
1008 msg.message =
"Orphan node (no parent, not root)";
1019 if (
node.decoratorChildId == 0)
1024 msg.message =
"Decorator has no child (decoratorChildId=0)";
1031 if (
node.childIds.empty())
1036 msg.message =
"Composite has no children";
1050 msg.message =
"Cycle detected starting from this node";
1097 node->decoratorChildId != 0)
1099 if (
dfs(
node->decoratorChildId))
1146 newNode.conditionParam = 0.0f;
1160 std::cout <<
"[BehaviorTreeAsset] Added node ID=" <<
newNode.id
1161 <<
" type=" <<
static_cast<int>(type) <<
" name='" <<
name <<
"'" << std::endl;
1172 if (
it->id == nodeId)
1198 if (
node.decoratorChildId == nodeId)
1200 node.decoratorChildId = 0;
1204 std::cout <<
"[BehaviorTreeAsset] Removed node ID=" << nodeId << std::endl;
1213 if (!parent || !
child)
1215 std::cerr <<
"[BehaviorTreeAsset] ConnectNodes failed: invalid node IDs" << std::endl;
1227 std::cerr <<
"[BehaviorTreeAsset] Connection already exists" << std::endl;
1232 parent->
childIds.push_back(childId);
1233 std::cout <<
"[BehaviorTreeAsset] Connected composite node " << parentId <<
" -> " << childId << std::endl;
1240 std::cerr <<
"[BehaviorTreeAsset] Decorator already has a child" << std::endl;
1245 std::cout <<
"[BehaviorTreeAsset] Connected decorator node " << parentId <<
" -> " << childId << std::endl;
1250 std::cerr <<
"[BehaviorTreeAsset] Cannot connect from leaf node" << std::endl;
1271 std::cout <<
"[BehaviorTreeAsset] Disconnected " << parentId <<
" -X-> " << childId << std::endl;
1281 std::cout <<
"[BehaviorTreeAsset] Disconnected decorator " << parentId <<
" -X-> " << childId << std::endl;
std::cout<< "[BTEditor] Duplicated node: "<< duplicate.name<< " (ID: "<< duplicate.id<< ")"<< std::endl;} } m_selectedNodes=newNodes;m_treeModified=true;m_currentLayout=m_layoutEngine.ComputeLayout(&m_editingTree, m_nodeSpacingX, m_nodeSpacingY, m_currentZoom);m_validationMessages=m_editingTree.ValidateTreeFull();} bool BehaviorTreeDebugWindow::ValidateConnection(uint32_t parentId, uint32_t childId) const { const BTNode *parent=m_editingTree.GetNode(parentId);const BTNode *child=m_editingTree.GetNode(childId);if(!parent||!child) return false;if(parentId==childId) return false;if(parent->type !=BTNodeType::Selector &&parent->type !=BTNodeType::Sequence &&parent->type !=BTNodeType::Inverter &&parent->type !=BTNodeType::Repeater) { return false;} if((parent->type==BTNodeType::Inverter||parent->type==BTNodeType::Repeater) &&parent->decoratorChildId !=0) { return false;} if(parent->type==BTNodeType::Selector||parent->type==BTNodeType::Sequence) { if(std::find(parent->childIds.begin(), parent->childIds.end(), childId) !=parent->childIds.end()) { return false;} } std::vector< uint32_t > visited
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)
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.
@ 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.
@ Sequence
AND node - succeeds if all children succeed.
@ Inverter
Decorator - inverts child result.
@ Condition
Leaf node - checks a condition.
@ Repeater
Decorator - repeats child N times.
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
const BehaviorTreeAsset * GetTree(uint32_t treeId) 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
bool ReloadTree(uint32_t treeId)
const BehaviorTreeAsset * GetTreeByPath(const std::string &treePath) const
const BehaviorTreeAsset * GetTreeByAnyId(uint32_t treeId) const
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.
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)
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