diff options
Diffstat (limited to 'src/brussel.engine/Level.cpp')
-rw-r--r-- | src/brussel.engine/Level.cpp | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/src/brussel.engine/Level.cpp b/src/brussel.engine/Level.cpp new file mode 100644 index 0000000..076e5d5 --- /dev/null +++ b/src/brussel.engine/Level.cpp @@ -0,0 +1,228 @@ +#include "Level.hpp" + +#include "AppConfig.hpp" + +#include <PodVector.hpp> +#include <RapidJsonHelper.hpp> +#include <ScopeGuard.hpp> +#include <Utils.hpp> + +#include <imgui.h> +#include <rapidjson/document.h> +#include <rapidjson/filereadstream.h> +#include <rapidjson/filewritestream.h> +#include <rapidjson/prettywriter.h> +#include <cstdio> +#include <filesystem> + +using namespace std::literals; +namespace fs = std::filesystem; + +constexpr auto kParentToRootObject = std::numeric_limits<size_t>::max(); +constexpr auto kInvalidEntryId = std::numeric_limits<size_t>::max(); + +struct Level::InstanciationEntry { + // If set to std::numeric_limits<size_t>::max(), this object is parented to the "root" provided when instanciating + size_t parentId; + rapidjson::Document data; +}; + +Level::Level() = default; + +Level::~Level() = default; + +void Level::Instanciate(GameObject* relRoot) const { + auto objectsLut = std::make_unique<GameObject*[]>(mEntries.size()); + for (auto& entry : mEntries) { + GameObject* parent; + if (entry.parentId == kParentToRootObject) { + parent = relRoot; + } else { + parent = objectsLut[entry.parentId]; + } + + // TODO deser object + } +} + +void Level::ShowInstanciationEntries(IEditor& editor) { + for (auto& entry : mEntries) { + // TODO + } +} + +void LevelManager::DiscoverFilesDesignatedLocation() { + auto path = AppConfig::assetDirPath / "Levels"; + DiscoverFiles(path); +} + +void LevelManager::DiscoverFiles(const std::filesystem::path& dir) { + for (auto& item : fs::directory_iterator(dir)) { + auto& path = item.path(); + if (!item.is_regular_file()) { + continue; + } + if (path.extension() != ".json") { + continue; + } + + // Parse uid from filename, map key + Uid uid; + uid.ReadString(path.filename().string()); + + // Map value + LoadableObject obj; + obj.filePath = path; + + mObjByUid.try_emplace(uid, std::move(obj)); + } +} + +Level* LevelManager::FindLevel(const Uid& uid) const { + auto iter = mObjByUid.find(uid); + if (iter != mObjByUid.end()) { + return iter->second.level.Get(); + } else { + return nullptr; + } +} + +#define BRUSSEL_DEF_LEVEL_NAME "New Level" +#define BRUSSEL_DEF_LEVEL_DESC "No description." + +Level* LevelManager::LoadLevel(const Uid& uid) { + auto iter = mObjByUid.find(uid); + if (iter != mObjByUid.end()) { + auto& ldObj = iter->second; + if (ldObj.level != nullptr) { + auto file = Utils::OpenCstdioFile(ldObj.filePath, Utils::Read, false); + if (!file) { + fprintf(stderr, "Cannot open file level file %s that was discovered on game startup.", ldObj.filePath.string().c_str()); + return nullptr; + } + DEFER { fclose(file); }; + + char readerBuffer[65536]; + rapidjson::FileReadStream stream(file, readerBuffer, sizeof(readerBuffer)); + + rapidjson::Document root; + root.ParseStream(stream); + + Level* level; + ldObj.level.Attach(level = new Level()); + + level->mMan = this; + level->mUid = uid; + +#if defined(BRUSSEL_DEV_ENV) + BRUSSEL_JSON_GET_DEFAULT(root, "Name", std::string, ldObj.name, BRUSSEL_DEF_LEVEL_NAME); + BRUSSEL_JSON_GET_DEFAULT(root, "Description", std::string, ldObj.description, BRUSSEL_DEF_LEVEL_DESC) +#endif + + auto rvEntries = rapidjson::GetProperty(root, rapidjson::kArrayType, "DataEntries"sv); + if (!rvEntries) return nullptr; + for (auto iter = rvEntries->Begin(); iter != rvEntries->End(); ++iter) { + Level::InstanciationEntry entry; + + BRUSSEL_JSON_GET_DEFAULT(*iter, "ParentId", int, entry.parentId, kInvalidEntryId); + + auto rvDataEntry = rapidjson::GetProperty(*iter, "Data"sv); + if (!rvDataEntry) return nullptr; + entry.data.CopyFrom(*iter, entry.data.GetAllocator()); + + level->mEntries.push_back(std::move(entry)); + } + } + return ldObj.level.Get(); + } else { + return nullptr; + } +} + +void LevelManager::PrepareLevel(const Uid& uid) { + // TODO +} + +LevelManager::LoadableObject& LevelManager::AddLevel(const Uid& uid) { + auto&& [iter, inserted] = mObjByUid.try_emplace(uid); + auto& ldObj = iter->second; + ldObj.level->mUid = uid; +#if defined(BRUSSEL_DEV_ENV) + ldObj.name = BRUSSEL_DEF_LEVEL_NAME; + ldObj.description = BRUSSEL_DEF_LEVEL_DESC; +#endif + return ldObj; +} + +void LevelManager::SaveLevel(const Uid& uid) const { + auto iter = mObjByUid.find(uid); + if (iter == mObjByUid.end()) return; + auto& obj = iter->second; + + SaveLevelImpl(obj, obj.filePath); +} + +void LevelManager::SaveLevel(const Uid& uid, const std::filesystem::path& path) const { + auto iter = mObjByUid.find(uid); + if (iter == mObjByUid.end()) return; + auto& obj = iter->second; + + SaveLevelImpl(obj, path); +} + +void LevelManager::SaveLevelImpl(const LoadableObject& obj, const std::filesystem::path& path) const { + rapidjson::Document root; + + // TODO + + auto file = Utils::OpenCstdioFile(path, Utils::WriteTruncate); + if (!file) return; + DEFER { fclose(file); }; + + char writerBuffer[65536]; + rapidjson::FileWriteStream stream(file, writerBuffer, sizeof(writerBuffer)); + rapidjson::Writer<rapidjson::FileWriteStream> writer(stream); + root.Accept(writer); +} + +LevelWrapperObject::LevelWrapperObject(GameWorld* world) + : GameObject(KD_LevelWrapper, world) // +{ + mStopFreePropagation = true; +} + +LevelWrapperObject::~LevelWrapperObject() { + // Destruction/freeing of this object is handled by our parent + for (auto child : GetChildren()) { + FreeRecursive(child); + } +} + +void LevelWrapperObject::SetBoundLevel(Level* level) { + if (mLevel != level) { + mLevel.Attach(level); + + // Cleanup old children + // TODO needs to Resleep()? + auto children = RemoveAllChildren(); + for (auto child : children) { + FreeRecursive(child); + } + } + + level->Instanciate(this); + + PodVector<GameObject*> stack; + stack.push_back(this); + + while (!stack.empty()) { + auto obj = stack.back(); + stack.pop_back(); + + for (auto child : obj->GetChildren()) { + stack.push_back(child); + } + + obj->Awaken(); + } +} |