Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
EntityPrefabEditorPlugin.cpp
Go to the documentation of this file.
1/*
2 * Olympe Blueprint Editor - Entity Prefab Plugin Implementation
3 */
4
6#include "../../Source/third_party/imgui/imgui.h"
7#include <chrono>
8#include <sstream>
9#include <iomanip>
10#include <cstring>
11
13
14namespace Olympe
15{
19
23
25 {
26 auto now = std::chrono::system_clock::now();
27 auto time = std::chrono::system_clock::to_time_t(now);
28 std::stringstream ss;
29
30 // Use localtime_s for MSVC, localtime_r for other platforms
31 #ifdef _MSC_VER
32 std::tm timeinfo;
34 ss << std::put_time(&timeinfo, "%Y-%m-%dT%H:%M:%S");
35 #else
36 ss << std::put_time(std::localtime(&time), "%Y-%m-%dT%H:%M:%S");
37 #endif
38
39 return ss.str();
40 }
41
43 {
45 prefab["schema_version"] = 2;
46 prefab["blueprintType"] = "EntityPrefab";
47 prefab["name"] = name;
48 prefab["description"] = "";
49 prefab["metadata"]["author"] = "Atlasbruce";
50 prefab["metadata"]["created"] = GetCurrentTimestamp();
51 prefab["metadata"]["lastModified"] = GetCurrentTimestamp();
52 prefab["metadata"]["tags"] = json::array();
53
54 // Components by default
55 prefab["data"]["prefabName"] = name;
56 prefab["data"]["components"] = json::array();
57
58 // Identity + Position by default
60 identity["type"] = "Identity_data";
61 identity["properties"]["name"] = name + "_{id}";
62 identity["properties"]["tag"] = name;
63 identity["properties"]["entityType"] = "Generic";
64
65 json position;
66 position["type"] = "Position_data";
67
68 // Create position object using initializer list syntax
70 posObj["x"] = 0;
71 posObj["y"] = 0;
72 posObj["z"] = 0;
73 position["properties"]["position"] = posObj;
74
75 prefab["data"]["components"].push_back(identity);
76 prefab["data"]["components"].push_back(position);
77
78 return prefab;
79 }
80
82 {
83 // V2 format check
84 if (blueprint.contains("blueprintType") &&
85 blueprint["blueprintType"] == "EntityPrefab")
86 {
87 return true;
88 }
89
90 // V1 format heuristic
91 return blueprint.contains("components");
92 }
93
94 std::vector<ValidationError> EntityPrefabEditorPlugin::Validate(const json& blueprint)
95 {
96 std::vector<ValidationError> errors;
97
98 if (!blueprint.contains("data"))
99 {
101 err.nodeId = -1;
102 err.nodeName = "";
103 err.message = "Missing 'data' section";
104 err.severity = ErrorSeverity::Error;
105 errors.push_back(err);
106 return errors;
107 }
108
109 const json& data = blueprint["data"];
110
111 if (!data.contains("components"))
112 {
114 err.nodeId = -1;
115 err.nodeName = "";
116 err.message = "Missing 'components' array";
117 err.severity = ErrorSeverity::Error;
118 errors.push_back(err);
119 }
120
121 return errors;
122 }
123
125 {
126 if (!blueprintData.contains("data"))
127 {
128 ImGui::Text("Invalid blueprint: missing 'data' section");
129 return;
130 }
131
132 auto& data = blueprintData["data"];
133
134 if (!data.contains("components"))
135 {
136 data["components"] = json::array();
137 }
138
139 auto& components = data["components"];
140
141 ImGui::Text("Components: %d", (int)components.size());
142 ImGui::Separator();
143
144 // List components
145 size_t toRemove = (size_t)-1;
146 for (size_t i = 0; i < components.size(); ++i)
147 {
148 auto& comp = components[i];
149 std::string compType = comp.value("type", "Unknown");
150
151 ImGui::PushID((int)i);
152
153 bool headerOpen = ImGui::CollapsingHeader(compType.c_str(), ImGuiTreeNodeFlags_DefaultOpen);
154
155 // Remove button
156 ImGui::SameLine(ImGui::GetWindowWidth() - 80);
157 if (ImGui::SmallButton("Remove"))
158 {
159 toRemove = i;
160 }
161
162 if (headerOpen)
163 {
164 ImGui::Indent();
165 if (comp.contains("properties"))
166 {
168 }
169 ImGui::Unindent();
170 }
171
172 ImGui::PopID();
173 }
174
175 // Remove component if requested
176 if (toRemove != (size_t)-1)
177 {
178 components.erase(toRemove);
179 ctx.isDirty = true;
180 }
181
182 ImGui::Separator();
183
184 // Add Component button
185 if (ImGui::Button("Add Component", ImVec2(-1, 0)))
186 {
187 ImGui::OpenPopup("AddComponentPopup");
188 }
189
190 if (ImGui::BeginPopup("AddComponentPopup"))
191 {
193 for (const auto& type : availableTypes)
194 {
195 if (ImGui::MenuItem(type.c_str()))
196 {
197 AddComponentToBlueprint(blueprintData, type);
198 ctx.isDirty = true;
199 }
200 }
201 ImGui::EndPopup();
202 }
203 }
204
206 {
207 ImGui::Text("Type: Entity Prefab");
208
209 if (blueprintData.contains("data") && blueprintData["data"].contains("components"))
210 {
211 ImGui::Text("Components: %d", (int)blueprintData["data"]["components"].size());
212 }
213 else
214 {
215 ImGui::Text("Components: 0");
216 }
217 }
218
220 {
221 if (ImGui::Button("Add Component"))
222 {
223 ImGui::OpenPopup("AddComponentToolbar");
224 }
225 }
226
228 {
229 // C++14 compatible: use explicit iterators without structured bindings
230 for (auto it = properties.begin(); it != properties.end(); ++it)
231 {
232 const std::string& key = it.key();
233 json& value = it.value();
234
235 ImGui::PushID(key.c_str());
236
237 if (value.is_number_float())
238 {
239 float f = value.get<float>();
240 if (ImGui::DragFloat(key.c_str(), &f, 1.0f))
241 {
242 properties[key] = f;
243 ctx.isDirty = true;
244 }
245 }
246 else if (value.is_number_integer())
247 {
248 int i = value.get<int>();
249 if (ImGui::InputInt(key.c_str(), &i))
250 {
251 properties[key] = i;
252 ctx.isDirty = true;
253 }
254 }
255 else if (value.is_string())
256 {
257 std::string str = value.get<std::string>();
258 char buffer[256];
259 #ifdef _MSC_VER
260 strncpy_s(buffer, 256, str.c_str(), 255);
261 #else
262 strncpy(buffer, str.c_str(), 255);
263 #endif
264 buffer[255] = '\0';
265 if (ImGui::InputText(key.c_str(), buffer, 256))
266 {
267 properties[key] = std::string(buffer);
268 ctx.isDirty = true;
269 }
270 }
271 else if (value.is_object())
272 {
273 // Nested object (e.g., position with x,y,z)
274 if (ImGui::TreeNode(key.c_str()))
275 {
277 ImGui::TreePop();
278 }
279 }
280
281 ImGui::PopID();
282 }
283 }
284
285 void EntityPrefabEditorPlugin::AddComponentToBlueprint(json& blueprintData, const std::string& type)
286 {
288 newComp["type"] = type;
289 newComp["properties"] = json::object();
290
291 blueprintData["data"]["components"].push_back(newComp);
292 }
293
295 {
296 return {
297 "Identity_data", "Position_data", "VisualSprite_data",
298 "BoundingBox_data", "Movement_data", "PhysicsBody_data",
299 "Health_data", "PlayerBinding_data", "Controller_data",
300 "AIBlackboard_data", "AISenses_data", "AIState_data",
301 "BehaviorTreeRuntime_data", "MoveIntent_data", "AttackIntent_data"
302 };
303 }
304}
nlohmann::json json
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
void RenderEditor(nlohmann::json &blueprintData, EditorContext_st &ctx) override
nlohmann::json CreateNew(const std::string &name) override
bool CanHandle(const nlohmann::json &blueprint) const override
void RenderProperties(const nlohmann::json &blueprintData) override
void AddComponentToBlueprint(nlohmann::json &blueprintData, const std::string &type)
std::vector< std::string > GetAvailableComponentTypes()
void RenderToolbar(nlohmann::json &blueprintData) override
std::vector< ValidationError > Validate(const nlohmann::json &blueprint) override
void RenderComponentPropertiesEditor(nlohmann::json &properties, EditorContext_st &ctx)
nlohmann::json json
nlohmann::json json