#include "balloon_formations.h" #include // Para size_t #include // Para max #include // Para array #include // Para isdigit #include // Para exception #include // Para basic_istream, basic_ifstream, ifstream, istringstream #include // Para map, operator== #include // Para basic_istringstream #include // Para allocator, char_traits, operator==, string, operator<=>, basic_string, stoi, getline #include "asset.h" // Para Asset #include "balloon.h" // Para BALLOON_SIZE, BalloonSize, BalloonType, BALLOON_VELX_NEGATIVE, BALLOON_VELX_POSITIVE #include "param.h" // Para Param, ParamGame, param #include "utils.h" // Para Zone, BLOCK void BalloonFormations::initFormations() { // Calcular posiciones base const int DEFAULT_POS_Y = param.game.play_area.rect.h - BALLOON_SPAWN_HEIGHT; const int X4_0 = param.game.play_area.rect.x; const int X4_100 = param.game.play_area.rect.w - BALLOON_SIZE.at(3); const int X3_0 = param.game.play_area.rect.x; const int X3_100 = param.game.play_area.rect.w - BALLOON_SIZE.at(2); const int X2_0 = param.game.play_area.rect.x; const int X2_100 = param.game.play_area.rect.w - BALLOON_SIZE.at(1); const int X1_0 = param.game.play_area.rect.x; const int X1_50 = param.game.play_area.center_x - (BALLOON_SIZE.at(0) / 2); const int X1_100 = param.game.play_area.rect.w - BALLOON_SIZE.at(0); // Variables calculadas para posiciones especiales const int QUARTER1_X4 = param.game.play_area.first_quarter_x - (BALLOON_SIZE.at(3) / 2); const int QUARTER3_X4 = param.game.play_area.third_quarter_x - (BALLOON_SIZE.at(3) / 2); // Mapa de variables para reemplazar en el archivo std::map variables = { {"X1_0", X1_0}, {"X1_50", X1_50}, {"X1_100", X1_100}, {"X2_0", X2_0}, {"X2_100", X2_100}, {"X3_0", X3_0}, {"X3_100", X3_100}, {"X4_0", X4_0}, {"X4_100", X4_100}, {"QUARTER1_X4", QUARTER1_X4}, {"QUARTER3_X4", QUARTER3_X4}, {"DEFAULT_POS_Y", DEFAULT_POS_Y}, {"BALLOON_SIZE_0", BALLOON_SIZE.at(0)}, {"BALLOON_SIZE_1", BALLOON_SIZE.at(1)}, {"BALLOON_SIZE_2", BALLOON_SIZE.at(2)}, {"BALLOON_SIZE_3", BALLOON_SIZE.at(3)}, {"RIGHT", BALLOON_VELX_POSITIVE}, {"LEFT", BALLOON_VELX_NEGATIVE}}; balloon_formation_.reserve(NUMBER_OF_BALLOON_FORMATIONS); if (!loadFormationsFromFile(Asset::get()->get("balloon_formations.txt"), variables)) { // Fallback: cargar formaciones por defecto si falla la carga del archivo loadDefaultFormations(); } } auto BalloonFormations::loadFormationsFromFile(const std::string& filename, const std::map& variables) -> bool { std::ifstream file(filename); if (!file.is_open()) { return false; } std::string line; int current_formation = -1; std::vector current_params; while (std::getline(file, line)) { // Eliminar espacios en blanco al inicio y final line = trim(line); // Saltar líneas vacías y comentarios if (line.empty() || line.at(0) == '#') { continue; } // Verificar si es una nueva formación if (line.substr(0, 10) == "formation:") { // Guardar formación anterior si existe if (current_formation >= 0 && !current_params.empty()) { balloon_formation_.emplace_back(current_params.size(), current_params); } // Iniciar nueva formación current_formation = std::stoi(line.substr(10)); current_params.clear(); continue; } // Procesar línea de parámetros de balloon if (current_formation >= 0) { auto params = parseBalloonLine(line, variables); if (params.has_value()) { current_params.push_back(params.value()); } } } // Guardar última formación if (current_formation >= 0 && !current_params.empty()) { balloon_formation_.emplace_back(current_params.size(), current_params); } // Crear variantes flotantes (formaciones 50-99) createFloaterVariants(); // Añadir formación de prueba addTestFormation(); file.close(); return true; } auto BalloonFormations::parseBalloonLine(const std::string& line, const std::map& variables) -> std::optional { std::istringstream iss(line); std::string token; std::vector tokens; // Dividir por comas while (std::getline(iss, token, ',')) { tokens.push_back(trim(token)); } if (tokens.size() != 7) { return std::nullopt; } try { int x = evaluateExpression(tokens.at(0), variables); int desp = evaluateExpression(tokens.at(1), variables); int y = evaluateExpression(tokens.at(2), variables); float vel_x = evaluateExpression(tokens.at(3), variables); BalloonType type = (tokens.at(4) == "BALLOON") ? BalloonType::BALLOON : BalloonType::FLOATER; BalloonSize size; if (tokens.at(5) == "SIZE1") { size = BalloonSize::SIZE1; desp = desp * (BALLOON_SIZE.at(0) + 1); } else if (tokens.at(5) == "SIZE2") { size = BalloonSize::SIZE2; desp = desp * (BALLOON_SIZE.at(1) + 1); } else if (tokens.at(5) == "SIZE3") { size = BalloonSize::SIZE3; desp = desp * (BALLOON_SIZE.at(2) + 1); } else if (tokens.at(5) == "SIZE4") { size = BalloonSize::SIZE4; desp = desp * (BALLOON_SIZE.at(3) + 1); } else { return std::nullopt; } int creation_time = CREATION_TIME + evaluateExpression(tokens.at(6), variables); return BalloonFormationParams(x + desp, y, vel_x, type, size, creation_time); } catch (const std::exception&) { return std::nullopt; } } auto BalloonFormations::evaluateExpression(const std::string& expr, const std::map& variables) -> float { std::string trimmed_expr = trim(expr); // Si es un número directo if ((std::isdigit(trimmed_expr.at(0)) != 0) || (trimmed_expr.at(0) == '-' && trimmed_expr.length() > 1)) { return std::stoi(trimmed_expr); } // Si es una variable simple if (variables.find(trimmed_expr) != variables.end()) { return variables.at(trimmed_expr); } // Evaluación de expresiones simples (suma, resta, multiplicación) return evaluateSimpleExpression(trimmed_expr, variables); } auto BalloonFormations::evaluateSimpleExpression(const std::string& expr, const std::map& variables) -> float { // Buscar operadores (+, -, *, /) for (size_t i = 1; i < expr.length(); ++i) { char op = expr.at(i); if (op == '+' || op == '-' || op == '*' || op == '/') { std::string left = trim(expr.substr(0, i)); std::string right = trim(expr.substr(i + 1)); int left_val = evaluateExpression(left, variables); int right_val = evaluateExpression(right, variables); switch (op) { case '+': return left_val + right_val; case '-': return left_val - right_val; case '*': return left_val * right_val; case '/': return right_val != 0 ? left_val / right_val : 0; } } } // Si no se encuentra operador, intentar como variable o número return variables.find(expr) != variables.end() ? variables.at(expr) : std::stoi(expr); } auto BalloonFormations::trim(const std::string& str) -> std::string { size_t start = str.find_first_not_of(" \t\r\n"); if (start == std::string::npos) { return ""; } size_t end = str.find_last_not_of(" \t\r\n"); return str.substr(start, end - start + 1); } void BalloonFormations::createFloaterVariants() { balloon_formation_.resize(100); // Crear variantes flotantes de las primeras 50 formaciones for (size_t k = 0; k < 50 && k < balloon_formation_.size(); k++) { std::vector floater_params; floater_params.reserve(balloon_formation_.at(k).number_of_balloons); for (int i = 0; i < balloon_formation_.at(k).number_of_balloons; i++) { const auto& original = balloon_formation_.at(k).init.at(i); floater_params.emplace_back( original.x, original.y, original.vel_x, BalloonType::FLOATER, original.size, original.creation_counter); } balloon_formation_.at(k + 50) = BalloonFormationUnit( balloon_formation_.at(k).number_of_balloons, floater_params); } } void BalloonFormations::addTestFormation() { std::vector test_params = { {10, -BLOCK, 0, BalloonType::FLOATER, BalloonSize::SIZE1, 200}, {50, -BLOCK, 0, BalloonType::FLOATER, BalloonSize::SIZE2, 200}, {90, -BLOCK, 0, BalloonType::FLOATER, BalloonSize::SIZE3, 200}, {140, -BLOCK, 0, BalloonType::FLOATER, BalloonSize::SIZE4, 200}}; balloon_formation_.at(99) = BalloonFormationUnit(4, test_params); } void BalloonFormations::loadDefaultFormations() { // Código de fallback con algunas formaciones básicas hardcodeadas // para que el juego funcione aunque falle la carga del archivo const int DEFAULT_POS_Y = param.game.play_area.rect.h - BALLOON_SPAWN_HEIGHT; const int X4_0 = param.game.play_area.rect.x; const int X4_100 = param.game.play_area.rect.w - BALLOON_SIZE.at(3); // Formación básica #00 std::vector basic_formation = { BalloonFormationParams(X4_0, DEFAULT_POS_Y, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME), BalloonFormationParams(X4_100, DEFAULT_POS_Y, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME)}; balloon_formation_.emplace_back(2, basic_formation); } // Inicializa los conjuntos de formaciones void BalloonFormations::initFormationPools() { // Reserva espacio para cada pool de formaciones balloon_formation_pool_.resize(NUMBER_OF_SETS_PER_POOL); // Set #0 balloon_formation_pool_.at(0) = { &balloon_formation_.at(0), &balloon_formation_.at(1), &balloon_formation_.at(2), &balloon_formation_.at(3), &balloon_formation_.at(4), &balloon_formation_.at(5), &balloon_formation_.at(6), &balloon_formation_.at(7), &balloon_formation_.at(8), &balloon_formation_.at(9)}; // Set #1 balloon_formation_pool_.at(1) = { &balloon_formation_.at(10), &balloon_formation_.at(11), &balloon_formation_.at(12), &balloon_formation_.at(13), &balloon_formation_.at(14), &balloon_formation_.at(15), &balloon_formation_.at(16), &balloon_formation_.at(17), &balloon_formation_.at(18), &balloon_formation_.at(19)}; // Set #2 balloon_formation_pool_.at(2) = { &balloon_formation_.at(0), &balloon_formation_.at(1), &balloon_formation_.at(2), &balloon_formation_.at(3), &balloon_formation_.at(4), &balloon_formation_.at(55), &balloon_formation_.at(56), &balloon_formation_.at(57), &balloon_formation_.at(58), &balloon_formation_.at(59)}; // Set #3 balloon_formation_pool_.at(3) = { &balloon_formation_.at(50), &balloon_formation_.at(51), &balloon_formation_.at(52), &balloon_formation_.at(53), &balloon_formation_.at(54), &balloon_formation_.at(5), &balloon_formation_.at(6), &balloon_formation_.at(7), &balloon_formation_.at(8), &balloon_formation_.at(9)}; // Set #4 balloon_formation_pool_.at(4) = { &balloon_formation_.at(60), &balloon_formation_.at(61), &balloon_formation_.at(62), &balloon_formation_.at(63), &balloon_formation_.at(64), &balloon_formation_.at(65), &balloon_formation_.at(66), &balloon_formation_.at(67), &balloon_formation_.at(68), &balloon_formation_.at(69)}; // Set #5 balloon_formation_pool_.at(5) = { &balloon_formation_.at(10), &balloon_formation_.at(61), &balloon_formation_.at(12), &balloon_formation_.at(63), &balloon_formation_.at(14), &balloon_formation_.at(65), &balloon_formation_.at(16), &balloon_formation_.at(67), &balloon_formation_.at(18), &balloon_formation_.at(69)}; // Set #6 balloon_formation_pool_.at(6) = { &balloon_formation_.at(60), &balloon_formation_.at(11), &balloon_formation_.at(62), &balloon_formation_.at(13), &balloon_formation_.at(64), &balloon_formation_.at(15), &balloon_formation_.at(66), &balloon_formation_.at(17), &balloon_formation_.at(68), &balloon_formation_.at(19)}; // Set #7 balloon_formation_pool_.at(7) = { &balloon_formation_.at(20), &balloon_formation_.at(21), &balloon_formation_.at(22), &balloon_formation_.at(23), &balloon_formation_.at(24), &balloon_formation_.at(65), &balloon_formation_.at(66), &balloon_formation_.at(67), &balloon_formation_.at(68), &balloon_formation_.at(69)}; // Set #8 balloon_formation_pool_.at(8) = { &balloon_formation_.at(70), &balloon_formation_.at(71), &balloon_formation_.at(72), &balloon_formation_.at(73), &balloon_formation_.at(74), &balloon_formation_.at(15), &balloon_formation_.at(16), &balloon_formation_.at(17), &balloon_formation_.at(18), &balloon_formation_.at(19)}; // Set #9 balloon_formation_pool_.at(9) = { &balloon_formation_.at(20), &balloon_formation_.at(21), &balloon_formation_.at(22), &balloon_formation_.at(23), &balloon_formation_.at(24), &balloon_formation_.at(70), &balloon_formation_.at(71), &balloon_formation_.at(72), &balloon_formation_.at(73), &balloon_formation_.at(74)}; }