diff --git a/CMakeLists.txt b/CMakeLists.txt index ca1c558..9fb832f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,7 @@ set(APP_SOURCES source/game/gameplay/room.cpp source/game/gameplay/scoreboard.cpp source/game/gameplay/tilemap_renderer.cpp + source/game/gameplay/zone_manager.cpp # Game - Scenes source/game/scenes/game.cpp diff --git a/config/assets.yaml b/config/assets.yaml index 035e3b2..1e4b0b0 100644 --- a/config/assets.yaml +++ b/config/assets.yaml @@ -63,6 +63,11 @@ assets: DATA: - ${PREFIX}/data/console/commands.yaml + # ZONES + zones: + DATA: + - ${PREFIX}/data/zones/zones.yaml + # ROOMS rooms: ROOM: diff --git a/data/tilesets/cave.gif b/data/tilesets/cave.gif new file mode 100644 index 0000000..f15ee34 Binary files /dev/null and b/data/tilesets/cave.gif differ diff --git a/data/tilesets/neighborhood.gif b/data/tilesets/neighborhood.gif new file mode 100644 index 0000000..24e1708 Binary files /dev/null and b/data/tilesets/neighborhood.gif differ diff --git a/data/zones/zones.yaml b/data/zones/zones.yaml new file mode 100644 index 0000000..f76d28e --- /dev/null +++ b/data/zones/zones.yaml @@ -0,0 +1,13 @@ +# Zonas del juego +# Cada zona define un tileset y una pista de música compartidos por todas las +# habitaciones que pertenecen a ella. Las habitaciones individuales pueden +# sobreescribir tileSetFile y/o music en su propio yaml. + +zones: + - name: neighborhood + tileSetFile: neighborhood.gif + music: 574070_KUVO_Farewell_to_school.ogg + + - name: cave + tileSetFile: cave.gif + music: 574071_EA_DTV.ogg diff --git a/source/game/gameplay/zone.hpp b/source/game/gameplay/zone.hpp new file mode 100644 index 0000000..c278f41 --- /dev/null +++ b/source/game/gameplay/zone.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include // Para string + +/** + * @brief Datos de una zona del juego + * + * Una zona agrupa un conjunto de habitaciones que comparten tileset y música. + * Las habitaciones pueden hacer override individual de tile_set_file y/o music + * en su propio yaml; los valores aquí son la fuente por defecto. + * + * Las zonas se cargan desde data/zones/zones.yaml por el ZoneManager. + */ +namespace Zone { + + struct Data { + std::string name; // Nombre único de la zona (ej. "neighborhood", "cave") + std::string tile_set_file; // Fichero de tileset por defecto (ej. "neighborhood.gif") + std::string music; // Pista de música por defecto (ej. "574070_KUVO_Farewell_to_school.ogg") + }; + +} // namespace Zone diff --git a/source/game/gameplay/zone_manager.cpp b/source/game/gameplay/zone_manager.cpp new file mode 100644 index 0000000..926297b --- /dev/null +++ b/source/game/gameplay/zone_manager.cpp @@ -0,0 +1,98 @@ +#include "game/gameplay/zone_manager.hpp" + +#include // Para exception +#include // Para cerr, cout +#include // Para string + +#include "core/resources/resource_helper.hpp" // Para Resource::Helper::loadFile +#include "external/fkyaml_node.hpp" // Para fkyaml::node + +// [SINGLETON] +ZoneManager* ZoneManager::zone_manager = nullptr; + +// [SINGLETON] Inicialización: crea la instancia y carga el yaml de zonas +void ZoneManager::init() { + if (ZoneManager::zone_manager != nullptr) { return; } + ZoneManager::zone_manager = new ZoneManager(); + ZoneManager::zone_manager->loadFromFile("data/zones/zones.yaml"); +} + +// [SINGLETON] +void ZoneManager::destroy() { + delete ZoneManager::zone_manager; + ZoneManager::zone_manager = nullptr; +} + +// [SINGLETON] +auto ZoneManager::get() -> ZoneManager* { + return ZoneManager::zone_manager; +} + +// Carga zones.yaml usando Resource::Helper (soporta pack + filesystem) +void ZoneManager::loadFromFile(const std::string& file_path) { + auto file_data = Resource::Helper::loadFile(file_path); + if (file_data.empty()) { + std::cerr << "ZoneManager: Unable to load " << file_path << "\n"; + return; + } + + try { + const std::string YAML_CONTENT(file_data.begin(), file_data.end()); + auto yaml = fkyaml::node::deserialize(YAML_CONTENT); + + if (!yaml.contains("zones") || yaml["zones"].is_null()) { + std::cerr << "ZoneManager: " << file_path << " has no 'zones' section\n"; + return; + } + + for (const auto& zone_node : yaml["zones"]) { + Zone::Data zone; + + if (zone_node.contains("name")) { + zone.name = zone_node["name"].get_value(); + } + if (zone_node.contains("tileSetFile")) { + zone.tile_set_file = zone_node["tileSetFile"].get_value(); + } + if (zone_node.contains("music")) { + zone.music = zone_node["music"].get_value(); + } + + if (zone.name.empty()) { + std::cerr << "ZoneManager: skipping zone without name\n"; + continue; + } + + zones_.push_back(zone); + } + + std::cout << "ZoneManager: loaded " << zones_.size() << " zones from " << file_path << "\n"; + } catch (const fkyaml::exception& e) { + std::cerr << "ZoneManager: YAML parsing error in " << file_path << ": " << e.what() << "\n"; + } catch (const std::exception& e) { + std::cerr << "ZoneManager: Error loading " << file_path << ": " << e.what() << "\n"; + } +} + +auto ZoneManager::getZone(const std::string& name) const -> const Zone::Data* { + for (const auto& zone : zones_) { + if (zone.name == name) { + return &zone; + } + } + return nullptr; +} + +auto ZoneManager::getDefaultZone() const -> const Zone::Data* { + if (zones_.empty()) { return nullptr; } + return &zones_.front(); +} + +auto ZoneManager::getZoneNames() const -> std::vector { + std::vector names; + names.reserve(zones_.size()); + for (const auto& zone : zones_) { + names.push_back(zone.name); + } + return names; +} diff --git a/source/game/gameplay/zone_manager.hpp b/source/game/gameplay/zone_manager.hpp new file mode 100644 index 0000000..b34f90c --- /dev/null +++ b/source/game/gameplay/zone_manager.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include // Para string +#include // Para vector + +#include "game/gameplay/zone.hpp" // Para Zone::Data + +/** + * @brief Singleton que carga y gestiona el catálogo de zonas + * + * Carga data/zones/zones.yaml en init() y permite consultar zonas por nombre. + * El loader usa Resource::Helper::loadFile, que soporta tanto el resource pack + * como el filesystem (modo desarrollo). Por eso ZoneManager no depende del + * Resource::Cache y puede inicializarse en cualquier momento antes de + * RoomLoader::loadYAML. + */ +class ZoneManager { + public: + // Gestión singleton + static void init(); // Carga zones.yaml y crea el singleton + static void destroy(); // Destruye el singleton + static auto get() -> ZoneManager*; // Acceso al singleton + + /** + * @brief Devuelve la zona con el nombre indicado, o nullptr si no existe + */ + [[nodiscard]] auto getZone(const std::string& name) const -> const Zone::Data*; + + /** + * @brief Devuelve la zona por defecto (primera del fichero), o nullptr si no hay zonas + * + * Se usa como fallback cuando una room.yaml no especifica zone o especifica + * una zona desconocida. + */ + [[nodiscard]] auto getDefaultZone() const -> const Zone::Data*; + + /** + * @brief Devuelve la lista de nombres de todas las zonas (para validación en editor) + */ + [[nodiscard]] auto getZoneNames() const -> std::vector; + + private: + // Constantes singleton + static ZoneManager* zone_manager; // [SINGLETON] Objeto privado + + // Constructor y destructor privados [SINGLETON] + ZoneManager() = default; + ~ZoneManager() = default; + + // Carga el yaml y rellena zones_ + void loadFromFile(const std::string& file_path); + + // Variables miembro + std::vector zones_; // Catálogo de zonas +}; diff --git a/source/game/scenes/game.cpp b/source/game/scenes/game.cpp index 6503504..c75c7e0 100644 --- a/source/game/scenes/game.cpp +++ b/source/game/scenes/game.cpp @@ -25,6 +25,7 @@ #include "game/gameplay/item_tracker.hpp" // Para ItemTracker #include "game/gameplay/key_tracker.hpp" // Para KeyTracker #include "game/gameplay/room.hpp" // Para Room, RoomData +#include "game/gameplay/zone_manager.hpp" // Para ZoneManager #include "game/gameplay/room_tracker.hpp" // Para RoomTracker #include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data, Scoreboard #include "game/options.hpp" // Para Options, options, Cheat, SectionState @@ -68,6 +69,7 @@ Game::Game(Mode mode) #endif // Crea objetos e inicializa variables + ZoneManager::init(); ItemTracker::init(); KeyTracker::init(); DoorTracker::init(); @@ -173,6 +175,7 @@ Game::~Game() { KeyTracker::destroy(); DoorTracker::destroy(); Inventory::destroy(); + ZoneManager::destroy(); if (Console::get() != nullptr) { Console::get()->on_toggle = nullptr; }