Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
BlueprintMigrator.cpp
Go to the documentation of this file.
1/*
2 * Olympe Blueprint Editor - Blueprint Migrator Implementation
3 */
4
5#include "BlueprintMigrator.h"
6#include <chrono>
7#include <sstream>
8#include <iomanip>
9#include <algorithm>
10#include <queue>
11#include <tuple>
12
14
15namespace Olympe
16{
20
24
26 {
27 return blueprint.contains("schema_version") &&
28 blueprint["schema_version"].get<int>() == 2;
29 }
30
32 {
33 auto now = std::chrono::system_clock::now();
34 auto time = std::chrono::system_clock::to_time_t(now);
35 std::tm timeInfo;
36 #ifdef _WIN32
38 #else
40 #endif
41 std::stringstream ss;
42 ss << std::put_time(&timeInfo, "%Y-%m-%dT%H:%M:%S");
43 return ss.str();
44 }
45
47 {
48 // Detect based on structure
49 if (blueprint.contains("components"))
50 {
51 return "EntityPrefab";
52 }
53
54 if (blueprint.contains("rootNodeId") && blueprint.contains("nodes"))
55 {
56 // Check if it's HFSM (has states) or BehaviorTree
57 if (blueprint.contains("states"))
58 {
59 return "HFSM";
60 }
61 return "BehaviorTree";
62 }
63
64 return "Unknown";
65 }
66
68 {
69 // Check if already v2
70 if (IsV2(v1Blueprint))
71 {
72 return v1Blueprint;
73 }
74
75 json v2;
76
77 // Detect type
79
80 // Base structure
81 v2["schema_version"] = 2;
82 v2["blueprintType"] = detectedType;
83 v2["name"] = v1Blueprint.value<std::string>("name", "Unnamed");
84 v2["description"] = "";
85
86 // Metadata
87 v2["metadata"]["author"] = "Atlasbruce";
88 v2["metadata"]["created"] = GetCurrentTimestamp();
89 v2["metadata"]["lastModified"] = GetCurrentTimestamp();
90 v2["metadata"]["tags"] = json::array();
91
92 // Editor state
93 v2["editorState"]["zoom"] = 1.0;
94 v2["editorState"]["scrollOffset"] = json::object();
95 v2["editorState"]["scrollOffset"]["x"] = 0;
96 v2["editorState"]["scrollOffset"]["y"] = 0;
97
98 // Data section
99 v2["data"] = json::object();
100
101 // Type-specific migration
102 if (detectedType == "BehaviorTree")
103 {
105 }
106 else if (detectedType == "HFSM")
107 {
108 MigrateHFSM(v1Blueprint, v2["data"]);
109 }
110 else if (detectedType == "EntityPrefab")
111 {
113 }
114
115 return v2;
116 }
117
119 {
120 v2Data["rootNodeId"] = v1.value<int>("rootNodeId", 1);
121 v2Data["nodes"] = json::array();
122
123 if (!v1.contains("nodes") || !v1["nodes"].is_array())
124 {
125 return;
126 }
127
128 // Build children map for layout calculation
129 std::map<int, std::vector<int>> childrenMap;
130 for (size_t i = 0; i < v1["nodes"].size(); ++i)
131 {
132 const json& node = v1["nodes"][i];
133 int id = node.value<int>("id", 0);
134 if (node.contains("children") && node["children"].is_array())
135 {
136 std::vector<int> children;
137 for (size_t j = 0; j < node["children"].size(); ++j)
138 {
139 if (node["children"][j].is_number())
140 {
141 children.push_back(node["children"][j].get<int>());
142 }
143 }
144 childrenMap[id] = children;
145 }
146 }
147
148 // Calculate positions
149 int rootId = v1.value<int>("rootNodeId", 1);
150 std::map<int, NodeLayout> layouts = CalculateHierarchicalLayout(v1["nodes"], childrenMap, rootId);
151
152 // Migrate each node
153 for (size_t i = 0; i < v1["nodes"].size(); ++i)
154 {
155 const json& v1Node = v1["nodes"][i];
156 json v2Node;
157 int nodeId = v1Node.value<int>("id", 0);
158
159 v2Node["id"] = nodeId;
160 v2Node["name"] = v1Node.value<std::string>("name", "Unnamed");
161 v2Node["type"] = v1Node.value<std::string>("type", "Unknown");
162
163 // Position from calculated layout
164 if (layouts.count(nodeId))
165 {
166 v2Node["position"]["x"] = layouts[nodeId].posX;
167 v2Node["position"]["y"] = layouts[nodeId].posY;
168 }
169 else
170 {
171 // Fallback position
172 v2Node["position"]["x"] = 400.0;
173 v2Node["position"]["y"] = 300.0;
174 }
175
176 // Copy children array if present
177 if (v1Node.contains("children"))
178 {
179 v2Node["children"] = v1Node["children"];
180 }
181
182 // Type-specific fields
183 if (v1Node.contains("actionType"))
184 {
185 v2Node["actionType"] = v1Node["actionType"];
186 }
187 if (v1Node.contains("conditionType"))
188 {
189 v2Node["conditionType"] = v1Node["conditionType"];
190 }
191 if (v1Node.contains("decoratorType"))
192 {
193 v2Node["decoratorType"] = v1Node["decoratorType"];
194 }
195
196 // Migrate parameters to unified structure
198
199 v2Data["nodes"].push_back(v2Node);
200 }
201 }
202
204 {
205 // Similar to BehaviorTree but for HFSM
206 v2Data["initialState"] = v1.value<std::string>("initialState", "");
207 v2Data["states"] = json::array();
208
209 if (v1.contains("states") && v1["states"].is_array())
210 {
211 for (size_t i = 0; i < v1["states"].size(); ++i)
212 {
213 const json& state = v1["states"][i];
214 json v2State = state;
215 // Add position if not present
216 if (!v2State.contains("position"))
217 {
218 v2State["position"]["x"] = 400.0;
219 v2State["position"]["y"] = 300.0;
220 }
221 v2Data["states"].push_back(v2State);
222 }
223 }
224
225 if (v1.contains("transitions"))
226 {
227 v2Data["transitions"] = v1["transitions"];
228 }
229 }
230
232 {
233 // EntityPrefab migration - mostly copy as-is
234 v2Data["prefabName"] = v1.value<std::string>("name", "Unnamed");
235
236 if (v1.contains("components"))
237 {
238 v2Data["components"] = v1["components"];
239 }
240 else
241 {
242 v2Data["components"] = json::array();
243 }
244 }
245
247 {
248 v2Node["parameters"] = json::object();
249
250 // Migrate old parameter fields to new unified structure
251 if (v1Node.contains("param"))
252 {
253 v2Node["parameters"]["param"] = v1Node["param"];
254 }
255 if (v1Node.contains("param1"))
256 {
257 v2Node["parameters"]["param1"] = v1Node["param1"];
258 }
259 if (v1Node.contains("param2"))
260 {
261 v2Node["parameters"]["param2"] = v1Node["param2"];
262 }
263 }
264
265 std::map<int, BlueprintMigrator::NodeLayout> BlueprintMigrator::CalculateHierarchicalLayout(
266 const json& nodes,
267 const std::map<int, std::vector<int>>& childrenMap,
268 int rootId)
269 {
270 std::map<int, NodeLayout> layouts;
271
272 // BFS to calculate positions
273 std::queue<std::tuple<int, int, int>> queue; // nodeId, depth, siblingIndex
274 queue.push(std::make_tuple(rootId, 0, 0));
275
276 std::map<int, int> depthCounter; // Count nodes at each depth
277
278 while (!queue.empty())
279 {
280 std::tuple<int, int, int> front = queue.front();
281 queue.pop();
282
283 int nodeId = std::get<0>(front);
284 int depth = std::get<1>(front);
285 int siblingIndex = std::get<2>(front);
286
287 // Calculate position
289 layout.nodeId = nodeId;
290 layout.depth = depth;
291 layout.siblingIndex = depthCounter[depth]++;
292
293 layout.posX = START_X + depth * HORIZONTAL_SPACING;
294 layout.posY = START_Y + layout.siblingIndex * VERTICAL_SPACING;
295
296 layouts[nodeId] = layout;
297
298 // Add children to queue
299 if (childrenMap.count(nodeId))
300 {
301 int childIndex = 0;
302 for (int childId : childrenMap.at(nodeId))
303 {
304 queue.push(std::make_tuple(childId, depth + 1, childIndex++));
305 }
306 }
307 }
308
309 return layouts;
310 }
311}
nlohmann::json json
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
static constexpr float HORIZONTAL_SPACING
nlohmann::json MigrateToV2(const nlohmann::json &v1Blueprint)
static constexpr float VERTICAL_SPACING
static constexpr float START_Y
std::string DetectBlueprintType(const nlohmann::json &blueprint)
void MigrateEntityPrefab(const nlohmann::json &v1, nlohmann::json &v2Data)
std::map< int, NodeLayout > CalculateHierarchicalLayout(const nlohmann::json &nodes, const std::map< int, std::vector< int > > &childrenMap, int rootId)
void MigrateHFSM(const nlohmann::json &v1, nlohmann::json &v2Data)
void MigrateBehaviorTree(const nlohmann::json &v1, nlohmann::json &v2Data)
static constexpr float START_X
void MigrateParameters(const nlohmann::json &v1Node, nlohmann::json &v2Node)
bool IsV2(const nlohmann::json &blueprint) const
nlohmann::json json
nlohmann::json json