Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
BTNodeGraphManager.h
Go to the documentation of this file.
1/*
2 * Olympe Blueprint Editor - Node Graph Manager
3 *
4 * Backend for managing behavior tree and HFSM node graphs
5 * Provides CRUD operations and graph serialization
6 */
7
8#pragma once
9
10#include <string>
11#include <vector>
12#include <map>
13#include <memory>
14#include "../../Source/third_party/nlohmann/json.hpp"
16
17namespace Olympe
18{
19 /**
20 * @struct ClipboardNode
21 * @brief Serializable node data for copy/paste operations
22 */
24 {
25 int nodeId;
26 int nodeType; // Stored as int for serialization
27 std::string name;
28 float posX, posY;
29 std::string actionType;
30 std::string conditionType;
31 std::string decoratorType;
32 std::string subgraphUUID;
33 std::map<std::string, std::string> parameters;
34 std::vector<int> childIds;
36
37 ClipboardNode() : nodeId(0), nodeType(0), posX(0.0f), posY(0.0f), decoratorChildId(-1) {}
38 };
39 // Node type enumeration
40 enum class NodeType
41 {
42 // Behavior Tree - Flow Control (Composite)
48
49 // Behavior Tree - Conditions
51
52 // Behavior Tree - Actions
54
55 // Behavior Tree - Decorators
63
64 // Behavior Tree - Entry Points
65 BT_Root, ///< Phase 38b: Root entry point (green, fixed position)
66 BT_OnEvent, ///< Phase 38b: Event-driven root (green, event-triggered)
67
68 // Behavior Tree - Utilities
70 BT_SubGraph, ///< Phase 8: references a subgraph by UUID (BehaviorTree)
71
72 // HFSM nodes
75 HFSM_SubGraph, ///< Phase 8: references a subgraph by UUID (HFSM)
76
77 // Generic
79 };
80
81 // Convert NodeType to string
82 inline const char* NodeTypeToString(NodeType type)
83 {
84 switch (type)
85 {
86 // Composites
87 case NodeType::BT_Sequence: return "Sequence";
88 case NodeType::BT_Selector: return "Selector";
89 case NodeType::BT_Parallel: return "Parallel";
90 case NodeType::BT_RandomSelector: return "RandomSelector";
91 case NodeType::BT_ParallelThreshold: return "ParallelThreshold";
92
93 // Conditions & Actions
94 case NodeType::BT_Condition: return "Condition";
95 case NodeType::BT_Action: return "Action";
96
97 // Decorators
98 case NodeType::BT_Decorator: return "Decorator";
99 case NodeType::BT_Inverter: return "Inverter";
100 case NodeType::BT_Monitor: return "Monitor";
101 case NodeType::BT_Repeater: return "Repeater";
102 case NodeType::BT_UntilSuccess: return "UntilSuccess";
103 case NodeType::BT_UntilFailure: return "UntilFailure";
104 case NodeType::BT_Cooldown: return "Cooldown";
105
106 // Entry Points
107 case NodeType::BT_Root: return "Root";
108 case NodeType::BT_OnEvent: return "OnEvent";
109
110 // Utilities
111 case NodeType::BT_SendMessage: return "SendMessage";
112 case NodeType::BT_SubGraph: return "SubGraph";
113
114 // HFSM
115 case NodeType::HFSM_State: return "State";
116 case NodeType::HFSM_Transition: return "Transition";
117 case NodeType::HFSM_SubGraph: return "HFSMSubGraph";
118
119 // Generic
120 case NodeType::Comment: return "Comment";
121 default: return "Unknown";
122 }
123 }
124
125 // Convert string to NodeType
126 // Maps ALL BehaviorTree type names (generic and specific implementations) to generic NodeType enum
127 // This ensures consistent styling: all Conditions → BT_Condition, all Actions → BT_Action, etc.
128 inline NodeType StringToNodeType(const std::string& str)
129 {
130 // ====== ENTRY POINTS (GREEN) ======
131 if (str == "Root" || str == "BT_Root") return NodeType::BT_Root;
132 if (str == "OnEvent" || str == "BT_OnEvent") return NodeType::BT_OnEvent;
133
134 // ====== COMPOSITES (DARK BLUE) ======
135 if (str == "Sequence" || str == "BT_Sequence") return NodeType::BT_Sequence;
136 if (str == "Selector" || str == "BT_Selector") return NodeType::BT_Selector;
137 if (str == "Parallel" || str == "BT_Parallel") return NodeType::BT_Parallel;
138 if (str == "RandomSelector" || str == "BT_RandomSelector") return NodeType::BT_RandomSelector;
139 if (str == "ParallelThreshold" || str == "BT_ParallelThreshold") return NodeType::BT_ParallelThreshold;
140
141 // ====== CONDITIONS (PURPLE) ======
142 // Generic condition types
143 if (str == "Condition" || str == "BT_Condition") return NodeType::BT_Condition;
144
145 // Specific condition implementations - all map to BT_Condition for consistent PURPLE color
146 if (str == "CheckBlackboardValue" || str == "BT_CheckBlackboardValue") return NodeType::BT_Condition;
147 if (str == "HasTarget" || str == "BT_HasTarget") return NodeType::BT_Condition;
148 if (str == "IsTargetInRange" || str == "BT_IsTargetInRange") return NodeType::BT_Condition;
149 if (str == "CanSeeTarget" || str == "BT_CanSeeTarget") return NodeType::BT_Condition;
150 if (str == "CanSetTarget" || str == "BT_CanSetTarget") return NodeType::BT_Condition;
151 if (str == "Monitor" || str == "BT_Monitor") return NodeType::BT_Condition; // Monitor is a condition variant
152
153 // ====== DECORATORS (PLUM) ======
154 // Generic decorator
155 if (str == "Decorator" || str == "BT_Decorator") return NodeType::BT_Decorator;
156
157 // Specific decorator implementations
158 if (str == "Inverter" || str == "BT_Inverter") return NodeType::BT_Decorator;
159 if (str == "Cooldown" || str == "BT_Cooldown") return NodeType::BT_Decorator;
160
161 // ====== REPEATERS (YELLOW) ======
162 if (str == "Repeater" || str == "BT_Repeater") return NodeType::BT_Repeater;
163 if (str == "UntilSuccess" || str == "BT_UntilSuccess") return NodeType::BT_Repeater;
164 if (str == "UntilFailure" || str == "BT_UntilFailure") return NodeType::BT_Repeater;
165
166 // ====== ACTIONS (ORANGE) ======
167 // Generic action type
168 if (str == "Action" || str == "BT_Action") return NodeType::BT_Action;
169
170 // Specific action implementations - all map to BT_Action for consistent ORANGE color
171 if (str == "Wait" || str == "BT_Wait") return NodeType::BT_Action;
172 if (str == "WaitRandomTime" || str == "BT_WaitRandomTime") return NodeType::BT_Action;
173 if (str == "SetBlackboardValue" || str == "BT_SetBlackboardValue") return NodeType::BT_Action;
174 if (str == "MoveToTarget" || str == "BT_MoveToTarget") return NodeType::BT_Action;
175 if (str == "MoveToPosition" || str == "BT_MoveToPosition") return NodeType::BT_Action;
176 if (str == "AttackTarget" || str == "BT_AttackTarget") return NodeType::BT_Action;
177 if (str == "PlayAnimation" || str == "BT_PlayAnimation") return NodeType::BT_Action;
178 if (str == "EmitSound" || str == "BT_EmitSound") return NodeType::BT_Action;
179
180 // ====== UTILITIES ======
181 if (str == "SendMessage" || str == "BT_SendMessage") return NodeType::BT_SendMessage;
182 if (str == "SubGraph" || str == "BT_SubGraph") return NodeType::BT_SubGraph;
183
184 // ====== HFSM (Hierarchical Finite State Machine) ======
185 if (str == "State" || str == "HFSM_State") return NodeType::HFSM_State;
186 if (str == "Transition" || str == "HFSM_Transition") return NodeType::HFSM_Transition;
187 if (str == "HFSMSubGraph" || str == "HFSM_SubGraph") return NodeType::HFSM_SubGraph;
188
189 // ====== MISC ======
190 if (str == "Comment") return NodeType::Comment;
191
192 // Default fallback: treat as Action (orange)
193 return NodeType::BT_Action;
194 }
195
196 // Graph node structure
198 {
199 int id = 0;
201 std::string name;
202 float posX = 0.0f;
203 float posY = 0.0f;
204
205 // For Action nodes
206 std::string actionType;
207
208 // For Condition nodes
209 std::string conditionType;
210
211 // For Decorator nodes
212 std::string decoratorType;
213
214 // Phase 8: UUID reference for BT_SubGraph / HFSM_SubGraph nodes.
215 // Empty string means this node is not a subgraph reference.
216 std::string subgraphUUID;
217
218 // Generic parameters (key-value pairs)
219 std::map<std::string, std::string> parameters;
220
221 // Child nodes (for composite nodes)
222 std::vector<int> childIds;
223
224 // Decorator child (single child for decorators)
226
227 // ====================================================================
228 // Event-Driven Execution Fields (for OnEvent nodes)
229 // ====================================================================
230
231 /**
232 * @brief Event type that triggers this OnEvent node
233 * Only used for BT_OnEvent nodes. Value is event type ID string
234 * (e.g., "Olympe_EventType_AI_Explosion")
235 */
236 std::string eventType;
237
238 /**
239 * @brief Optional event message filter for OnEvent nodes
240 * If set, only events with matching message field trigger this node
241 */
242 std::string eventMessage;
243
244 /**
245 * @brief Index of this node in its graph's event roots array (m_eventRootIds)
246 * Only valid if this is an OnEvent node. Used for fast lookup.
247 */
248 uint32_t onEventRootIndex = ~0u; // Invalid index by default
249
250 GraphNode() = default;
251 GraphNode(int nodeId, NodeType nodeType, const std::string& nodeName = "")
252 : id(nodeId), type(nodeType), name(nodeName) {}
253 };
254
255 // Link between nodes
257 {
258 int fromNode = 0;
259 int toNode = 0;
260 int fromAttr = 0; // Output attribute ID
261 int toAttr = 0; // Input attribute ID
262
263 GraphLink() = default;
265 };
266
267 // Editor metadata for graph
269 {
270 float zoom = 1.0f;
271 float scrollOffsetX = 0.0f;
272 float scrollOffsetY = 0.0f;
273 std::string lastModified;
274
275 EditorMetadata() = default;
276 };
277
278 // Node graph (Behavior Tree or HFSM)
280 {
281 public:
282 NodeGraph();
283 ~NodeGraph() = default;
284
285 // Copy constructor and assignment - explicitly defaulted
286 NodeGraph(const NodeGraph& other);
288
289 // Move constructor and assignment
290 NodeGraph(NodeGraph&& other) noexcept;
291 NodeGraph& operator=(NodeGraph&& other) noexcept;
292
293 // Graph metadata
294 std::string name;
295 std::string type; // "BehaviorTree" or "HFSM"
296 int rootNodeId = -1;
298
299 // Node CRUD
300 int CreateNode(NodeType type, float x, float y, const std::string& name = "");
301 bool DeleteNode(int nodeId);
302 GraphNode* GetNode(int nodeId);
303 const GraphNode* GetNode(int nodeId) const;
304 std::vector<GraphNode*> GetAllNodes();
305 std::vector<const GraphNode*> GetAllNodes() const;
306
307 // Link operations
308 bool LinkNodes(int parentId, int childId);
309 bool UnlinkNodes(int parentId, int childId);
310 std::vector<GraphLink> GetAllLinks() const;
311
312 // Parameter operations
313 bool SetNodeParameter(int nodeId, const std::string& paramName, const std::string& value);
314 std::string GetNodeParameter(int nodeId, const std::string& paramName) const;
315
316 // Serialization
317 nlohmann::json ToJson() const;
318 static NodeGraph FromJson(const nlohmann::json& j);
319
320 // Validation
321 bool ValidateGraph(std::string& errorMsg) const;
322
323 // Utility
324 void Clear();
325 int GetNextNodeId() const { return m_NextNodeId; }
326 int GetRootNodeId() const { return rootNodeId; }
327
328 // Calculate node positions for v1 blueprints (hierarchical layout)
329 void CalculateNodePositionsHierarchical();
330
331 // Dirty flag tracking for unsaved changes
332 bool IsDirty() const { return m_IsDirty; }
333 void MarkDirty() { m_IsDirty = true; }
334 void ClearDirty() { m_IsDirty = false; }
335
336 // Filepath tracking
337 const std::string& GetFilepath() const { return m_Filepath; }
338 void SetFilepath(const std::string& filepath) { m_Filepath = filepath; }
339 bool HasFilepath() const { return !m_Filepath.empty(); }
340
341 // Undo/Redo support
342 CommandHistory* GetCommandHistory();
343 const CommandHistory* GetCommandHistory() const;
344 bool CanUndo() const;
345 bool CanRedo() const;
346 std::string GetUndoDescription() const;
347 std::string GetRedoDescription() const;
348 bool Undo();
349 bool Redo();
350
351 // Copy/Paste support
352 std::vector<ClipboardNode> m_clipboardData;
353 void CopyNodesToClipboard(const std::vector<int>& nodeIds);
354 std::vector<int> PasteNodesFromClipboard(float offsetX = 30.0f, float offsetY = 30.0f);
355 std::vector<int> DuplicateNodes(const std::vector<int>& nodeIds, float offsetX = 30.0f, float offsetY = 30.0f);
356
357 private:
358 std::vector<GraphNode> m_Nodes;
360 bool m_IsDirty = false;
361 std::string m_Filepath;
362 std::unique_ptr<CommandHistory> m_commandHistory;
363
364 // ====================================================================
365 // Event Root Tracking (for OnEvent nodes)
366 // ====================================================================
367 /**
368 * @brief Separate array of node IDs that are OnEvent root nodes
369 * These nodes represent independent execution trees triggered by EventQueue messages
370 * Not connected to the main Root node tree
371 */
372 std::vector<uint32_t> m_eventRootIds;
373
374 // ====================================================================
375 // Helper methods
376 // ====================================================================
377
378 /**
379 * @brief Check if a node ID is a valid root (main Root or OnEvent root)
380 * Root nodes cannot be deleted or moved under other nodes
381 */
382 bool IsValidRoot(uint32_t nodeId) const;
383
384 /**
385 * @brief Add node ID to event roots array
386 * Called when creating an OnEvent node
387 */
388 void AddEventRoot(uint32_t nodeId);
389
390 /**
391 * @brief Remove node ID from event roots array
392 * Called when deleting an OnEvent node
393 */
394 void RemoveEventRoot(uint32_t nodeId);
395
396 /**
397 * @brief Get all OnEvent root node IDs
398 */
399 const std::vector<uint32_t>& GetEventRootIds() const;
400
401 // Helper to find node index
402 int FindNodeIndex(int nodeId) const;
403 };
404
405 /**
406 * NodeGraphManager - Manages multiple node graphs
407 * Allows opening multiple behavior trees/FSMs simultaneously
408 */
410 {
411 public:
412 static NodeGraphManager& Instance();
413 static NodeGraphManager& Get() { return Instance(); }
414
415 // Lifecycle
416 void Initialize();
417 void Shutdown();
418
419 // Graph management
420 int CreateGraph(const std::string& name, const std::string& type);
421 bool CloseGraph(int graphId);
422 NodeGraph* GetGraph(int graphId);
423 const NodeGraph* GetGraph(int graphId) const;
424
425 // Active graph
426 void SetActiveGraph(int graphId);
427 int GetActiveGraphId() const { return m_ActiveGraphId; }
429 const NodeGraph* GetActiveGraph() const;
430
431 // Graph list
432 std::vector<int> GetAllGraphIds() const;
433 std::string GetGraphName(int graphId) const;
434 void SetGraphOrder(const std::vector<int>& newOrder);
435
436 // File operations
437 bool SaveGraph(int graphId, const std::string& filepath);
438 int LoadGraph(const std::string& filepath);
439
440 // State
441 bool IsInitialized() const { return m_Initialized; }
442
443 // Dirty flag queries for graphs
444 bool IsGraphDirty(int graphId) const;
445 bool HasUnsavedChanges() const; // Returns true if any graph has unsaved changes
446
447 private:
450
453
454 private:
455 bool m_Initialized = false;
457 int m_LastActiveGraphId = -1; // Track last active for persistence
459 std::map<int, std::unique_ptr<NodeGraph>> m_Graphs;
460 std::vector<int> m_GraphOrder; // Track insertion order for consistent tab rendering
461 };
462}
Undo/redo history manager for graph commands.
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
Manages undo/redo stacks for graph operations.
NodeGraphManager - Manages multiple node graphs Allows opening multiple behavior trees/FSMs simultane...
bool IsGraphDirty(int graphId) const
std::vector< int > GetAllGraphIds() const
void SetGraphOrder(const std::vector< int > &newOrder)
int LoadGraph(const std::string &filepath)
NodeGraphManager(const NodeGraphManager &)=delete
NodeGraph * GetGraph(int graphId)
std::string GetGraphName(int graphId) const
int CreateGraph(const std::string &name, const std::string &type)
std::vector< int > m_GraphOrder
static NodeGraphManager & Instance()
bool SaveGraph(int graphId, const std::string &filepath)
static NodeGraphManager & Get()
std::map< int, std::unique_ptr< NodeGraph > > m_Graphs
NodeGraphManager & operator=(const NodeGraphManager &)=delete
bool UnlinkNodes(int parentId, int childId)
bool DeleteNode(int nodeId)
const std::string & GetFilepath() const
NodeGraph & operator=(const NodeGraph &other)
void AddEventRoot(uint32_t nodeId)
Add node ID to event roots array Called when creating an OnEvent node.
static NodeGraph FromJson(const nlohmann::json &j)
void CopyNodesToClipboard(const std::vector< int > &nodeIds)
const std::vector< uint32_t > & GetEventRootIds() const
Get all OnEvent root node IDs.
void SetFilepath(const std::string &filepath)
bool LinkNodes(int parentId, int childId)
std::vector< int > PasteNodesFromClipboard(float offsetX=30.0f, float offsetY=30.0f)
EditorMetadata editorMetadata
std::vector< ClipboardNode > m_clipboardData
std::vector< GraphLink > GetAllLinks() const
std::vector< int > DuplicateNodes(const std::vector< int > &nodeIds, float offsetX=30.0f, float offsetY=30.0f)
std::unique_ptr< CommandHistory > m_commandHistory
bool IsValidRoot(uint32_t nodeId) const
Check if a node ID is a valid root (main Root or OnEvent root) Root nodes cannot be deleted or moved ...
std::vector< uint32_t > m_eventRootIds
Separate array of node IDs that are OnEvent root nodes These nodes represent independent execution tr...
bool SetNodeParameter(int nodeId, const std::string &paramName, const std::string &value)
nlohmann::json ToJson() const
~NodeGraph()=default
void RemoveEventRoot(uint32_t nodeId)
Remove node ID from event roots array Called when deleting an OnEvent node.
std::vector< GraphNode * > GetAllNodes()
std::vector< GraphNode > m_Nodes
bool ValidateGraph(std::string &errorMsg) const
int FindNodeIndex(int nodeId) const
GraphNode * GetNode(int nodeId)
std::string GetNodeParameter(int nodeId, const std::string &paramName) const
int CreateNode(NodeType type, float x, float y, const std::string &name="")
< Provides AssetID and INVALID_ASSET_ID
@ BT_OnEvent
Phase 38b: Event-driven root (green, event-triggered)
@ BT_SubGraph
Phase 8: references a subgraph by UUID (BehaviorTree)
@ BT_Root
Phase 38b: Root entry point (green, fixed position)
@ HFSM_SubGraph
Phase 8: references a subgraph by UUID (HFSM)
const char * NodeTypeToString(NodeType type)
NodeType StringToNodeType(const std::string &str)
nlohmann::json json
Serializable node data for copy/paste operations.
std::map< std::string, std::string > parameters
std::vector< int > childIds
GraphNode()=default
uint32_t onEventRootIndex
Index of this node in its graph's event roots array (m_eventRootIds) Only valid if this is an OnEvent...
std::map< std::string, std::string > parameters
std::string eventType
Event type that triggers this OnEvent node Only used for BT_OnEvent nodes.
GraphNode(int nodeId, NodeType nodeType, const std::string &nodeName="")
std::vector< int > childIds
std::string eventMessage
Optional event message filter for OnEvent nodes If set, only events with matching message field trigg...