#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; }