353 lines
11 KiB
C++
353 lines
11 KiB
C++
#include "room_loader.hpp"
|
||
|
||
#include <exception> // Para exception
|
||
#include <iostream> // 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<int>();
|
||
}
|
||
if (node.is_string()) {
|
||
const auto VALUE = node.get_value<std::string>();
|
||
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<std::vector<int>>& tilemap_2d) -> std::vector<int> {
|
||
std::vector<int> 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;
|
||
}
|
||
|
||
// Parsea la configuración general de la habitación
|
||
void RoomLoader::parseRoomConfig(const fkyaml::node& yaml, Room::Data& room, const std::string& file_name) {
|
||
if (!yaml.contains("room")) {
|
||
return;
|
||
}
|
||
|
||
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<std::string>();
|
||
}
|
||
if (room_node.contains("bgColor")) {
|
||
room.bg_color = room_node["bgColor"].get_value<std::string>();
|
||
}
|
||
if (room_node.contains("border")) {
|
||
room.border_color = room_node["border"].get_value<std::string>();
|
||
}
|
||
if (room_node.contains("tileSetFile")) {
|
||
room.tile_set_file = room_node["tileSetFile"].get_value<std::string>();
|
||
}
|
||
|
||
// Room connections
|
||
if (room_node.contains("connections")) {
|
||
parseRoomConnections(room_node["connections"], room);
|
||
}
|
||
|
||
// Item colors
|
||
room.item_color1 = room_node.contains("itemColor1")
|
||
? room_node["itemColor1"].get_value_or<std::string>("yellow")
|
||
: "yellow";
|
||
|
||
room.item_color2 = room_node.contains("itemColor2")
|
||
? room_node["itemColor2"].get_value_or<std::string>("magenta")
|
||
: "magenta";
|
||
|
||
// Dirección de la cinta transportadora (left/none/right)
|
||
room.conveyor_belt_direction = room_node.contains("conveyorBelt")
|
||
? convertAutoSurface(room_node["conveyorBelt"])
|
||
: 0;
|
||
}
|
||
|
||
// Parsea las conexiones de la habitación (arriba/abajo/izq/der)
|
||
void RoomLoader::parseRoomConnections(const fkyaml::node& conn_node, Room::Data& room) {
|
||
room.upper_room = conn_node.contains("up")
|
||
? convertRoomConnection(conn_node["up"].get_value_or<std::string>("null"))
|
||
: "0";
|
||
|
||
room.lower_room = conn_node.contains("down")
|
||
? convertRoomConnection(conn_node["down"].get_value_or<std::string>("null"))
|
||
: "0";
|
||
|
||
room.left_room = conn_node.contains("left")
|
||
? convertRoomConnection(conn_node["left"].get_value_or<std::string>("null"))
|
||
: "0";
|
||
|
||
room.right_room = conn_node.contains("right")
|
||
? convertRoomConnection(conn_node["right"].get_value_or<std::string>("null"))
|
||
: "0";
|
||
}
|
||
|
||
// Parsea el tilemap de la habitación
|
||
void RoomLoader::parseTilemap(const fkyaml::node& yaml, Room::Data& room, const std::string& file_name, bool verbose) {
|
||
if (!yaml.contains("tilemap")) {
|
||
std::cerr << "Warning: No tilemap found in " << file_name << '\n';
|
||
return;
|
||
}
|
||
|
||
const auto& tilemap_node = yaml["tilemap"];
|
||
|
||
// Read 2D array
|
||
std::vector<std::vector<int>> tilemap_2d;
|
||
tilemap_2d.reserve(16);
|
||
|
||
for (const auto& row_node : tilemap_node) {
|
||
std::vector<int> row;
|
||
row.reserve(32);
|
||
|
||
for (const auto& tile_node : row_node) {
|
||
row.push_back(tile_node.get_value<int>());
|
||
}
|
||
|
||
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";
|
||
}
|
||
}
|
||
|
||
// Parsea los límites de movimiento de un enemigo
|
||
void RoomLoader::parseEnemyBoundaries(const fkyaml::node& bounds_node, Enemy::Data& enemy) {
|
||
// Nuevo formato: position1 y position2
|
||
if (bounds_node.contains("position1")) {
|
||
const auto& pos1 = bounds_node["position1"];
|
||
if (pos1.contains("x")) {
|
||
enemy.x1 = pos1["x"].get_value<int>() * TILE_SIZE;
|
||
}
|
||
if (pos1.contains("y")) {
|
||
enemy.y1 = pos1["y"].get_value<int>() * TILE_SIZE;
|
||
}
|
||
}
|
||
if (bounds_node.contains("position2")) {
|
||
const auto& pos2 = bounds_node["position2"];
|
||
if (pos2.contains("x")) {
|
||
enemy.x2 = pos2["x"].get_value<int>() * TILE_SIZE;
|
||
}
|
||
if (pos2.contains("y")) {
|
||
enemy.y2 = pos2["y"].get_value<int>() * TILE_SIZE;
|
||
}
|
||
}
|
||
|
||
// Formato antiguo: x1/y1/x2/y2 (compatibilidad)
|
||
if (bounds_node.contains("x1")) {
|
||
enemy.x1 = bounds_node["x1"].get_value<int>() * TILE_SIZE;
|
||
}
|
||
if (bounds_node.contains("y1")) {
|
||
enemy.y1 = bounds_node["y1"].get_value<int>() * TILE_SIZE;
|
||
}
|
||
if (bounds_node.contains("x2")) {
|
||
enemy.x2 = bounds_node["x2"].get_value<int>() * TILE_SIZE;
|
||
}
|
||
if (bounds_node.contains("y2")) {
|
||
enemy.y2 = bounds_node["y2"].get_value<int>() * TILE_SIZE;
|
||
}
|
||
}
|
||
|
||
// Parsea los datos de un enemigo individual
|
||
auto RoomLoader::parseEnemyData(const fkyaml::node& enemy_node) -> Enemy::Data {
|
||
Enemy::Data enemy;
|
||
|
||
// Animation path
|
||
if (enemy_node.contains("animation")) {
|
||
enemy.animation_path = enemy_node["animation"].get_value<std::string>();
|
||
}
|
||
|
||
// 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<float>() * TILE_SIZE;
|
||
}
|
||
if (pos.contains("y")) {
|
||
enemy.y = pos["y"].get_value<float>() * 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<float>();
|
||
}
|
||
if (vel.contains("y")) {
|
||
enemy.vy = vel["y"].get_value<float>();
|
||
}
|
||
}
|
||
|
||
// Boundaries (in tiles, convert to pixels)
|
||
if (enemy_node.contains("boundaries")) {
|
||
parseEnemyBoundaries(enemy_node["boundaries"], enemy);
|
||
}
|
||
|
||
// Color
|
||
enemy.color = enemy_node.contains("color")
|
||
? enemy_node["color"].get_value_or<std::string>("white")
|
||
: "white";
|
||
|
||
// Optional fields
|
||
enemy.flip = enemy_node.contains("flip")
|
||
? enemy_node["flip"].get_value_or<bool>(false)
|
||
: false;
|
||
|
||
enemy.mirror = enemy_node.contains("mirror")
|
||
? enemy_node["mirror"].get_value_or<bool>(false)
|
||
: false;
|
||
|
||
enemy.frame = enemy_node.contains("frame")
|
||
? enemy_node["frame"].get_value_or<int>(-1)
|
||
: -1;
|
||
|
||
return enemy;
|
||
}
|
||
|
||
// Parsea la lista de enemigos de la habitación
|
||
void RoomLoader::parseEnemies(const fkyaml::node& yaml, Room::Data& room, bool verbose) {
|
||
if (!yaml.contains("enemies") || yaml["enemies"].is_null()) {
|
||
return;
|
||
}
|
||
|
||
const auto& enemies_node = yaml["enemies"];
|
||
|
||
for (const auto& enemy_node : enemies_node) {
|
||
room.enemies.push_back(parseEnemyData(enemy_node));
|
||
}
|
||
|
||
if (verbose) {
|
||
std::cout << "Loaded " << room.enemies.size() << " enemies\n";
|
||
}
|
||
}
|
||
|
||
// Parsea los datos de un item individual
|
||
auto RoomLoader::parseItemData(const fkyaml::node& item_node, const Room::Data& room) -> Item::Data {
|
||
Item::Data item;
|
||
|
||
// Tileset file
|
||
if (item_node.contains("tileSetFile")) {
|
||
item.tile_set_file = item_node["tileSetFile"].get_value<std::string>();
|
||
}
|
||
|
||
// Tile index
|
||
if (item_node.contains("tile")) {
|
||
item.tile = item_node["tile"].get_value<int>();
|
||
}
|
||
|
||
// 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<float>() * TILE_SIZE;
|
||
}
|
||
if (pos.contains("y")) {
|
||
item.y = pos["y"].get_value<float>() * TILE_SIZE;
|
||
}
|
||
}
|
||
|
||
// Counter
|
||
item.counter = item_node.contains("counter")
|
||
? item_node["counter"].get_value_or<int>(0)
|
||
: 0;
|
||
|
||
// Colors (assigned from room defaults)
|
||
item.color1 = stringToColor(room.item_color1);
|
||
item.color2 = stringToColor(room.item_color2);
|
||
|
||
return item;
|
||
}
|
||
|
||
// Parsea la lista de items de la habitación
|
||
void RoomLoader::parseItems(const fkyaml::node& yaml, Room::Data& room, bool verbose) {
|
||
if (!yaml.contains("items") || yaml["items"].is_null()) {
|
||
return;
|
||
}
|
||
|
||
const auto& items_node = yaml["items"];
|
||
|
||
for (const auto& item_node : items_node) {
|
||
room.items.push_back(parseItemData(item_node, room));
|
||
}
|
||
|
||
if (verbose) {
|
||
std::cout << "Loaded " << room.items.size() << " items\n";
|
||
}
|
||
}
|
||
|
||
// 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);
|
||
|
||
// Delegación a funciones especializadas
|
||
parseRoomConfig(yaml, room, FILE_NAME);
|
||
parseTilemap(yaml, room, FILE_NAME, verbose);
|
||
parseEnemies(yaml, room, verbose);
|
||
parseItems(yaml, room, verbose);
|
||
|
||
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;
|
||
}
|