Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
Serializer.h
Go to the documentation of this file.
1#pragma once
2
3#include "../AI/BehaviorTree.h"
4#include "../AI/BTGraphLayoutEngine.h"
5#include "../third_party/nlohmann/json.hpp"
6#include <string>
7
8namespace Olympe
9{
10 namespace NodeGraphShared
11 {
13
14 // Serialize a BehaviorTreeAsset to JSON using layout positions from the
15 // provided layout engine (may be null).
17 {
18 json j = json::object();
19
20 j["schema_version"] = 2;
21 j["type"] = "BehaviorTree";
22 j["blueprintType"] = "BehaviorTree";
23 j["name"] = tree.name;
24 j["description"] = "";
25
26 json metadata = json::object();
27 metadata["author"] = "BT Editor";
28 // Simple timestamp
29 time_t now = time(nullptr);
30 struct tm timeinfo;
31 #ifdef _WIN32
33 #else
35 #endif
36 char buffer[32];
37 strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", &timeinfo);
38 metadata["created"] = std::string(buffer);
39 metadata["lastModified"] = std::string(buffer);
40
41 json tags = json::array();
42 tags.push_back("AI");
43 tags.push_back("BehaviorTree");
44 metadata["tags"] = tags;
45
46 j["metadata"] = metadata;
47
48 json editorState = json::object();
49 editorState["zoom"] = 1.0;
50 json scrollOffset = json::object();
51 scrollOffset["x"] = 0.0;
52 scrollOffset["y"] = 0.0;
53 editorState["scrollOffset"] = scrollOffset;
54 j["editorState"] = editorState;
55
56 json data = json::object();
57 data["rootNodeId"] = static_cast<int>(tree.rootNodeId);
58
59 json nodesArray = json::array();
60 for (const auto& node : tree.nodes)
61 {
62 json nodeJson = json::object();
63 nodeJson["id"] = static_cast<int>(node.id);
64 nodeJson["name"] = node.name;
65
66 const char* typeStr = "";
67 switch (node.type)
68 {
69 case BTNodeType::Selector: typeStr = "Selector"; break;
70 case BTNodeType::Sequence: typeStr = "Sequence"; break;
71 case BTNodeType::Condition: typeStr = "Condition"; break;
72 case BTNodeType::Action: typeStr = "Action"; break;
73 case BTNodeType::Inverter: typeStr = "Inverter"; break;
74 case BTNodeType::Repeater: typeStr = "Repeater"; break;
75 }
76 nodeJson["type"] = typeStr;
77
78 json pos = json::object();
79
80 // Phase 38: Use stored editorPosX/Y if available, otherwise try layoutEngine, else auto-layout
81 if (node.editorPosX != 0.0f || node.editorPosY != 0.0f)
82 {
83 // Use stored positions (from editor canvas)
84 pos["x"] = node.editorPosX;
85 pos["y"] = node.editorPosY;
86 }
87 else if (layoutEngine)
88 {
89 const BTNodeLayout* nodeLayout = layoutEngine->GetNodeLayout(node.id);
90 if (nodeLayout)
91 {
92 pos["x"] = nodeLayout->position.x;
93 pos["y"] = nodeLayout->position.y;
94 }
95 else
96 {
97 pos["x"] = 200.0f;
98 pos["y"] = 100.0f * static_cast<float>(node.id);
99 }
100 }
101 else
102 {
103 pos["x"] = 200.0f;
104 pos["y"] = 100.0f * static_cast<float>(node.id);
105 }
106 nodeJson["position"] = pos;
107
108 if (node.type == BTNodeType::Selector || node.type == BTNodeType::Sequence)
109 {
110 json children = json::array();
111 for (uint32_t cid : node.childIds)
112 children.push_back(static_cast<int>(cid));
113 nodeJson["children"] = children;
114 }
115
116 if (node.type == BTNodeType::Inverter || node.type == BTNodeType::Repeater)
117 {
118 if (node.decoratorChildId != 0)
119 nodeJson["decoratorChildId"] = static_cast<int>(node.decoratorChildId);
120 if (node.type == BTNodeType::Repeater)
121 nodeJson["repeatCount"] = node.repeatCount;
122 }
123
124 if (node.type == BTNodeType::Action)
125 {
126 const char* actionTypeStr = "";
127 switch (node.actionType)
128 {
129 case BTActionType::SetMoveGoalToLastKnownTargetPos: actionTypeStr = "SetMoveGoalToLastKnownTargetPos"; break;
130 case BTActionType::SetMoveGoalToTarget: actionTypeStr = "SetMoveGoalToTarget"; break;
131 case BTActionType::SetMoveGoalToPatrolPoint: actionTypeStr = "SetMoveGoalToPatrolPoint"; break;
132 case BTActionType::MoveToGoal: actionTypeStr = "MoveToGoal"; break;
133 case BTActionType::AttackIfClose: actionTypeStr = "AttackIfClose"; break;
134 case BTActionType::PatrolPickNextPoint: actionTypeStr = "PatrolPickNextPoint"; break;
135 case BTActionType::ClearTarget: actionTypeStr = "ClearTarget"; break;
136 case BTActionType::Idle: actionTypeStr = "Idle"; break;
137 case BTActionType::WaitRandomTime: actionTypeStr = "WaitRandomTime"; break;
138 case BTActionType::ChooseRandomNavigablePoint: actionTypeStr = "ChooseRandomNavigablePoint"; break;
139 case BTActionType::RequestPathfinding: actionTypeStr = "RequestPathfinding"; break;
140 case BTActionType::FollowPath: actionTypeStr = "FollowPath"; break;
141 }
142 nodeJson["actionType"] = actionTypeStr;
143
144 json params = json::object();
145 params["param1"] = node.actionParam1;
146 params["param2"] = node.actionParam2;
147 nodeJson["parameters"] = params;
148 }
149
150 if (node.type == BTNodeType::Condition)
151 {
152 const char* conditionTypeStr = "";
153 switch (node.conditionType)
154 {
155 case BTConditionType::TargetVisible: conditionTypeStr = "TargetVisible"; break;
156 case BTConditionType::TargetInRange: conditionTypeStr = "TargetInRange"; break;
157 case BTConditionType::HealthBelow: conditionTypeStr = "HealthBelow"; break;
158 case BTConditionType::HasMoveGoal: conditionTypeStr = "HasMoveGoal"; break;
159 case BTConditionType::CanAttack: conditionTypeStr = "CanAttack"; break;
160 case BTConditionType::HeardNoise: conditionTypeStr = "HeardNoise"; break;
161 case BTConditionType::IsWaitTimerExpired: conditionTypeStr = "IsWaitTimerExpired"; break;
162 case BTConditionType::HasNavigableDestination: conditionTypeStr = "HasNavigableDestination"; break;
163 case BTConditionType::HasValidPath: conditionTypeStr = "HasValidPath"; break;
164 case BTConditionType::HasReachedDestination: conditionTypeStr = "HasReachedDestination"; break;
165 }
166 nodeJson["conditionType"] = conditionTypeStr;
167
168 json params = json::object();
169 params["param"] = node.conditionParam;
170 nodeJson["parameters"] = params;
171 }
172
173 if (!nodeJson.contains("parameters"))
174 nodeJson["parameters"] = json::object();
175
176 nodesArray.push_back(nodeJson);
177 }
178
179 data["nodes"] = nodesArray;
180 j["data"] = data;
181
182 return j;
183 }
184 }
185}
@ 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.
@ Idle
Do nothing.
@ SetMoveGoalToPatrolPoint
Move to next patrol waypoint.
@ RequestPathfinding
Request pathfinding to moveGoal via MoveIntent.
@ FollowPath
Follow the path (check progression)
@ 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.
@ 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()
Definition ECS_Entity.h:56
Computes clean hierarchical layouts for behavior trees.
float x
Definition vector.h:25
nlohmann::json json
Definition Serializer.h:12
json SerializeBehaviorTreeToJson(const BehaviorTreeAsset &tree, const BTGraphLayoutEngine *layoutEngine)
Definition Serializer.h:16
< Provides AssetID and INVALID_ASSET_ID
nlohmann::json json
Layout information for a single behavior tree node.
Vector position
Final position (x, y)