#include "Level.hpp" #include "AppConfig.hpp" #include "PodVector.hpp" #include "RapidJsonHelper.hpp" #include "ScopeGuard.hpp" #include "Utils.hpp" #include #include #include #include #include #include using namespace std::literals; namespace fs = std::filesystem; constexpr auto kInvalidEntryId = std::numeric_limits::max(); struct Level::InstanciationEntry { // If set to std::numeric_limits::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 { } 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; #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, LoadableObject{}); auto& ldObj = iter->second; #if defined(BRUSSEL_DEV_ENV) ldObj.name = BRUSSEL_DEF_LEVEL_NAME; ldObj.description = BRUSSEl_DEF_LEVEL_DESC; #endif return ldObj; } 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 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(); } }