diff --git a/.gitmodules b/.gitmodules index 6dd82fa..9f871ba 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,3 +17,6 @@ path = docs/doxygen-awesome-css url = https://github.com/jothepro/doxygen-awesome-css.git +[submodule "extern/nlohmann_json"] + path = extern/nlohmann_json + url = https://github.com/nlohmann/json.git diff --git a/CMakeLists.txt b/CMakeLists.txt index d38fa27..b0764f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ add_subdirectory(extern/SDL_image EXCLUDE_FROM_ALL) add_subdirectory(extern/SDL_mixer EXCLUDE_FROM_ALL) add_subdirectory(extern/SDL_ttf EXCLUDE_FROM_ALL) add_subdirectory(extern/tmxlite/tmxlite EXCLUDE_FROM_ALL) +add_subdirectory(extern/nlohmann_json EXCLUDE_FROM_ALL) file(GLOB_RECURSE SOURCES ${ENGINE_SOURCE_DIR}/src/*.cpp) @@ -36,6 +37,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC # should be private when all SDL fu SDL3_image::SDL3_image-static SDL3_mixer::SDL3_mixer-static SDL3_ttf::SDL3_ttf-static + nlohmann_json::nlohmann_json tmxlite ) diff --git a/config.json b/config.json new file mode 100644 index 0000000..e4ee3bf --- /dev/null +++ b/config.json @@ -0,0 +1,7 @@ +{ + "fullscreen": false, + "title": "VGG (Very Good Game)", + "screen_height": 100, + "screen_width": 100, + "icon": "./engine/internalAssets/iconImage.bmp" +} \ No newline at end of file diff --git a/extern/nlohmann_json b/extern/nlohmann_json new file mode 160000 index 0000000..a006a7a --- /dev/null +++ b/extern/nlohmann_json @@ -0,0 +1 @@ +Subproject commit a006a7a48bb30a247f0344b788c62c2806edd90b diff --git a/include/ConfigLoader.h b/include/ConfigLoader.h new file mode 100644 index 0000000..6262f2b --- /dev/null +++ b/include/ConfigLoader.h @@ -0,0 +1,62 @@ +#pragma once + +#include + +using json = nlohmann::json; + +/*! + * \class ConfigLoader + * \brief Enables configuration of specific engine variables via a custom JSON file. + * + * The Config loader is responsible to handling customization for engine parameters like the + * window icon, window title, ... through json files. + * + * It includes a standard config file and the option to add a custom one by overwriting the setConfigFilePath() + * function within the implementation of the \ref Game class. Those files get merged, with a priorization on + * the parameters set within the custom config file. + * + * + * The currently available config parameters with their default values are: + * \include ../config.json + * + */ + +class ConfigLoader { + +public: + ConfigLoader(); + ~ConfigLoader(); + + /*! + * \brief Creates the final config for the engine + * + * Loads the default config and then creates the final config by either merging + * (if the custom config has been set) or by implementing the standard config (if no custom + * config was set). + * + * \private + */ + void init(); + /*! + * \brief Sets the customConfigPath variable + * + * \param path optional variable that should include the path to the custom config JSON file + * + * \private + */ + void setCustomConfig(const std::optional& path); + /*! + * \brief Gets final configuration + * \return `json` variable containing the final config + * \private + */ + json getFinalConfig(); + +private: + + std::optional customConfigPath; + json finalConfig; + + json loadConfigFromJSON(const std::string& path); + json mergeConfigs(json baseConfig, json customConfig); // + */ + virtual std::optional setConfigFilePath() {return std::nullopt;} + GameInternal* gameInternal; //!< \deprecated }; diff --git a/include/GameInternal.h b/include/GameInternal.h index 7cb3b1f..3850b43 100644 --- a/include/GameInternal.h +++ b/include/GameInternal.h @@ -11,6 +11,7 @@ #include "Vector2D.h" #include "Entity.h" #include "RenderManager.h" +#include "ConfigLoader.h" typedef std::function gamefunction; @@ -27,7 +28,7 @@ public: GameInternal(); ~GameInternal(); - SDL_AppResult init(const char* title, int xpos, int ypos, int width, int height, bool fullscreen); + SDL_AppResult init(); void handleEvents(); void update(Uint64 frameTime); @@ -48,6 +49,8 @@ public: RenderManager renderManager; Map* map; // game specific, might not be needed for all types of games + ConfigLoader* config; + std::vector& tiles; std::vector& players; std::vector& projectiles; diff --git a/internalAssets/iconImage.bmp b/internalAssets/iconImage.bmp new file mode 100644 index 0000000..6eddbef Binary files /dev/null and b/internalAssets/iconImage.bmp differ diff --git a/src/AssetManager.cpp b/src/AssetManager.cpp index ae8242a..75e78ec 100644 --- a/src/AssetManager.cpp +++ b/src/AssetManager.cpp @@ -14,6 +14,7 @@ #include "Vector2D.h" #include "PowerupComponent.h" #include +#include #include "Textures.h" @@ -76,8 +77,8 @@ Vector2D AssetManager::calculateSpawnPosition() { SDL_Rect spawnRect; spawnRect.h = spawnRect.w = 32; - spawnRect.x = rand() % (SCREEN_SIZE_WIDTH - spawnRect.w); - spawnRect.y = rand() % (SCREEN_SIZE_HEIGHT - spawnRect.h); + spawnRect.x = rand() % (VEGO_Game().config->getFinalConfig().at("screen_width").get() - spawnRect.w); + spawnRect.y = rand() % (VEGO_Game().config->getFinalConfig().at("screen_height").get() - spawnRect.h); conflict = false; for (auto cc : this->man->getGame()->collisionHandler->getColliders({ Entity::GroupLabel::MAPTILES })) { diff --git a/src/CollisionHandler.cpp b/src/CollisionHandler.cpp index 5821303..d153d0c 100644 --- a/src/CollisionHandler.cpp +++ b/src/CollisionHandler.cpp @@ -10,6 +10,7 @@ #include #include #include +#include IntersectionBitSet CollisionHandler::getIntersection(Entity* entityA, Entity* entityB, Vector2D posModA, Vector2D posModB) { @@ -66,20 +67,20 @@ IntersectionBitSet CollisionHandler::getIntersectionWithBounds(Entity* entity, V // all 4 directions and both sides to allow checking for fully out of bounds if (collider->x + posMod.x < 0 || - collider->x + posMod.x > SCREEN_SIZE_WIDTH) { + collider->x + posMod.x > VEGO_Game().config->getFinalConfig().at("screen_width")) { intersections.set((size_t) Direction::LEFT); } if (collider->x + collider->w + posMod.x < 0 || - collider->x + collider->w + posMod.x > SCREEN_SIZE_WIDTH) + collider->x + collider->w + posMod.x > VEGO_Game().config->getFinalConfig().at("screen_width")) intersections.set((size_t) Direction::RIGHT); if (collider->y + posMod.y < 0 || - collider->y + posMod.y > SCREEN_SIZE_HEIGHT) + collider->y + posMod.y > VEGO_Game().config->getFinalConfig().at("screen_height")) intersections.set((size_t) Direction::UP); if (collider->y + collider->h + posMod.y < 0 || - collider->y + collider->h + posMod.y > SCREEN_SIZE_HEIGHT) + collider->y + collider->h + posMod.y > VEGO_Game().config->getFinalConfig().at("screen_height")) intersections.set((size_t) Direction::DOWN); return intersections; diff --git a/src/ConfigLoader.cpp b/src/ConfigLoader.cpp new file mode 100644 index 0000000..9e9911c --- /dev/null +++ b/src/ConfigLoader.cpp @@ -0,0 +1,51 @@ +#include "ConfigLoader.h" + +#include + +ConfigLoader::ConfigLoader() {} + +ConfigLoader::~ConfigLoader() {} + +void ConfigLoader::init() { + //TODO: look into adaptive paths for better handling as this requires the implemented game + // to have ./engine in the root folder (very low prio) + const json baseConfig = loadConfigFromJSON("./engine/config.json"); + + if (!customConfigPath.has_value()) { + finalConfig = baseConfig; + return; + } + + finalConfig = mergeConfigs(baseConfig, loadConfigFromJSON(customConfigPath.value())); +} + +json ConfigLoader::loadConfigFromJSON(const std::string& path) { + std::ifstream config_file(path); + json config; + + if (!config_file.is_open()) { + throw std::runtime_error(std::string("Could not load config file at: " + path)); + } + + config_file >> config; + return config; +} + + +void ConfigLoader::setCustomConfig(const std::optional& path) { + customConfigPath = path; +} + +json ConfigLoader::mergeConfigs(json baseConfig, json customConfig) { + for (auto& entry : customConfig.items()) { + baseConfig[entry.key()] = entry.value(); + } + return baseConfig; +} + +json ConfigLoader::getFinalConfig() { + return finalConfig; +} + + + diff --git a/src/GameInternal.cpp b/src/GameInternal.cpp index 1e4eac3..77becb6 100644 --- a/src/GameInternal.cpp +++ b/src/GameInternal.cpp @@ -15,6 +15,8 @@ #include +#include "ConfigLoader.h" + GameInternal::GameInternal() : manager(this), renderManager(), @@ -27,15 +29,23 @@ GameInternal::GameInternal() : GameInternal::~GameInternal() = default; -SDL_AppResult GameInternal::init(const char* title, int xpos, int ypos, int width, int height, bool fullscreen) +SDL_AppResult GameInternal::init() { + config = new ConfigLoader(); + + this->gameInstance = GameFactory::instance().create(this); + config->setCustomConfig(this->gameInstance->setConfigFilePath()); + config->init(); + + json finalConfig = config->getFinalConfig(); + GameInternal::assets = new AssetManager(&manager); GameInternal::textureManager = new TextureManager(&manager); GameInternal::soundManager = new SoundManager(); GameInternal::collisionHandler = new CollisionHandler(manager); // why does this use a referrence, but AssetManager a pointer? int flags = 0; - if (fullscreen) + if (finalConfig.at("fullscreen")) { flags = SDL_WINDOW_FULLSCREEN; } @@ -52,7 +62,9 @@ SDL_AppResult GameInternal::init(const char* title, int xpos, int ypos, int widt return SDL_APP_FAILURE; } - window = SDL_CreateWindow(title, width, height, flags); + window = SDL_CreateWindow(finalConfig.at("title").get().c_str(), + finalConfig.at("screen_width"), finalConfig.at("screen_height"), flags); + if (!window) { std::cout << "ERROR: Window couldnt be created! " << SDL_GetError() << std::endl; @@ -62,7 +74,7 @@ SDL_AppResult GameInternal::init(const char* title, int xpos, int ypos, int widt // bad SDL_Surface* icon; - if((icon = SDL_LoadBMP("assets/iconImage.bmp"))) + if((icon = SDL_LoadBMP(finalConfig.at("icon").get().c_str()))) { SDL_SetWindowIcon(window, icon); } @@ -95,7 +107,6 @@ SDL_AppResult GameInternal::init(const char* title, int xpos, int ypos, int widt // loading music // assets->addMusic("background_music", "assets/sound/background_music.mp3"); - this->gameInstance = GameFactory::instance().create(this); this->gameInstance->init(); return SDL_APP_CONTINUE; diff --git a/src/_Init.cpp b/src/_Init.cpp index cb82136..d108b73 100644 --- a/src/_Init.cpp +++ b/src/_Init.cpp @@ -19,7 +19,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { *appstate = vego::game = new GameInternal(); - return vego::game->init("No_Name_Chicken_Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_SIZE_WIDTH, SCREEN_SIZE_HEIGHT, false); + return vego::game->init(); } SDL_AppResult SDL_AppIterate(void *appstate) {