157 lines
5.6 KiB
C++
157 lines
5.6 KiB
C++
// 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 <https://www.gnu.org/licenses/>.
|
|
|
|
#include <IAEngine/Engine.hpp>
|
|
#include <IAEngine/SceneManager.hpp>
|
|
|
|
#include <IAEngine/pugixml/pugixml.hpp>
|
|
|
|
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<const char *>(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
|
|
{
|
|
SceneManager::SceneManager(IN std::function<RefPtr<Node2D>(IN CONST String &, IN CONST Vector<String>&)> getCustomNode,
|
|
IN std::function<Handle(IN ResourceType type, IN CONST String &, IN INT64)> getResource):
|
|
m_customNodeGetter(getCustomNode), m_resourceGetter(getResource)
|
|
{
|
|
}
|
|
|
|
SceneManager::~SceneManager()
|
|
{
|
|
for(const auto& t: m_scenes)
|
|
delete t->Value;
|
|
}
|
|
|
|
VOID SceneManager::SwitchTo(IN CONST String &name)
|
|
{
|
|
Engine::ChangeActiveScene(m_scenes[name]);
|
|
}
|
|
|
|
Scene *SceneManager::GetScene(IN CONST String &name)
|
|
{
|
|
return m_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<String, Handle> 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()] = m_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()] =
|
|
m_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()};
|
|
}
|
|
|
|
// 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<TextureNode>(Engine::GetUniqueResourceName());
|
|
node->GetTextureComponent()->SetTexture(resources[t.attribute("texture").as_string()]);
|
|
scene->AddNode(node);
|
|
n = node.get();
|
|
}
|
|
else
|
|
{
|
|
const auto node = m_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");
|
|
{
|
|
scene->UIMarkupStyles() = getInnerXML(uiRoot.child("CSS")).c_str();
|
|
auto html = String(getInnerXML(uiRoot.child("HTML")).c_str());
|
|
html =
|
|
Utils::RegexReplaceGroups(html, "<img(.*?)src=\"(.*?)\"", [&](IN INT32 index, IN CONST String &match) {
|
|
if(index == 1)
|
|
return BuildString("$H$", resources[match]);
|
|
return match;
|
|
});
|
|
scene->UIMarkup() = html;
|
|
}
|
|
|
|
m_scenes[name] = scene;
|
|
}
|
|
} // namespace ia::iae
|