// IAEngine: 2D Game Engine by IA // Copyright (C) 2025 IASoft (PVT) LTD (oss@iasoft.dev) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include #include #include namespace ia::iae { // ----------------------------------------------- // getInnerXML and xml_string_writer were taken from https://stackoverflow.com/a/60337372 struct xml_string_writer : pugi::xml_writer { std::string result; virtual void write(const void *data, size_t size) { result.append(static_cast(data), size); } }; const auto getInnerXML = [](pugi::xml_node target) { xml_string_writer writer; for (pugi::xml_node child = target.first_child(); child; child = child.next_sibling()) child.print(writer, ""); return writer.result; }; // ----------------------------------------------- } // namespace ia::iae namespace ia::iae { Map SceneManager::s_scenes; std::function(IN CONST String &, IN CONST Vector &)> SceneManager::s_customNodeGetter; std::function SceneManager::s_resourceGetter; VOID SceneManager::Initialize(){} VOID SceneManager::Terminate(){ for (const auto &t : s_scenes) delete t->Value; } VOID SceneManager::Setup( IN std::function(IN CONST String &, IN CONST Vector &)> getCustomNode, IN std::function getResource) { s_customNodeGetter = getCustomNode; s_resourceGetter = getResource; } VOID SceneManager::SwitchTo(IN CONST String &name) { Engine::ChangeActiveScene(s_scenes[name]); } Scene *SceneManager::GetScene(IN CONST String &name) { return s_scenes[name]; } VOID SceneManager::AddScene(IN Scene *scene, IN CONST String &name, IN CONST String &xml) { pugi::xml_document doc; doc.load_string(xml.c_str()); const auto sceneRoot = doc.child("Scene"); Map resources; // Process Resources const auto resRoot = sceneRoot.child("Resources"); if (!resRoot) THROW_INVALID_DATA("Scene file is missing 'Resources' tag"); { for (const auto &t : resRoot.children()) { if (!strcmp(t.name(), "Image")) { resources[t.attribute("name").as_string()] = s_resourceGetter( ResourceType::IMAGE, t.attribute("path").as_string(), t.attribute("index").as_llong()); } else if (!strcmp(t.name(), "Audio")) { resources[t.attribute("name").as_string()] = s_resourceGetter(ResourceType::SOUND, t.attribute("path").as_string(), 0); } } } // Process Properties const auto propRoot = sceneRoot.child("Properties"); if (!propRoot) THROW_INVALID_DATA("Scene file is missing 'Properties' tag"); { auto t = propRoot.child("Extent"); scene->Extent() = Vec2{t.attribute("width").as_float(), t.attribute("height").as_float()}; t = propRoot.child("DesignViewport"); if (t) scene->DesignViewport() = Vec2{t.attribute("width").as_float(), t.attribute("height").as_float()}; else scene->DesignViewport() = Vec2{}; scene->EnableOnScreenGamePad() = false; t = propRoot.child("GamePad"); if (t) { const auto t2 = t.attribute("enableOnScreen"); if (t2 && t2.as_bool()) scene->EnableOnScreenGamePad() = true; } } // Process Nodes const auto nodeRoot = sceneRoot.child("Nodes"); if (!nodeRoot) THROW_INVALID_DATA("Scene file is missing 'Nodes' tag"); { for (const auto &t : nodeRoot) { Node2D *n{}; if (!strcmp(t.name(), "TextureNode")) { const auto node = MakeRefPtr(Engine::GetUniqueResourceName()); node->GetTextureComponent()->SetTexture(resources[t.attribute("texture").as_string()]); scene->AddNode(node); n = node.get(); } else { const auto node = s_customNodeGetter(t.name(), String(t.attribute("id").as_string()).split(';')); scene->AddNode(node); n = node.get(); } if (!n) continue; if (t.attribute("X") && t.attribute("Y")) n->SetLocalPosition({t.attribute("X").as_float(), t.attribute("Y").as_float()}); } } // Process UI const auto uiRoot = sceneRoot.child("UI"); if (!uiRoot) THROW_INVALID_DATA("Scene file is missing 'UI' tag"); { if (uiRoot.child("CSS") && uiRoot.child("HTML")) { scene->UIMarkupStyles() = getInnerXML(uiRoot.child("CSS")).c_str(); auto html = String(getInnerXML(uiRoot.child("HTML")).c_str()); html = Utils::RegexReplaceGroups(html, "UIMarkup() = html; } else { } } s_scenes[name] = scene; } } // namespace ia::iae