balloon_formation: posat ORDEN en el CAOS de tipos, structs i noms de variables que aci ningú sabia ja qui feia que. De paso llevades coses que sobraven i fetes les coses com toca. Este codi era del CC encara

balloon_formation: els pools de formacions es carreguen ara desde fitxer i ja no hi ha ni llimit de pools ni llimit de formacions per pool
falta: revisar les formacions i els pools que algo no quadra
This commit is contained in:
2025-07-24 15:58:04 +02:00
parent 2932664b9f
commit 1233b27eb6
10 changed files with 542 additions and 483 deletions

View File

@@ -33,7 +33,7 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
const int INDEX = static_cast<int>(size_);
gravity_ = param.balloon.settings.at(INDEX).grav;
default_vy_ = param.balloon.settings.at(INDEX).vel;
h_ = w_ = SIZE.at(INDEX);
h_ = w_ = WIDTH.at(INDEX);
power_ = POWER.at(INDEX);
menace_ = MENACE.at(INDEX);
score_ = SCORE.at(INDEX);
@@ -48,7 +48,7 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
gravity_ = 0.00F;
const int INDEX = static_cast<int>(size_);
h_ = w_ = SIZE.at(INDEX);
h_ = w_ = WIDTH.at(INDEX);
power_ = POWER.at(INDEX);
menace_ = MENACE.at(INDEX);
score_ = SCORE.at(INDEX);
@@ -60,7 +60,7 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
case Type::POWERBALL: {
constexpr int INDEX = 3;
h_ = w_ = SIZE.at(4);
h_ = w_ = WIDTH.at(4);
bouncing_sound_ = BOUNCING_SOUND.at(3);
popping_sound_ = "power_ball_explosion.wav";
power_ = score_ = menace_ = 0;
@@ -107,7 +107,7 @@ void Balloon::render() {
// Renderiza el fondo azul
{
auto sp = std::make_unique<Sprite>(sprite_->getTexture(), sprite_->getPosition());
sp->setSpriteClip(0, 0, SIZE.at(4), SIZE.at(4));
sp->setSpriteClip(0, 0, WIDTH.at(4), WIDTH.at(4));
sp->render();
}
@@ -121,7 +121,7 @@ void Balloon::render() {
// Añade la máscara del borde y los reflejos
{
auto sp = std::make_unique<Sprite>(sprite_->getTexture(), sprite_->getPosition());
sp->setSpriteClip(SIZE.at(4) * 2, 0, SIZE.at(4), SIZE.at(4));
sp->setSpriteClip(WIDTH.at(4) * 2, 0, WIDTH.at(4), WIDTH.at(4));
sp->render();
}
} else {

View File

@@ -21,7 +21,7 @@ class Balloon {
static constexpr std::array<int, 4> SCORE = {50, 100, 200, 400};
static constexpr std::array<int, 4> POWER = {1, 3, 7, 15};
static constexpr std::array<int, 4> MENACE = {1, 2, 4, 8};
static constexpr std::array<int, 5> SIZE = {10, 16, 26, 48, 49};
static constexpr std::array<int, 5> WIDTH = {10, 16, 26, 48, 49};
const std::array<std::string, 4> BOUNCING_SOUND = {
"balloon_bounce0.wav", "balloon_bounce1.wav", "balloon_bounce2.wav", "balloon_bounce3.wav"};
@@ -232,7 +232,7 @@ class Balloon {
bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar
bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar
bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido
BounceEffect bounce_effect_; // Efecto de rebote
BounceEffect bounce_effect_; // Efecto de rebote
// --- Posicionamiento y transformación ---
void shiftColliders(); // Alinea el círculo de colisión con el sprite

View File

@@ -19,43 +19,38 @@ 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);
const int X3_25 = param.game.play_area.first_quarter_x - (Balloon::WIDTH.at(3) / 2);
const int X3_75 = param.game.play_area.third_quarter_x - (Balloon::WIDTH.at(3) / 2);
const int X3_100 = param.game.play_area.rect.w - Balloon::WIDTH.at(3);
// 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);
const int X2_0 = param.game.play_area.rect.x;
const int X2_100 = param.game.play_area.rect.w - Balloon::WIDTH.at(2);
const int X1_0 = param.game.play_area.rect.x;
const int X1_100 = param.game.play_area.rect.w - Balloon::WIDTH.at(1);
const int X0_0 = param.game.play_area.rect.x;
const int X0_50 = param.game.play_area.center_x - (Balloon::WIDTH.at(0) / 2);
const int X0_100 = param.game.play_area.rect.w - Balloon::WIDTH.at(0);
// Mapa de variables para reemplazar en el archivo
std::map<std::string, float> 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},
{"X1_0", X0_0},
{"X1_50", X0_50},
{"X1_100", X0_100},
{"X2_0", X1_0},
{"X2_100", X1_100},
{"X3_0", X2_0},
{"X3_100", X2_100},
{"X4_0", X3_0},
{"X4_100", X3_100},
{"X3_25", X3_25},
{"X3_75", X3_75},
{"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();
@@ -70,7 +65,7 @@ auto BalloonFormations::loadFormationsFromFile(const std::string& filename, cons
std::string line;
int current_formation = -1;
std::vector<BalloonFormationParams> current_params;
std::vector<SpawnParams> current_params;
while (std::getline(file, line)) {
// Eliminar espacios en blanco al inicio y final
@@ -85,7 +80,7 @@ auto BalloonFormations::loadFormationsFromFile(const std::string& filename, cons
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);
formations_.emplace_back(current_params);
}
// Iniciar nueva formación
@@ -105,7 +100,7 @@ auto BalloonFormations::loadFormationsFromFile(const std::string& filename, cons
// Guardar última formación
if (current_formation >= 0 && !current_params.empty()) {
balloon_formation_.emplace_back(current_params.size(), current_params);
formations_.emplace_back(current_params);
}
// Crear variantes flotantes (formaciones 50-99)
@@ -120,7 +115,7 @@ auto BalloonFormations::loadFormationsFromFile(const std::string& filename, cons
return true;
}
auto BalloonFormations::parseBalloonLine(const std::string& line, const std::map<std::string, float>& variables) -> std::optional<BalloonFormationParams> {
auto BalloonFormations::parseBalloonLine(const std::string& line, const std::map<std::string, float>& variables) -> std::optional<SpawnParams> {
std::istringstream iss(line);
std::string token;
std::vector<std::string> tokens;
@@ -136,7 +131,7 @@ auto BalloonFormations::parseBalloonLine(const std::string& line, const std::map
try {
int x = evaluateExpression(tokens.at(0), variables);
int desp = evaluateExpression(tokens.at(1), variables);
int offset = evaluateExpression(tokens.at(1), variables);
int y = evaluateExpression(tokens.at(2), variables);
float vel_x = evaluateExpression(tokens.at(3), variables);
@@ -145,23 +140,23 @@ auto BalloonFormations::parseBalloonLine(const std::string& line, const std::map
Balloon::Size size;
if (tokens.at(5) == "SMALL") {
size = Balloon::Size::SMALL;
desp = desp * (Balloon::SIZE.at(0) + 1);
offset = offset * (Balloon::WIDTH.at(0) + 1);
} else if (tokens.at(5) == "MEDIUM") {
size = Balloon::Size::MEDIUM;
desp = desp * (Balloon::SIZE.at(1) + 1);
offset = offset * (Balloon::WIDTH.at(1) + 1);
} else if (tokens.at(5) == "LARGE") {
size = Balloon::Size::LARGE;
desp = desp * (Balloon::SIZE.at(2) + 1);
offset = offset * (Balloon::WIDTH.at(2) + 1);
} else if (tokens.at(5) == "EXTRALARGE") {
size = Balloon::Size::EXTRALARGE;
desp = desp * (Balloon::SIZE.at(3) + 1);
offset = offset * (Balloon::WIDTH.at(3) + 1);
} else {
return std::nullopt;
}
int creation_time = CREATION_TIME + evaluateExpression(tokens.at(6), variables);
int creation_time = DEFAULT_CREATION_TIME + evaluateExpression(tokens.at(6), variables);
return BalloonFormationParams(x + desp, y, vel_x, type, size, creation_time);
return SpawnParams(x + offset, y, vel_x, type, size, creation_time);
} catch (const std::exception&) {
return std::nullopt;
}
@@ -222,32 +217,30 @@ auto BalloonFormations::trim(const std::string& str) -> std::string {
}
void BalloonFormations::createFloaterVariants() {
balloon_formation_.resize(100);
formations_.resize(100);
// Crear variantes flotantes de las primeras 50 formaciones
for (size_t k = 0; k < 50 && k < balloon_formation_.size(); k++) {
std::vector<BalloonFormationParams> floater_params;
floater_params.reserve(balloon_formation_.at(k).number_of_balloons);
for (size_t k = 0; k < 50 && k < formations_.size(); k++) {
std::vector<SpawnParams> floater_params;
floater_params.reserve(formations_.at(k).balloons.size());
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, Balloon::Type::FLOATER, original.size, original.creation_counter);
for (size_t i = 0; i < formations_.at(k).balloons.size(); i++) {
const auto& original = formations_.at(k).balloons.at(i);
floater_params.emplace_back(original.x, original.y, original.vel_x, Balloon::Type::FLOATER, original.size, original.creation_counter);
}
balloon_formation_.at(k + 50) = BalloonFormationUnit(
balloon_formation_.at(k).number_of_balloons, floater_params);
formations_.at(k + 50) = Formation(floater_params);
}
}
#ifdef _DEBUG
void BalloonFormations::addTestFormation() {
std::vector<BalloonFormationParams> test_params = {
std::vector<SpawnParams> test_params = {
{10, -BLOCK, 0, Balloon::Type::FLOATER, Balloon::Size::SMALL, 200},
{50, -BLOCK, 0, Balloon::Type::FLOATER, Balloon::Size::MEDIUM, 200},
{90, -BLOCK, 0, Balloon::Type::FLOATER, Balloon::Size::LARGE, 200},
{140, -BLOCK, 0, Balloon::Type::FLOATER, Balloon::Size::EXTRALARGE, 200}};
balloon_formation_.at(99) = BalloonFormationUnit(4, test_params);
formations_.at(99) = Formation(test_params);
}
#endif
void BalloonFormations::loadDefaultFormations() {
@@ -256,147 +249,175 @@ void BalloonFormations::loadDefaultFormations() {
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 X4_100 = param.game.play_area.rect.w - Balloon::WIDTH.at(3);
// Formación básica #00
std::vector<BalloonFormationParams> basic_formation = {
BalloonFormationParams(X4_0, DEFAULT_POS_Y, Balloon::VELX_POSITIVE, Balloon::Type::BALLOON, Balloon::Size::EXTRALARGE, CREATION_TIME),
BalloonFormationParams(X4_100, DEFAULT_POS_Y, Balloon::VELX_NEGATIVE, Balloon::Type::BALLOON, Balloon::Size::EXTRALARGE, CREATION_TIME)};
balloon_formation_.emplace_back(2, basic_formation);
std::vector<SpawnParams> basic_formation = {
SpawnParams(X4_0, DEFAULT_POS_Y, Balloon::VELX_POSITIVE, Balloon::Type::BALLOON, Balloon::Size::EXTRALARGE, DEFAULT_CREATION_TIME),
SpawnParams(X4_100, DEFAULT_POS_Y, Balloon::VELX_NEGATIVE, Balloon::Type::BALLOON, Balloon::Size::EXTRALARGE, DEFAULT_CREATION_TIME)};
formations_.emplace_back(basic_formation);
}
// Inicializa los conjuntos de formaciones
// Nuevas implementaciones para el sistema de pools flexible
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)};
// Intentar cargar pools desde archivo
if (!loadPoolsFromFile(Asset::get()->get("balloon_pools.txt"))) {
// Fallback: cargar pools por defecto si falla la carga del archivo
loadDefaultPools();
}
}
auto BalloonFormations::loadPoolsFromFile(const std::string& filename) -> bool {
std::ifstream file(filename);
if (!file.is_open()) {
return false;
}
std::string line;
pools_.clear(); // Limpiar pools existentes
// Map temporal para ordenar los pools por ID
std::map<int, std::vector<int>> temp_pools;
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;
}
// Procesar línea de pool
auto pool_data = parsePoolLine(line);
if (pool_data.has_value()) {
temp_pools[pool_data->first] = pool_data->second;
}
}
file.close();
// Convertir el map ordenado a vector
// Redimensionar el vector para el pool con ID más alto
if (!temp_pools.empty()) {
int max_pool_id = temp_pools.rbegin()->first;
pools_.resize(max_pool_id + 1);
for (const auto& [pool_id, formations] : temp_pools) {
pools_[pool_id] = formations;
}
}
return !pools_.empty();
}
auto BalloonFormations::parsePoolLine(const std::string& line) -> std::optional<std::pair<int, std::vector<int>>> {
// Formato esperado: "POOL: 0 FORMATIONS: 1, 2, 14, 3, 5, 5"
// Buscar "POOL:"
size_t pool_pos = line.find("POOL:");
if (pool_pos == std::string::npos) {
return std::nullopt;
}
// Buscar "FORMATIONS:"
size_t formations_pos = line.find("FORMATIONS:");
if (formations_pos == std::string::npos) {
return std::nullopt;
}
try {
// Extraer el ID del pool
std::string pool_id_str = trim(line.substr(pool_pos + 5, formations_pos - pool_pos - 5));
int pool_id = std::stoi(pool_id_str);
// Extraer la lista de formaciones
std::string formations_str = trim(line.substr(formations_pos + 11));
std::vector<int> formation_ids;
// Parsear la lista de formaciones separadas por comas
std::istringstream iss(formations_str);
std::string token;
while (std::getline(iss, token, ',')) {
token = trim(token);
if (!token.empty()) {
int formation_id = std::stoi(token);
// Validar que el ID de formación existe
if (formation_id >= 0 && formation_id < static_cast<int>(formations_.size())) {
formation_ids.push_back(formation_id);
}
}
}
if (!formation_ids.empty()) {
return std::make_pair(pool_id, formation_ids);
}
} catch (const std::exception&) {
// Error de conversión o parsing
return std::nullopt;
}
return std::nullopt;
}
void BalloonFormations::loadDefaultPools() {
// Pools por defecto como fallback
pools_.clear();
// Crear algunos pools básicos si tenemos formaciones disponibles
if (formations_.empty()) {
return;
}
size_t total_formations = formations_.size();
// Pool 0: Primeras 10 formaciones (o las que haya disponibles)
Pool pool0;
for (size_t i = 0; i < std::min(size_t(10), total_formations); ++i) {
pool0.push_back(static_cast<int>(i));
}
if (!pool0.empty()) {
pools_.push_back(pool0);
}
// Pool 1: Formaciones 10-19 (si existen)
if (total_formations > 10) {
Pool pool1;
for (size_t i = 10; i < std::min(size_t(20), total_formations); ++i) {
pool1.push_back(static_cast<int>(i));
}
if (!pool1.empty()) {
pools_.push_back(pool1);
}
}
// Pool 2: Mix de formaciones normales y floaters (50+)
if (total_formations > 50) {
Pool pool2;
// Agregar algunas formaciones básicas
for (size_t i = 0; i < std::min(size_t(5), total_formations); ++i) {
pool2.push_back(static_cast<int>(i));
}
// Agregar algunas floaters si existen
for (size_t i = 50; i < std::min(size_t(55), total_formations); ++i) {
pool2.push_back(static_cast<int>(i));
}
if (!pool2.empty()) {
pools_.push_back(pool2);
}
}
// Pool 3: Solo floaters (si existen formaciones 50+)
if (total_formations > 50) {
Pool pool3;
for (size_t i = 50; i < std::min(size_t(70), total_formations); ++i) {
pool3.push_back(static_cast<int>(i));
}
if (!pool3.empty()) {
pools_.push_back(pool3);
}
}
}

View File

@@ -8,42 +8,40 @@
#include "balloon.h" // Para Balloon::Size, Balloon::Type
// --- Constantes de configuración ---
// --- Estructuras de datos ---
struct BalloonFormationParams {
int x = 0; // Posición en el eje X donde crear el globo
int y = 0; // Posición en el eje Y donde crear el globo
float vel_x = 0.0F; // Velocidad inicial en el eje X
Balloon::Type type = Balloon::Type::BALLOON; // Tipo de globo
Balloon::Size size = Balloon::Size::SMALL; // Tamaño de globo
int creation_counter = 0; // Temporizador para la creación del globo
// Constructor por defecto
BalloonFormationParams() = default;
// Constructor con parámetros
BalloonFormationParams(int x_val, int y_val, float vel_x_val, Balloon::Type type_val, Balloon::Size size_val, int creation_counter_val)
: x(x_val), y(y_val), vel_x(vel_x_val), type(type_val), size(size_val), creation_counter(creation_counter_val) {}
};
struct BalloonFormationUnit {
int number_of_balloons; // Cantidad de globos que forman la formación
std::vector<BalloonFormationParams> init; // Vector con todas las inicializaciones de los globos de la formación
// Constructor con parámetros
BalloonFormationUnit(int num_balloons, const std::vector<BalloonFormationParams>& init_params)
: number_of_balloons(num_balloons), init(init_params) {}
// Constructor por defecto
BalloonFormationUnit() : number_of_balloons(0) {}
};
using BalloonFormationPool = std::vector<const BalloonFormationUnit*>;
// --- Clase BalloonFormations ---
class BalloonFormations {
public:
// --- Estructuras de datos ---
struct SpawnParams {
int x = 0; // Posición en el eje X donde crear el globo
int y = 0; // Posición en el eje Y donde crear el globo
float vel_x = 0.0F; // Velocidad inicial en el eje X
Balloon::Type type = Balloon::Type::BALLOON; // Tipo de globo
Balloon::Size size = Balloon::Size::SMALL; // Tamaño de globo
int creation_counter = 0; // Temporizador para la creación del globo
// Constructor por defecto
SpawnParams() = default;
// Constructor con parámetros
SpawnParams(int x, int y, float vel_x, Balloon::Type type, Balloon::Size size, int creation_counter)
: x(x), y(y), vel_x(vel_x), type(type), size(size), creation_counter(creation_counter) {}
};
struct Formation {
std::vector<SpawnParams> balloons; // Vector con todas las inicializaciones de los globos de la formación
// Constructor con parámetros
Formation(const std::vector<SpawnParams>& spawn_params)
: balloons(spawn_params) {}
// Constructor por defecto
Formation() = default;
};
// Vector de índices a formaciones
using Pool = std::vector<int>;
// --- Constructor y destructor ---
BalloonFormations() {
initFormations();
@@ -52,43 +50,61 @@ class BalloonFormations {
~BalloonFormations() = default;
// --- Getters ---
auto getPool(int pool) -> const BalloonFormationPool& { return balloon_formation_pool_.at(pool); }
auto getSet(int pool, int set) -> const BalloonFormationUnit& { return *balloon_formation_pool_.at(pool).at(set); }
[[nodiscard]] auto getSet(int set) const -> const BalloonFormationUnit& { return balloon_formation_.at(set); }
auto getPool(int pool_id) -> const Pool& {
return pools_.at(pool_id);
}
auto getFormationFromPool(int pool_id, int formation_index) -> const Formation& {
int formation_id = pools_.at(pool_id).at(formation_index);
return formations_.at(formation_id);
}
[[nodiscard]] auto getFormation(int formation_id) const -> const Formation& {
return formations_.at(formation_id);
}
// --- Nuevos getters para información de pools ---
[[nodiscard]] auto getPoolCount() const -> size_t {
return pools_.size();
}
[[nodiscard]] auto getPoolSize(int pool_id) const -> size_t {
return pools_.at(pool_id).size();
}
private:
// --- Constantes ---
static constexpr int BALLOON_SPAWN_HEIGHT = 208; // Altura desde el suelo en la que aparecen los globos
static constexpr int CREATION_TIME = 200; // Tiempo base de creación de los globos para las formaciones
static constexpr int NUMBER_OF_BALLOON_FORMATIONS = 100;
static constexpr int MAX_NUMBER_OF_BALLOONS_IN_A_FORMATION = 50;
static constexpr int NUMBER_OF_SETS_PER_POOL = 10;
static constexpr int BALLOON_SPAWN_HEIGHT = 208; // Altura desde el suelo en la que aparecen los globos
static constexpr int DEFAULT_CREATION_TIME = 200; // Tiempo base de creación de los globos para las formaciones
// --- Datos ---
std::vector<BalloonFormationUnit> balloon_formation_; // Vector con todas las formaciones enemigas
std::vector<BalloonFormationPool> balloon_formation_pool_; // Conjuntos de formaciones enemigas
std::vector<Formation> formations_; // Vector con todas las formaciones disponibles
std::vector<Pool> pools_; // Vector de pools, cada pool contiene índices a formaciones
// --- Inicialización de formaciones ---
void initFormations(); // Inicializa la lista principal de formaciones de globos disponibles
void initFormationPools(); // Prepara las estructuras de agrupamiento o reutilización de formaciones (pools)
void initFormationPools(); // Carga los pools desde archivo de configuración
// --- Carga y análisis de datos ---
auto loadFormationsFromFile(const std::string& filename, const std::map<std::string, float>& variables) -> bool; // Carga las formaciones desde un archivo, evaluando variables dinámicas
auto parseBalloonLine(const std::string& line, const std::map<std::string, float>& variables) -> std::optional<BalloonFormationParams>; // Parsea una línea individual del archivo y genera parámetros de formación
auto loadFormationsFromFile(const std::string& filename, const std::map<std::string, float>& variables) -> bool;
auto parseBalloonLine(const std::string& line, const std::map<std::string, float>& variables) -> std::optional<SpawnParams>;
auto loadPoolsFromFile(const std::string& filename) -> bool; // Nueva función para cargar pools
auto parsePoolLine(const std::string& line) -> std::optional<std::pair<int, std::vector<int>>>; // Nueva función para parsear líneas de pools
// --- Evaluación de expresiones ---
auto evaluateExpression(const std::string& expr, const std::map<std::string, float>& variables) -> float; // Evalúa expresiones matemáticas con variables definidas (complejas)
auto evaluateSimpleExpression(const std::string& expr, const std::map<std::string, float>& variables) -> float; // Evalúa expresiones más sencillas (sin paréntesis o operadores avanzados)
auto evaluateExpression(const std::string& expr, const std::map<std::string, float>& variables) -> float;
auto evaluateSimpleExpression(const std::string& expr, const std::map<std::string, float>& variables) -> float;
// --- Utilidades ---
static auto trim(const std::string& str) -> std::string; // Elimina espacios en blanco al inicio y fin de una cadena
static auto trim(const std::string& str) -> std::string;
// --- Generación de variantes ---
void createFloaterVariants(); // Genera variantes de globos flotantes según configuración o aleatoriedad
void loadDefaultFormations(); // Carga las formaciones por defecto incluidas en el juego si no hay archivo externo
void createFloaterVariants();
void loadDefaultFormations();
void loadDefaultPools(); // Nueva función para pools por defecto
// --- Depuración (solo en modo DEBUG) ---
#ifdef _DEBUG
void addTestFormation(); // Añade una formación de prueba para debug o validación en tiempo de desarrollo
void addTestFormation();
#endif
};
};

View File

@@ -94,27 +94,18 @@ void BalloonManager::deployBalloonFormation(int stage) {
power_ball_counter_ = (power_ball_counter_ > 0) ? (power_ball_counter_ - 1) : 0;
// Elige una formación enemiga la azar
auto formation = rand() % 10;
auto formation_id = rand() % 10;
// Evita repetir la ultima formación enemiga desplegada
if (formation == last_balloon_deploy_) {
++formation %= 10;
if (formation_id == last_balloon_deploy_) {
++formation_id %= 10;
}
last_balloon_deploy_ = formation;
last_balloon_deploy_ = formation_id;
const auto SET = balloon_formations_->getSet(stage, formation);
const auto NUM_ENEMIES = SET.number_of_balloons;
for (int i = 0; i < NUM_ENEMIES; ++i) {
auto p = SET.init[i];
createBalloon(
p.x,
p.y,
p.type,
p.size,
p.vel_x,
balloon_speed_,
(creation_time_enabled_) ? p.creation_counter : 0);
const auto BALLOONS = balloon_formations_->getFormationFromPool(stage, formation_id).balloons;
for (auto balloon : BALLOONS) {
createBalloon(balloon.x, balloon.y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, (creation_time_enabled_) ? balloon.creation_counter : 0);
}
balloon_deploy_counter_ = 300;
@@ -123,21 +114,17 @@ void BalloonManager::deployBalloonFormation(int stage) {
}
// Crea una formación de enemigos específica
void BalloonManager::deploySet(int set_number) {
const auto SET = balloon_formations_->getSet(set_number);
const auto NUM_ENEMIES = SET.number_of_balloons;
for (int i = 0; i < NUM_ENEMIES; ++i) {
auto balloon = SET.init[i];
void BalloonManager::deployFormation(int formation_id) {
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
for (auto balloon : BALLOONS) {
createBalloon(balloon.x, balloon.y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, balloon.creation_counter);
}
}
// Crea una formación de enemigos específica
void BalloonManager::deploySet(int set_number, int y) {
const auto SET = balloon_formations_->getSet(set_number);
const auto NUM_ENEMIES = SET.number_of_balloons;
for (int i = 0; i < NUM_ENEMIES; ++i) {
auto balloon = SET.init[i];
// Crea una formación de enemigos específica a una altura determinada
void BalloonManager::deployFormation(int formation_id, int y) {
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
for (auto balloon : BALLOONS) {
createBalloon(balloon.x, y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, balloon.creation_counter);
}
}
@@ -184,7 +171,7 @@ void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon,
const float VX = direction == "LEFT" ? Balloon::VELX_NEGATIVE : Balloon::VELX_POSITIVE;
const auto SIZE = static_cast<Balloon::Size>(static_cast<int>(balloon->getSize()) - 1);
const int PARENT_HEIGHT = balloon->getHeight();
const int CHILD_HEIGHT = Balloon::SIZE.at(static_cast<int>(balloon->getSize()) - 1);
const int CHILD_HEIGHT = Balloon::WIDTH.at(static_cast<int>(balloon->getSize()) - 1);
const int Y = balloon->getPosY() + (PARENT_HEIGHT - CHILD_HEIGHT) / 2;
const int X = direction == "LEFT" ? balloon->getPosX() + (balloon->getWidth() / 3) : balloon->getPosX() + 2 * (balloon->getWidth() / 3);
@@ -209,12 +196,12 @@ void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon,
void BalloonManager::createPowerBall() {
if (can_deploy_balloons_) {
constexpr int VALUES = 6;
constexpr float POS_Y = -Balloon::SIZE.at(4);
constexpr float POS_Y = -Balloon::WIDTH.at(4);
constexpr int CREATION_TIME = 0;
const float LEFT = param.game.play_area.rect.x;
const float CENTER = param.game.play_area.center_x - (Balloon::SIZE.at(4) / 2);
const float RIGHT = param.game.play_area.rect.w - Balloon::SIZE.at(4);
const float CENTER = param.game.play_area.center_x - (Balloon::WIDTH.at(4) / 2);
const float RIGHT = param.game.play_area.rect.w - Balloon::WIDTH.at(4);
const int LUCK = rand() % VALUES;
const std::array<float, VALUES> POS_X = {LEFT, LEFT, CENTER, CENTER, RIGHT, RIGHT};
@@ -342,14 +329,14 @@ void BalloonManager::normalColorsToAllBalloons() {
// Crea dos globos gordos
void BalloonManager::createTwoBigBalloons() {
deploySet(1);
deployFormation(1);
}
// Crea una disposición de globos aleatoria
void BalloonManager::createRandomBalloons() {
const int NUM_BALLOONS = 2 + rand() % 4;
for (int i = 0; i < NUM_BALLOONS; ++i) {
const float X = param.game.game_area.rect.x + (rand() % static_cast<int>(param.game.game_area.rect.w)) - Balloon::SIZE.at(3);
const float X = param.game.game_area.rect.x + (rand() % static_cast<int>(param.game.game_area.rect.w)) - Balloon::WIDTH.at(3);
const int Y = param.game.game_area.rect.y + (rand() % 50);
const auto SIZE = static_cast<Balloon::Size>(rand() % 4);
const float VEL_X = (rand() % 2 == 0) ? Balloon::VELX_POSITIVE : Balloon::VELX_NEGATIVE;

View File

@@ -34,15 +34,15 @@ class BalloonManager {
// Creación de formaciones enemigas
void deployBalloonFormation(int stage); // Crea una formación de enemigos aleatoria
void deploySet(int set); // Crea una formación específica
void deploySet(int set, int y); // Crea una formación específica con coordenadas
void deployFormation(int set); // Crea una formación específica
void deployFormation(int set, int y); // Crea una formación específica con coordenadas
// Creación de globos
auto createBalloon(float x, int y, Balloon::Type type, Balloon::Size size, float velx, float speed, int creation_timer) -> std::shared_ptr<Balloon>; // Crea un nuevo globo
void createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction); // Crea un globo a partir de otro
void createPowerBall(); // Crea una PowerBall
void createTwoBigBalloons(); // Crea dos globos grandes
void createRandomBalloons(); // Crea una disposición aleatoria de globos
void createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction); // Crea un globo a partir de otro
void createPowerBall(); // Crea una PowerBall
void createTwoBigBalloons(); // Crea dos globos grandes
void createRandomBalloons(); // Crea una disposición aleatoria de globos
// Control de velocidad y despliegue
void setBalloonSpeed(float speed); // Ajusta la velocidad de los globos

View File

@@ -260,6 +260,7 @@ void Director::setFileList() {
Asset::get()->add(PREFIX + "/data/config/demo2.bin", AssetType::DEMODATA);
Asset::get()->add(PREFIX + "/data/config/gamecontrollerdb.txt", AssetType::DATA);
Asset::get()->add(PREFIX + "/data/config/balloon_formations.txt", AssetType::DATA);
Asset::get()->add(PREFIX + "/data/config/pool_formations.txt", AssetType::DATA);
// Musicas
Asset::get()->add(PREFIX + "/data/music/intro.ogg", AssetType::MUSIC);

View File

@@ -314,7 +314,7 @@ void Credits::throwBalloons() {
if (counter_ % SPEED == 0) {
const int INDEX = (counter_ / SPEED) % SETS.size();
balloon_manager_->deploySet(SETS.at(INDEX), -60);
balloon_manager_->deployFormation(SETS.at(INDEX), -60);
}
if (counter_ % (SPEED * 4) == 0 && counter_ > 0) {