#include "balloon_manager.h" #include // Para remove_if #include #include // Para rand #include // Para accumulate #include "balloon.h" // Para Balloon, Balloon::SCORE.at( )ALLOON_VELX... #include "balloon_formations.h" // Para BalloonFormationParams, BalloonForma... #include "color.h" // Para Zone, Color, flash_color #include "explosions.h" // Para Explosions #include "param.h" // Para Param, ParamGame, param #include "resource.h" // Para Resource #include "screen.h" // Para Screen #include "stage.h" // Para addPower #include "utils.h" // Constructor BalloonManager::BalloonManager() : explosions_(std::make_unique()), balloon_formations_(std::make_unique()) { init(); } // Inicializa void BalloonManager::init() { // Texturas - Globos balloon_textures_.emplace_back(Resource::get()->getTexture("balloon0.png")); balloon_textures_.emplace_back(Resource::get()->getTexture("balloon1.png")); balloon_textures_.emplace_back(Resource::get()->getTexture("balloon2.png")); balloon_textures_.emplace_back(Resource::get()->getTexture("balloon3.png")); balloon_textures_.emplace_back(Resource::get()->getTexture("powerball.png")); // Animaciones -- Globos balloon_animations_.emplace_back(Resource::get()->getAnimation("balloon0.ani")); balloon_animations_.emplace_back(Resource::get()->getAnimation("balloon1.ani")); balloon_animations_.emplace_back(Resource::get()->getAnimation("balloon2.ani")); balloon_animations_.emplace_back(Resource::get()->getAnimation("balloon3.ani")); balloon_animations_.emplace_back(Resource::get()->getAnimation("powerball.ani")); // Texturas - Explosiones explosions_textures_.emplace_back(Resource::get()->getTexture("explosion0.png")); explosions_textures_.emplace_back(Resource::get()->getTexture("explosion1.png")); explosions_textures_.emplace_back(Resource::get()->getTexture("explosion2.png")); explosions_textures_.emplace_back(Resource::get()->getTexture("explosion3.png")); // Animaciones -- Explosiones explosions_animations_.emplace_back(Resource::get()->getAnimation("explosion0.ani")); explosions_animations_.emplace_back(Resource::get()->getAnimation("explosion1.ani")); explosions_animations_.emplace_back(Resource::get()->getAnimation("explosion2.ani")); explosions_animations_.emplace_back(Resource::get()->getAnimation("explosion3.ani")); // Añade texturas explosions_->addTexture(0, explosions_textures_[0], explosions_animations_[0]); explosions_->addTexture(1, explosions_textures_[1], explosions_animations_[1]); explosions_->addTexture(2, explosions_textures_[2], explosions_animations_[2]); explosions_->addTexture(3, explosions_textures_[3], explosions_animations_[3]); } // Actualiza void BalloonManager::update() { for (auto balloon : balloons_) { balloon->update(); } updateBalloonDeployCounter(); explosions_->update(); } // Renderiza los objetos void BalloonManager::render() { for (auto &balloon : balloons_) { balloon->render(); } explosions_->render(); } // Crea una formación de enemigos void BalloonManager::deployBalloonFormation(int stage) { // Solo despliega una formación enemiga si ha pasado cierto tiempo desde la última if (balloon_deploy_counter_ == 0) { // En este punto se decide entre crear una powerball o una formación enemiga if ((rand() % 100 < 15) && (canPowerBallBeCreated())) { // Crea una powerball createPowerBall(); // Da un poco de margen para que se creen mas enemigos balloon_deploy_counter_ = 10; } else { // Decrementa el contador de despliegues enemigos de la PowerBall power_ball_counter_ = (power_ball_counter_ > 0) ? (power_ball_counter_ - 1) : 0; // Elige una formación enemiga la azar auto formation = rand() % 10; // Evita repetir la ultima formación enemiga desplegada if (formation == last_balloon_deploy_) { ++formation %= 10; } last_balloon_deploy_ = formation; 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); } balloon_deploy_counter_ = 300; } } } // 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 p = SET.init[i]; createBalloon(p.x, p.y, p.type, p.size, p.vel_x, balloon_speed_, p.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 p = SET.init[i]; createBalloon(p.x, y, p.type, p.size, p.vel_x, balloon_speed_, p.creation_counter); } } // Vacia del vector de globos los globos que ya no sirven void BalloonManager::freeBalloons() { auto it = std::remove_if(balloons_.begin(), balloons_.end(), [](const auto &balloon) { return !balloon->isEnabled(); }); balloons_.erase(it, balloons_.end()); } // Actualiza la variable enemyDeployCounter void BalloonManager::updateBalloonDeployCounter() { if (balloon_deploy_counter_ > 0) { --balloon_deploy_counter_; } } // Indica si se puede crear una powerball auto BalloonManager::canPowerBallBeCreated() -> bool { return (!power_ball_enabled_) && (calculateScreenPower() > Balloon::POWERBALL_SCREENPOWER_MINIMUM) && (power_ball_counter_ == 0); } // Calcula el poder actual de los globos en pantalla auto BalloonManager::calculateScreenPower() -> int { return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) { return sum + (balloon->isEnabled() ? balloon->getPower() : 0); }); } // Crea un globo nuevo en el vector de globos auto BalloonManager::createBalloon(float x, int y, Balloon::Type type, Balloon::Size size, float velx, float speed, int creation_timer) -> std::shared_ptr { if (can_deploy_balloons_) { const int INDEX = static_cast(size); balloons_.emplace_back(std::make_shared(x, y, type, size, velx, speed, creation_timer, play_area_, balloon_textures_.at(INDEX), balloon_animations_.at(INDEX))); balloons_.back()->setSound(sound_enabled_); balloons_.back()->setBouncingSound(bouncing_sound_enabled_); balloons_.back()->setPoppingSound(poping_sound_enabled_); return balloons_.back(); } return nullptr; } // Crea un globo a partir de otro globo void BalloonManager::createChildBalloon(const std::shared_ptr &balloon, const std::string &direction) { if (can_deploy_balloons_) { // Calcula parametros const float VX = direction == "LEFT" ? Balloon::VELX_NEGATIVE : Balloon::VELX_POSITIVE; const auto SIZE = static_cast(static_cast(balloon->getSize()) - 1); const int PARENT_HEIGHT = balloon->getHeight(); const int CHILD_HEIGHT = Balloon::SIZE.at(static_cast(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); // Crea el globo auto b = createBalloon(0, Y, balloon->getType(), SIZE, VX, balloon_speed_, 0); // Establece parametros b->alignTo(X); b->setVelY(b->getType() == Balloon::Type::BALLOON ? -2.50F : Balloon::VELX_NEGATIVE * 2.0F); // Herencia de estados if (balloon->isStopped()) { b->stop(); } if (balloon->isUsingReversedColor()) { b->useReverseColor(); } } } // Crea una PowerBall void BalloonManager::createPowerBall() { if (can_deploy_balloons_) { constexpr int VALUES = 6; constexpr float POS_Y = -Balloon::SIZE.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 int LUCK = rand() % VALUES; const std::array POS_X = {LEFT, LEFT, CENTER, CENTER, RIGHT, RIGHT}; const std::array VEL_X = {Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE}; balloons_.emplace_back(std::make_unique(POS_X[LUCK], POS_Y, Balloon::Type::POWERBALL, Balloon::Size::EXTRALARGE, VEL_X[LUCK], balloon_speed_, CREATION_TIME, play_area_, balloon_textures_[4], balloon_animations_[4])); balloons_.back()->setInvulnerable(true); power_ball_enabled_ = true; power_ball_counter_ = Balloon::POWERBALL_COUNTER; } } // Establece la velocidad de los globos void BalloonManager::setBalloonSpeed(float speed) { balloon_speed_ = speed; for (auto &balloon : balloons_) { balloon->setSpeed(speed); } } // Explosiona un globo. Lo destruye y crea otros dos si es el caso auto BalloonManager::popBalloon(std::shared_ptr balloon) -> int { Stage::addPower(1); int score = 0; if (balloon->getType() == Balloon::Type::POWERBALL) { balloon->pop(true); score = destroyAllBalloons(); power_ball_enabled_ = false; balloon_deploy_counter_ = 20; } else { score = balloon->getScore(); if (balloon->getSize() != Balloon::Size::SMALL) { createChildBalloon(balloon, "LEFT"); createChildBalloon(balloon, "RIGHT"); } // Agrega la explosión y elimina el globo explosions_->add(balloon->getPosX(), balloon->getPosY(), static_cast(balloon->getSize())); balloon->pop(true); } return score; } // Explosiona un globo. Lo destruye = no crea otros globos auto BalloonManager::destroyBalloon(std::shared_ptr &balloon) -> int { int score = 0; // Calcula la puntuación y el poder que generaria el globo en caso de romperlo a él y a sus hijos switch (balloon->getSize()) { case Balloon::Size::EXTRALARGE: score = Balloon::SCORE.at(3) + (2 * Balloon::SCORE.at(2)) + (4 * Balloon::SCORE.at(1)) + (8 * Balloon::SCORE.at(0)); break; case Balloon::Size::LARGE: score = Balloon::SCORE.at(2) + (2 * Balloon::SCORE.at(1)) + (4 * Balloon::SCORE.at(0)); break; case Balloon::Size::MEDIUM: score = Balloon::SCORE.at(1) + (2 * Balloon::SCORE.at(0)); break; case Balloon::Size::SMALL: score = Balloon::SCORE.at(0); break; default: score = 0; break; } // Aumenta el poder de la fase Stage::addPower(balloon->getPower()); // Destruye el globo explosions_->add(balloon->getPosX(), balloon->getPosY(), static_cast(balloon->getSize())); balloon->pop(); return score; } // Destruye todos los globos auto BalloonManager::destroyAllBalloons() -> int { int score = 0; for (auto &balloon : balloons_) { score += destroyBalloon(balloon); } balloon_deploy_counter_ = 300; Screen::get()->flash(FLASH_COLOR, 3); Screen::get()->shake(); return score; } // Detiene todos los globos void BalloonManager::stopAllBalloons() { for (auto &balloon : balloons_) { balloon->stop(); } } // Pone en marcha todos los globos void BalloonManager::startAllBalloons() { for (auto &balloon : balloons_) { if (!balloon->isBeingCreated()) { balloon->start(); } } } // Cambia el color de todos los globos void BalloonManager::reverseColorsToAllBalloons() { for (auto &balloon : balloons_) { if (balloon->isStopped()) { balloon->useReverseColor(); } } } // Cambia el color de todos los globos void BalloonManager::normalColorsToAllBalloons() { for (auto &balloon : balloons_) { balloon->useNormalColor(); } } // Crea dos globos gordos void BalloonManager::createTwoBigBalloons() { deploySet(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(param.game.game_area.rect.w)) - Balloon::SIZE.at(3); const int Y = param.game.game_area.rect.y + (rand() % 50); const auto SIZE = static_cast(rand() % 4); const float VEL_X = (rand() % 2 == 0) ? Balloon::VELX_POSITIVE : Balloon::VELX_NEGATIVE; const int CREATION_COUNTER = 0; createBalloon(X, Y, Balloon::Type::BALLOON, SIZE, VEL_X, balloon_speed_, CREATION_COUNTER); } } // Obtiene el nivel de ameza actual generado por los globos auto BalloonManager::getMenace() -> int { return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) { return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); }); } // Establece el sonido de los globos void BalloonManager::setSounds(bool value) { sound_enabled_ = value; for (auto &balloon : balloons_) { balloon->setSound(value); } } // Activa o desactiva los sonidos de rebote los globos void BalloonManager::setBouncingSounds(bool value) { bouncing_sound_enabled_ = value; for (auto &balloon : balloons_) { balloon->setBouncingSound(value); } } // Activa o desactiva los sonidos de los globos al explotar void BalloonManager::setPoppingSounds(bool value) { poping_sound_enabled_ = value; for (auto &balloon : balloons_) { balloon->setPoppingSound(value); } }