aboutsummaryrefslogtreecommitdiff
path: root/src/brussel.engine/Level.cpp
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2023-10-19 22:50:07 -0700
committerrtk0c <[email protected]>2023-10-19 22:50:07 -0700
commit2c92e07f337e42cf58970443f9de678f85a9b2a4 (patch)
tree075d5407e1e12a9d35cbee6e4c20ad34e0765c42 /src/brussel.engine/Level.cpp
parent615809c036f604bce4582cea8ad49c64693f4f45 (diff)
The great renaming: switch to "module style"
Diffstat (limited to 'src/brussel.engine/Level.cpp')
-rw-r--r--src/brussel.engine/Level.cpp228
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();
+ }
+}