#include "room_loader.hpp" #include // Para exception #include // Para cout, cerr #include "external/fkyaml_node.hpp" // Para fkyaml::node #include "core/resources/resource_helper.hpp" // Para Resource::Helper #include "utils/defines.hpp" // Para TILE_SIZE #include "utils/utils.hpp" // Para stringToColor // Convierte room connection de YAML a formato interno auto RoomLoader::convertRoomConnection(const std::string& value) -> std::string { if (value == "null" || value.empty()) { return "0"; } // Si ya tiene .yaml, devolverlo tal cual; si no, añadirlo if (value.size() > 5 && value.substr(value.size() - 5) == ".yaml") { return value; } return value + ".yaml"; } // Convierte string de autoSurface a int auto RoomLoader::convertAutoSurface(const fkyaml::node& node) -> int { if (node.is_integer()) { return node.get_value(); } if (node.is_string()) { const std::string value = node.get_value(); if (value == "left") return -1; if (value == "right") return 1; } return 0; // "none" o default } // Convierte un tilemap 2D a vector 1D flat auto RoomLoader::flattenTilemap(const std::vector>& tilemap_2d) -> std::vector { std::vector tilemap_flat; tilemap_flat.reserve(512); // 16 rows × 32 cols for (const auto& row : tilemap_2d) { for (int tile : row) { tilemap_flat.push_back(tile); } } return tilemap_flat; } // Carga un archivo de room en formato YAML auto RoomLoader::loadYAML(const std::string& file_path, bool verbose) -> Room::Data { Room::Data room; // Extract filename for logging const std::string FILE_NAME = file_path.substr(file_path.find_last_of("\\/") + 1); try { // Load YAML file using ResourceHelper (supports both filesystem and pack) auto file_data = Resource::Helper::loadFile(file_path); if (file_data.empty()) { std::cerr << "Error: Unable to load file " << FILE_NAME << '\n'; return room; } // Parse YAML from string std::string yaml_content(file_data.begin(), file_data.end()); auto yaml = fkyaml::node::deserialize(yaml_content); // --- Parse room configuration --- if (yaml.contains("room")) { const auto& room_node = yaml["room"]; // Extract room number from filename (e.g., "01.yaml" → "01") room.number = FILE_NAME.substr(0, FILE_NAME.find_last_of('.')); // Basic properties if (room_node.contains("name")) { room.name = room_node["name"].get_value(); } if (room_node.contains("bgColor")) { room.bg_color = room_node["bgColor"].get_value(); } if (room_node.contains("border")) { room.border_color = room_node["border"].get_value(); } if (room_node.contains("tileSetFile")) { room.tile_set_file = room_node["tileSetFile"].get_value(); } // Room connections if (room_node.contains("connections")) { const auto& conn = room_node["connections"]; if (conn.contains("up")) { room.upper_room = convertRoomConnection(conn["up"].get_value_or("null")); } else { room.upper_room = "0"; } if (conn.contains("down")) { room.lower_room = convertRoomConnection(conn["down"].get_value_or("null")); } else { room.lower_room = "0"; } if (conn.contains("left")) { room.left_room = convertRoomConnection(conn["left"].get_value_or("null")); } else { room.left_room = "0"; } if (conn.contains("right")) { room.right_room = convertRoomConnection(conn["right"].get_value_or("null")); } else { room.right_room = "0"; } } // Item colors if (room_node.contains("itemColor1")) { room.item_color1 = room_node["itemColor1"].get_value_or("yellow"); } else { room.item_color1 = "yellow"; } if (room_node.contains("itemColor2")) { room.item_color2 = room_node["itemColor2"].get_value_or("magenta"); } else { room.item_color2 = "magenta"; } // Dirección de la cinta transportadora (left/none/right) if (room_node.contains("conveyorBelt")) { room.conveyor_belt_direction = convertAutoSurface(room_node["conveyorBelt"]); } else { room.conveyor_belt_direction = 0; } } // --- Parse tilemap --- if (yaml.contains("tilemap")) { const auto& tilemap_node = yaml["tilemap"]; // Read 2D array std::vector> tilemap_2d; tilemap_2d.reserve(16); for (const auto& row_node : tilemap_node) { std::vector row; row.reserve(32); for (const auto& tile_node : row_node) { row.push_back(tile_node.get_value()); } tilemap_2d.push_back(row); } // Convert to 1D flat array room.tile_map = flattenTilemap(tilemap_2d); if (verbose) { std::cout << "Loaded tilemap: " << room.tile_map.size() << " tiles\n"; } } else { std::cerr << "Warning: No tilemap found in " << FILE_NAME << '\n'; } // --- Parse enemies --- if (yaml.contains("enemies") && !yaml["enemies"].is_null()) { const auto& enemies_node = yaml["enemies"]; for (const auto& enemy_node : enemies_node) { Enemy::Data enemy; // Animation path if (enemy_node.contains("animation")) { enemy.animation_path = enemy_node["animation"].get_value(); } // Position (in tiles, convert to pixels) if (enemy_node.contains("position")) { const auto& pos = enemy_node["position"]; if (pos.contains("x")) { enemy.x = pos["x"].get_value() * TILE_SIZE; } if (pos.contains("y")) { enemy.y = pos["y"].get_value() * TILE_SIZE; } } // Velocity (already in pixels/second) if (enemy_node.contains("velocity")) { const auto& vel = enemy_node["velocity"]; if (vel.contains("x")) { enemy.vx = vel["x"].get_value(); } if (vel.contains("y")) { enemy.vy = vel["y"].get_value(); } } // Boundaries (in tiles, convert to pixels) // Soporta formato nuevo (position1/position2) y antiguo (x1/y1/x2/y2) if (enemy_node.contains("boundaries")) { const auto& bounds = enemy_node["boundaries"]; // Nuevo formato: position1 y position2 if (bounds.contains("position1")) { const auto& pos1 = bounds["position1"]; if (pos1.contains("x")) { enemy.x1 = pos1["x"].get_value() * TILE_SIZE; } if (pos1.contains("y")) { enemy.y1 = pos1["y"].get_value() * TILE_SIZE; } } if (bounds.contains("position2")) { const auto& pos2 = bounds["position2"]; if (pos2.contains("x")) { enemy.x2 = pos2["x"].get_value() * TILE_SIZE; } if (pos2.contains("y")) { enemy.y2 = pos2["y"].get_value() * TILE_SIZE; } } // Formato antiguo: x1/y1/x2/y2 (compatibilidad) if (bounds.contains("x1")) { enemy.x1 = bounds["x1"].get_value() * TILE_SIZE; } if (bounds.contains("y1")) { enemy.y1 = bounds["y1"].get_value() * TILE_SIZE; } if (bounds.contains("x2")) { enemy.x2 = bounds["x2"].get_value() * TILE_SIZE; } if (bounds.contains("y2")) { enemy.y2 = bounds["y2"].get_value() * TILE_SIZE; } } // Color if (enemy_node.contains("color")) { enemy.color = enemy_node["color"].get_value_or("white"); } else { enemy.color = "white"; } // Optional fields if (enemy_node.contains("flip")) { enemy.flip = enemy_node["flip"].get_value_or(false); } else { enemy.flip = false; } if (enemy_node.contains("mirror")) { enemy.mirror = enemy_node["mirror"].get_value_or(false); } else { enemy.mirror = false; } if (enemy_node.contains("frame")) { enemy.frame = enemy_node["frame"].get_value_or(-1); } else { enemy.frame = -1; } room.enemies.push_back(enemy); } if (verbose) { std::cout << "Loaded " << room.enemies.size() << " enemies\n"; } } // --- Parse items --- if (yaml.contains("items") && !yaml["items"].is_null()) { const auto& items_node = yaml["items"]; for (const auto& item_node : items_node) { Item::Data item; // Tileset file if (item_node.contains("tileSetFile")) { item.tile_set_file = item_node["tileSetFile"].get_value(); } // Tile index if (item_node.contains("tile")) { item.tile = item_node["tile"].get_value(); } // Position (in tiles, convert to pixels) if (item_node.contains("position")) { const auto& pos = item_node["position"]; if (pos.contains("x")) { item.x = pos["x"].get_value() * TILE_SIZE; } if (pos.contains("y")) { item.y = pos["y"].get_value() * TILE_SIZE; } } // Counter if (item_node.contains("counter")) { item.counter = item_node["counter"].get_value_or(0); } else { item.counter = 0; } // Colors (assigned from room defaults) item.color1 = stringToColor(room.item_color1); item.color2 = stringToColor(room.item_color2); room.items.push_back(item); } if (verbose) { std::cout << "Loaded " << room.items.size() << " items\n"; } } if (verbose) { std::cout << "Room loaded successfully: " << FILE_NAME << '\n'; } } catch (const fkyaml::exception& e) { std::cerr << "YAML parsing error in " << FILE_NAME << ": " << e.what() << '\n'; } catch (const std::exception& e) { std::cerr << "Error loading room " << FILE_NAME << ": " << e.what() << '\n'; } return room; }