diff --git a/CMakeLists.txt b/CMakeLists.txt index b4ad683..a79fc9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ set(APP_SOURCES source/game/gameplay/item_manager.cpp source/game/gameplay/item_tracker.cpp source/game/gameplay/room.cpp + source/game/gameplay/room_loader.cpp source/game/gameplay/room_tracker.cpp source/game/gameplay/scoreboard.cpp source/game/gameplay/stats.cpp diff --git a/source/game/gameplay/room.cpp b/source/game/gameplay/room.cpp index 67a1d14..aa5dbd6 100644 --- a/source/game/gameplay/room.cpp +++ b/source/game/gameplay/room.cpp @@ -18,6 +18,7 @@ #include "game/gameplay/enemy_manager.hpp" // Para EnemyManager #include "game/gameplay/item_manager.hpp" // Para ItemManager #include "game/gameplay/item_tracker.hpp" // Para ItemTracker +#include "game/gameplay/room_loader.hpp" // Para RoomLoader #include "game/gameplay/tilemap_renderer.hpp" // Para TilemapRenderer #include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data #include "game/options.hpp" // Para Options, OptionsStats, options @@ -265,291 +266,12 @@ auto Room::checkRightSlopes(SDL_FPoint& p) -> bool { } -// Asigna variables a una estructura RoomData -auto Room::setRoom(Data& room, const std::string& key, const std::string& value) -> bool { - // Indicador de éxito en la asignación - bool success = true; - - try { - if (key == "tileMapFile") { - room.tile_map_file = value; - } else if (key == "name") { - room.name = value; - } else if (key == "bgColor") { - room.bg_color = value; - } else if (key == "border") { - room.border_color = value; - } else if (key == "itemColor1") { - room.item_color1 = value; - } else if (key == "itemColor2") { - room.item_color2 = value; - } else if (key == "tileSetFile") { - room.tile_set_file = value; - } else if (key == "roomUp") { - room.upper_room = value; - } else if (key == "roomDown") { - room.lower_room = value; - } else if (key == "roomLeft") { - room.left_room = value; - } else if (key == "roomRight") { - room.right_room = value; - } else if (key == "autoSurface") { - room.conveyor_belt_direction = (value == "right") ? 1 : -1; - } else if (key.empty() || key.substr(0, 1) == "#") { - // No se realiza ninguna acción para estas claves - } else { - success = false; - } - } catch (const std::exception& e) { - std::cerr << "Error al asignar la clave " << key << " con valor " << value << ": " << e.what() << '\n'; - success = false; - } - - return success; -} - -// Asigna variables a una estructura EnemyData -auto Room::setEnemy(Enemy::Data& enemy, const std::string& key, const std::string& value) -> bool { - // Indicador de éxito en la asignación - bool success = true; - - try { - if (key == "animation") { - enemy.animation_path = value; - } else if (key == "x") { - enemy.x = std::stof(value) * TILE_SIZE; - } else if (key == "y") { - enemy.y = std::stof(value) * TILE_SIZE; - } else if (key == "vx") { - enemy.vx = std::stof(value); - } else if (key == "vy") { - enemy.vy = std::stof(value); - } else if (key == "x1") { - enemy.x1 = std::stoi(value) * TILE_SIZE; - } else if (key == "x2") { - enemy.x2 = std::stoi(value) * TILE_SIZE; - } else if (key == "y1") { - enemy.y1 = std::stoi(value) * TILE_SIZE; - } else if (key == "y2") { - enemy.y2 = std::stoi(value) * TILE_SIZE; - } else if (key == "flip") { - enemy.flip = stringToBool(value); - } else if (key == "mirror") { - enemy.mirror = stringToBool(value); - } else if (key == "color") { - enemy.color = value; - } else if (key == "frame") { - enemy.frame = std::stoi(value); - } else if (key == "[/enemy]" || key == "tileSetFile" || key.substr(0, 1) == "#") { - // No se realiza ninguna acción para estas claves - } else { - success = false; - } - } catch (const std::exception& e) { - std::cerr << "Error al asignar la clave " << key << " con valor " << value << ": " << e.what() << '\n'; - success = false; - } - - return success; -} - -// Asigna variables a una estructura ItemData -auto Room::setItem(Item::Data& item, const std::string& key, const std::string& value) -> bool { - // Indicador de éxito en la asignación - bool success = true; - - try { - if (key == "tileSetFile") { - item.tile_set_file = value; - } else if (key == "counter") { - item.counter = std::stoi(value); - } else if (key == "x") { - item.x = std::stof(value) * TILE_SIZE; - } else if (key == "y") { - item.y = std::stof(value) * TILE_SIZE; - } else if (key == "tile") { - item.tile = std::stof(value); - } else if (key == "[/item]") { - // No se realiza ninguna acción para esta clave - } else { - success = false; - } - } catch (const std::exception& e) { - std::cerr << "Error al asignar la clave " << key << " con valor " << value << ": " << e.what() << '\n'; - success = false; - } - - return success; -} - -// Carga las variables y texturas desde un fichero de mapa de tiles -auto Room::loadRoomTileFile(const std::string& file_path, bool verbose) -> std::vector { - std::vector tile_map_file; - const std::string FILENAME = file_path.substr(file_path.find_last_of("\\/") + 1); - - // Load file using ResourceHelper (supports both filesystem and pack) - auto file_data = Resource::Helper::loadFile(file_path); - - if (!file_data.empty()) { - // Convert bytes to string and parse - std::string content(file_data.begin(), file_data.end()); - std::istringstream stream(content); - std::string line; - - // Procesa el fichero linea a linea - while (std::getline(stream, line)) { // Lee el fichero linea a linea - if (line.find("data encoding") != std::string::npos) { - // Lee la primera linea - std::getline(stream, line); - // Trim line to handle Windows line endings - line.erase(0, line.find_first_not_of(" \t\r\n")); - line.erase(line.find_last_not_of(" \t\r\n") + 1); - while (line != "") { // Procesa lineas mientras haya - std::stringstream ss(line); - std::string tmp; - while (getline(ss, tmp, ',')) { - // Trim whitespace (including \r, \n, spaces, tabs) - tmp.erase(0, tmp.find_first_not_of(" \t\r\n")); - tmp.erase(tmp.find_last_not_of(" \t\r\n") + 1); - - // Skip empty strings (from trailing commas) - if (!tmp.empty()) { - tile_map_file.push_back(std::stoi(tmp) - 1); - } - } - - // Lee la siguiente linea - std::getline(stream, line); - // Trim line to handle Windows line endings - line.erase(0, line.find_first_not_of(" \t\r\n")); - line.erase(line.find_last_not_of(" \t\r\n") + 1); - } - } - } - - if (verbose) { - std::cout << "TileMap loaded: " << FILENAME.c_str() << '\n'; - } - } else { // El fichero no se puede abrir - if (verbose) { - std::cout << "Warning: Unable to open " << FILENAME.c_str() << " file" << '\n'; - } - } - - return tile_map_file; -} - -// Carga las variables desde un fichero de mapa +// Carga las variables desde un fichero de mapa (delegado a RoomLoader) auto Room::loadRoomFile(const std::string& file_path, bool verbose) -> Data { - Data room; - room.item_color1 = "yellow"; - room.item_color2 = "magenta"; - room.conveyor_belt_direction = 1; - - const std::string FILE_NAME = file_path.substr(file_path.find_last_of("\\/") + 1); - room.number = FILE_NAME.substr(0, FILE_NAME.find_last_of('.')); - - // Load file using ResourceHelper (supports both filesystem and pack) - auto file_data = Resource::Helper::loadFile(file_path); - - if (!file_data.empty()) { - // Convert bytes to string and parse - std::string content(file_data.begin(), file_data.end()); - std::istringstream stream(content); - std::string line; - - // Procesa el fichero linea a linea - while (std::getline(stream, line)) { - // Remove Windows line ending if present - if (!line.empty() && line.back() == '\r') { - line.pop_back(); - } - // Si la linea contiene el texto [enemy] se realiza el proceso de carga de un enemigo - if (line == "[enemy]") { - room.enemies.push_back(loadEnemyFromFile(stream, FILE_NAME, verbose)); - } - // Si la linea contiene el texto [item] se realiza el proceso de carga de un item - else if (line == "[item]") { - room.items.push_back(loadItemFromFile(stream, FILE_NAME, verbose)); - } - // En caso contrario se parsea el fichero para buscar las variables y los valores - else { - auto [key, value] = parseKeyValue(line); - if (!setRoom(room, key, value)) { - logUnknownParameter(FILE_NAME, key, verbose); - } - } - } - - if (verbose) { - std::cout << "Room loaded: " << FILE_NAME.c_str() << '\n'; - } - } else { // El fichero no se puede abrir - std::cout << "Warning: Unable to open " << FILE_NAME.c_str() << " file" << '\n'; - } - - return room; + return RoomLoader::loadRoomFile(file_path, verbose); } -// Parsea una línea en key y value separados por '=' -auto Room::parseKeyValue(const std::string& line) -> std::pair { - int pos = line.find('='); - std::string key = line.substr(0, pos); - std::string value = line.substr(pos + 1, line.length()); - return {key, value}; +// Carga las variables y texturas desde un fichero de mapa de tiles (delegado a RoomLoader) +auto Room::loadRoomTileFile(const std::string& file_path, bool verbose) -> std::vector { + return RoomLoader::loadRoomTileFile(file_path, verbose); } - -// Muestra un warning de parámetro desconocido -void Room::logUnknownParameter(const std::string& file_name, const std::string& key, bool verbose) { - if (verbose) { - std::cout << "Warning: file " << file_name.c_str() << "\n, unknown parameter \"" << key.c_str() << "\"" << '\n'; - } -} - -// Carga un bloque [enemy]...[/enemy] desde un archivo -auto Room::loadEnemyFromFile(std::istream& file, const std::string& file_name, bool verbose) -> Enemy::Data { - Enemy::Data enemy; - enemy.flip = false; - enemy.mirror = false; - enemy.frame = -1; - - std::string line; - do { - std::getline(file, line); - // Remove Windows line ending if present - if (!line.empty() && line.back() == '\r') { - line.pop_back(); - } - auto [key, value] = parseKeyValue(line); - - if (!setEnemy(enemy, key, value)) { - logUnknownParameter(file_name, key, verbose); - } - } while (line != "[/enemy]"); - - return enemy; -} - -// Carga un bloque [item]...[/item] desde un archivo -auto Room::loadItemFromFile(std::istream& file, const std::string& file_name, bool verbose) -> Item::Data { - Item::Data item; - item.counter = 0; - item.color1 = stringToColor("yellow"); - item.color2 = stringToColor("magenta"); - - std::string line; - do { - std::getline(file, line); - // Remove Windows line ending if present - if (!line.empty() && line.back() == '\r') { - line.pop_back(); - } - auto [key, value] = parseKeyValue(line); - - if (!setItem(item, key, value)) { - logUnknownParameter(file_name, key, verbose); - } - } while (line != "[/item]"); - - return item; -} \ No newline at end of file diff --git a/source/game/gameplay/room.hpp b/source/game/gameplay/room.hpp index 4fee95a..15d621c 100644 --- a/source/game/gameplay/room.hpp +++ b/source/game/gameplay/room.hpp @@ -88,6 +88,8 @@ class Room { auto checkRightSlopes(SDL_FPoint& p) -> bool; // Comprueba las colisiones void setPaused(bool value); // Pone el mapa en modo pausa [[nodiscard]] auto getConveyorBeltDirection() const -> int { return conveyor_belt_direction_; } // Obten la direccion de las superficies automaticas + + // Métodos de carga de archivos (delegados a RoomLoader) static auto loadRoomFile(const std::string& file_path, bool verbose = false) -> Data; // Carga las variables desde un fichero de mapa static auto loadRoomTileFile(const std::string& file_path, bool verbose = false) -> std::vector; // Carga las variables y texturas desde un fichero de mapa de tiles @@ -126,11 +128,4 @@ class Room { // --- Funciones --- void initializeRoom(const Data& room); // Inicializa los valores void openTheJail(); // Abre la jail para poder entrar - static auto setRoom(Data& room, const std::string& key, const std::string& value) -> bool; // Asigna variables a una estructura RoomData - static auto setEnemy(Enemy::Data& enemy, const std::string& key, const std::string& value) -> bool; // Asigna variables a una estructura EnemyData - static auto setItem(Item::Data& item, const std::string& key, const std::string& value) -> bool; // Asigna variables a una estructura ItemData - static auto parseKeyValue(const std::string& line) -> std::pair; - static void logUnknownParameter(const std::string& file_name, const std::string& key, bool verbose); - static auto loadEnemyFromFile(std::istream& file, const std::string& file_name, bool verbose) -> Enemy::Data; - static auto loadItemFromFile(std::istream& file, const std::string& file_name, bool verbose) -> Item::Data; }; \ No newline at end of file diff --git a/source/game/gameplay/room_loader.cpp b/source/game/gameplay/room_loader.cpp new file mode 100644 index 0000000..552a104 --- /dev/null +++ b/source/game/gameplay/room_loader.cpp @@ -0,0 +1,298 @@ +#include "room_loader.hpp" + +#include // Para exception +#include // Para cout, cerr +#include // Para istringstream, stringstream + +#include "core/resources/resource_helper.hpp" // Para Resource::Helper +#include "utils/defines.hpp" // Para TILE_SIZE +#include "utils/utils.hpp" // Para stringToBool, stringToColor + +// Asigna variables a una estructura RoomData +auto RoomLoader::setRoom(Room::Data& room, const std::string& key, const std::string& value) -> bool { + // Indicador de éxito en la asignación + bool success = true; + + try { + if (key == "tileMapFile") { + room.tile_map_file = value; + } else if (key == "name") { + room.name = value; + } else if (key == "bgColor") { + room.bg_color = value; + } else if (key == "border") { + room.border_color = value; + } else if (key == "itemColor1") { + room.item_color1 = value; + } else if (key == "itemColor2") { + room.item_color2 = value; + } else if (key == "tileSetFile") { + room.tile_set_file = value; + } else if (key == "roomUp") { + room.upper_room = value; + } else if (key == "roomDown") { + room.lower_room = value; + } else if (key == "roomLeft") { + room.left_room = value; + } else if (key == "roomRight") { + room.right_room = value; + } else if (key == "autoSurface") { + room.conveyor_belt_direction = (value == "right") ? 1 : -1; + } else if (key.empty() || key.substr(0, 1) == "#") { + // No se realiza ninguna acción para estas claves + } else { + success = false; + } + } catch (const std::exception& e) { + std::cerr << "Error al asignar la clave " << key << " con valor " << value << ": " << e.what() << '\n'; + success = false; + } + + return success; +} + +// Asigna variables a una estructura EnemyData +auto RoomLoader::setEnemy(Enemy::Data& enemy, const std::string& key, const std::string& value) -> bool { + // Indicador de éxito en la asignación + bool success = true; + + try { + if (key == "animation") { + enemy.animation_path = value; + } else if (key == "x") { + enemy.x = std::stof(value) * TILE_SIZE; + } else if (key == "y") { + enemy.y = std::stof(value) * TILE_SIZE; + } else if (key == "vx") { + enemy.vx = std::stof(value); + } else if (key == "vy") { + enemy.vy = std::stof(value); + } else if (key == "x1") { + enemy.x1 = std::stoi(value) * TILE_SIZE; + } else if (key == "x2") { + enemy.x2 = std::stoi(value) * TILE_SIZE; + } else if (key == "y1") { + enemy.y1 = std::stoi(value) * TILE_SIZE; + } else if (key == "y2") { + enemy.y2 = std::stoi(value) * TILE_SIZE; + } else if (key == "flip") { + enemy.flip = stringToBool(value); + } else if (key == "mirror") { + enemy.mirror = stringToBool(value); + } else if (key == "color") { + enemy.color = value; + } else if (key == "frame") { + enemy.frame = std::stoi(value); + } else if (key == "[/enemy]" || key == "tileSetFile" || key.substr(0, 1) == "#") { + // No se realiza ninguna acción para estas claves + } else { + success = false; + } + } catch (const std::exception& e) { + std::cerr << "Error al asignar la clave " << key << " con valor " << value << ": " << e.what() << '\n'; + success = false; + } + + return success; +} + +// Asigna variables a una estructura ItemData +auto RoomLoader::setItem(Item::Data& item, const std::string& key, const std::string& value) -> bool { + // Indicador de éxito en la asignación + bool success = true; + + try { + if (key == "tileSetFile") { + item.tile_set_file = value; + } else if (key == "counter") { + item.counter = std::stoi(value); + } else if (key == "x") { + item.x = std::stof(value) * TILE_SIZE; + } else if (key == "y") { + item.y = std::stof(value) * TILE_SIZE; + } else if (key == "tile") { + item.tile = std::stof(value); + } else if (key == "[/item]") { + // No se realiza ninguna acción para esta clave + } else { + success = false; + } + } catch (const std::exception& e) { + std::cerr << "Error al asignar la clave " << key << " con valor " << value << ": " << e.what() << '\n'; + success = false; + } + + return success; +} + +// Carga las variables y texturas desde un fichero de mapa de tiles +auto RoomLoader::loadRoomTileFile(const std::string& file_path, bool verbose) -> std::vector { + std::vector tile_map_file; + const std::string FILENAME = file_path.substr(file_path.find_last_of("\\/") + 1); + + // Load file using ResourceHelper (supports both filesystem and pack) + auto file_data = Resource::Helper::loadFile(file_path); + + if (!file_data.empty()) { + // Convert bytes to string and parse + std::string content(file_data.begin(), file_data.end()); + std::istringstream stream(content); + std::string line; + + // Procesa el fichero linea a linea + while (std::getline(stream, line)) { // Lee el fichero linea a linea + if (line.find("data encoding") != std::string::npos) { + // Lee la primera linea + std::getline(stream, line); + // Trim line to handle Windows line endings + line.erase(0, line.find_first_not_of(" \t\r\n")); + line.erase(line.find_last_not_of(" \t\r\n") + 1); + while (line != "") { // Procesa lineas mientras haya + std::stringstream ss(line); + std::string tmp; + while (getline(ss, tmp, ',')) { + // Trim whitespace (including \r, \n, spaces, tabs) + tmp.erase(0, tmp.find_first_not_of(" \t\r\n")); + tmp.erase(tmp.find_last_not_of(" \t\r\n") + 1); + + // Skip empty strings (from trailing commas) + if (!tmp.empty()) { + tile_map_file.push_back(std::stoi(tmp) - 1); + } + } + + // Lee la siguiente linea + std::getline(stream, line); + // Trim line to handle Windows line endings + line.erase(0, line.find_first_not_of(" \t\r\n")); + line.erase(line.find_last_not_of(" \t\r\n") + 1); + } + } + } + + if (verbose) { + std::cout << "TileMap loaded: " << FILENAME.c_str() << '\n'; + } + } else { // El fichero no se puede abrir + if (verbose) { + std::cout << "Warning: Unable to open " << FILENAME.c_str() << " file" << '\n'; + } + } + + return tile_map_file; +} + +// Carga las variables desde un fichero de mapa +auto RoomLoader::loadRoomFile(const std::string& file_path, bool verbose) -> Room::Data { + Room::Data room; + room.item_color1 = "yellow"; + room.item_color2 = "magenta"; + room.conveyor_belt_direction = 1; + + const std::string FILE_NAME = file_path.substr(file_path.find_last_of("\\/") + 1); + room.number = FILE_NAME.substr(0, FILE_NAME.find_last_of('.')); + + // Load file using ResourceHelper (supports both filesystem and pack) + auto file_data = Resource::Helper::loadFile(file_path); + + if (!file_data.empty()) { + // Convert bytes to string and parse + std::string content(file_data.begin(), file_data.end()); + std::istringstream stream(content); + std::string line; + + // Procesa el fichero linea a linea + while (std::getline(stream, line)) { + // Remove Windows line ending if present + if (!line.empty() && line.back() == '\r') { + line.pop_back(); + } + // Si la linea contiene el texto [enemy] se realiza el proceso de carga de un enemigo + if (line == "[enemy]") { + room.enemies.push_back(loadEnemyFromFile(stream, FILE_NAME, verbose)); + } + // Si la linea contiene el texto [item] se realiza el proceso de carga de un item + else if (line == "[item]") { + room.items.push_back(loadItemFromFile(stream, FILE_NAME, verbose)); + } + // En caso contrario se parsea el fichero para buscar las variables y los valores + else { + auto [key, value] = parseKeyValue(line); + if (!setRoom(room, key, value)) { + logUnknownParameter(FILE_NAME, key, verbose); + } + } + } + + if (verbose) { + std::cout << "Room loaded: " << FILE_NAME.c_str() << '\n'; + } + } else { // El fichero no se puede abrir + std::cout << "Warning: Unable to open " << FILE_NAME.c_str() << " file" << '\n'; + } + + return room; +} + +// Parsea una línea en key y value separados por '=' +auto RoomLoader::parseKeyValue(const std::string& line) -> std::pair { + int pos = line.find('='); + std::string key = line.substr(0, pos); + std::string value = line.substr(pos + 1, line.length()); + return {key, value}; +} + +// Muestra un warning de parámetro desconocido +void RoomLoader::logUnknownParameter(const std::string& file_name, const std::string& key, bool verbose) { + if (verbose) { + std::cout << "Warning: file " << file_name.c_str() << "\n, unknown parameter \"" << key.c_str() << "\"" << '\n'; + } +} + +// Carga un bloque [enemy]...[/enemy] desde un archivo +auto RoomLoader::loadEnemyFromFile(std::istream& file, const std::string& file_name, bool verbose) -> Enemy::Data { + Enemy::Data enemy; + enemy.flip = false; + enemy.mirror = false; + enemy.frame = -1; + + std::string line; + do { + std::getline(file, line); + // Remove Windows line ending if present + if (!line.empty() && line.back() == '\r') { + line.pop_back(); + } + auto [key, value] = parseKeyValue(line); + + if (!setEnemy(enemy, key, value)) { + logUnknownParameter(file_name, key, verbose); + } + } while (line != "[/enemy]"); + + return enemy; +} + +// Carga un bloque [item]...[/item] desde un archivo +auto RoomLoader::loadItemFromFile(std::istream& file, const std::string& file_name, bool verbose) -> Item::Data { + Item::Data item; + item.counter = 0; + item.color1 = stringToColor("yellow"); + item.color2 = stringToColor("magenta"); + + std::string line; + do { + std::getline(file, line); + // Remove Windows line ending if present + if (!line.empty() && line.back() == '\r') { + line.pop_back(); + } + auto [key, value] = parseKeyValue(line); + + if (!setItem(item, key, value)) { + logUnknownParameter(file_name, key, verbose); + } + } while (line != "[/item]"); + + return item; +} diff --git a/source/game/gameplay/room_loader.hpp b/source/game/gameplay/room_loader.hpp new file mode 100644 index 0000000..aead9f1 --- /dev/null +++ b/source/game/gameplay/room_loader.hpp @@ -0,0 +1,116 @@ +#pragma once + +#include // Para istream +#include // Para string +#include // Para pair +#include // Para vector + +#include "game/entities/enemy.hpp" // Para Enemy::Data +#include "game/entities/item.hpp" // Para Item::Data +#include "game/gameplay/room.hpp" // Para Room::Data + +/** + * @brief Cargador de archivos de habitaciones + * + * Responsabilidades: + * - Cargar archivos de room (.room) + * - Cargar archivos de tilemap (.tmx) + * - Parsear datos de room, enemy e item + * - Validar y convertir valores de configuración + * + * Esta clase contiene solo métodos estáticos y no debe instanciarse. + */ +class RoomLoader { + public: + // Constructor eliminado para prevenir instanciación + RoomLoader() = delete; + ~RoomLoader() = delete; + RoomLoader(const RoomLoader&) = delete; + auto operator=(const RoomLoader&) -> RoomLoader& = delete; + RoomLoader(RoomLoader&&) = delete; + auto operator=(RoomLoader&&) -> RoomLoader& = delete; + + /** + * @brief Carga un archivo de room (.room) + * @param file_path Ruta al archivo de room + * @param verbose Si true, muestra información de debug + * @return Room::Data con todos los datos de la habitación + * + * Parsea un archivo .room que contiene: + * - Variables de configuración (name, colors, borders, etc.) + * - Bloques [enemy]...[/enemy] con datos de enemigos + * - Bloques [item]...[/item] con datos de items + */ + static auto loadRoomFile(const std::string& file_path, bool verbose = false) -> Room::Data; + + /** + * @brief Carga un archivo de tilemap (.tmx) + * @param file_path Ruta al archivo de tilemap + * @param verbose Si true, muestra información de debug + * @return Vector de índices de tiles + * + * Parsea un archivo .tmx en formato CSV generado por Tiled + */ + static auto loadRoomTileFile(const std::string& file_path, bool verbose = false) -> std::vector; + + private: + /** + * @brief Asigna valores a una estructura Room::Data + * @param room Estructura a modificar + * @param key Nombre de la variable + * @param value Valor a asignar + * @return true si la variable fue reconocida y asignada, false si no + */ + static auto setRoom(Room::Data& room, const std::string& key, const std::string& value) -> bool; + + /** + * @brief Asigna valores a una estructura Enemy::Data + * @param enemy Estructura a modificar + * @param key Nombre de la variable + * @param value Valor a asignar + * @return true si la variable fue reconocida y asignada, false si no + */ + static auto setEnemy(Enemy::Data& enemy, const std::string& key, const std::string& value) -> bool; + + /** + * @brief Asigna valores a una estructura Item::Data + * @param item Estructura a modificar + * @param key Nombre de la variable + * @param value Valor a asignar + * @return true si la variable fue reconocida y asignada, false si no + */ + static auto setItem(Item::Data& item, const std::string& key, const std::string& value) -> bool; + + /** + * @brief Parsea una línea en formato "key=value" + * @param line Línea a parsear + * @return Par {key, value}. Si no hay '=', devuelve {"", ""} + */ + static auto parseKeyValue(const std::string& line) -> std::pair; + + /** + * @brief Registra un parámetro desconocido en la consola + * @param file_name Nombre del archivo siendo parseado + * @param key Nombre del parámetro desconocido + * @param verbose Si true, muestra el mensaje + */ + static void logUnknownParameter(const std::string& file_name, const std::string& key, bool verbose); + + /** + * @brief Carga un bloque [enemy]...[/enemy] desde un stream + * @param file Stream del archivo + * @param file_name Nombre del archivo (para debug) + * @param verbose Si true, muestra información de debug + * @return Enemy::Data con los datos del enemigo + */ + static auto loadEnemyFromFile(std::istream& file, const std::string& file_name, bool verbose) -> Enemy::Data; + + /** + * @brief Carga un bloque [item]...[/item] desde un stream + * @param file Stream del archivo + * @param file_name Nombre del archivo (para debug) + * @param verbose Si true, muestra información de debug + * @return Item::Data con los datos del item + */ + static auto loadItemFromFile(std::istream& file, const std::string& file_name, bool verbose) -> Item::Data; +};