#include "balloon_manager.h" #include // Para rand #include // Para remove_if #include // Para accumulate #include "balloon.h" // Para Balloon, BALLOON_SCORE, BALLOON_VELX... #include "balloon_formations.h" // Para BalloonFormationParams, BalloonForma... #include "explosions.h" // Para Explosions #include "jail_audio.h" // Para JA_PlaySound #include "param.h" // Para Param, ParamGame, param #include "resource.h" // Para Resource #include "screen.h" // Para Screen #include "stage.h" // Para power #include "texture.h" // Para Texture #include "utils.h" // Para Zone, BLOCK, Color, flash_color // 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("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("balloon4.png")); balloon_textures_.emplace_back(Resource::get()->getTexture("powerball.png")); // Animaciones -- Globos 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("balloon4.ani")); balloon_animations_.emplace_back(Resource::get()->getAnimation("powerball.ani")); // Texturas - Explosiones 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")); explosions_textures_.emplace_back(Resource::get()->getTexture("explosion4.png")); // Animaciones -- Explosiones 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")); explosions_animations_.emplace_back(Resource::get()->getAnimation("explosion4.ani")); // Añade texturas explosions_->addTexture(1, explosions_textures_[0], explosions_animations_[0]); explosions_->addTexture(2, explosions_textures_[1], explosions_animations_[1]); explosions_->addTexture(3, explosions_textures_[2], explosions_animations_[2]); explosions_->addTexture(4, 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 numEnemies = set.number_of_balloons; for (int i = 0; i < numEnemies; ++i) { auto p = set.init[i]; createBalloon(p.x, p.y, p.type, p.size, p.vel_x, balloon_speed_, p.creation_counter); } balloon_deploy_counter_ = 300; } } } // 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 bool BalloonManager::canPowerBallBeCreated() { return (!power_ball_enabled_) && (calculateScreenPower() > POWERBALL_SCREENPOWER_MINIMUM) && (power_ball_counter_ == 0); } // Calcula el poder actual de los globos en pantalla int BalloonManager::calculateScreenPower() { 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 std::shared_ptr BalloonManager::createBalloon(float x, int y, BalloonType type, BalloonSize size, float velx, float speed, int creation_timer) { const int index = static_cast(size); balloons_.emplace_back(std::make_shared(x, y, type, size, velx, speed, creation_timer, balloon_textures_.at(index), balloon_animations_.at(index))); return balloons_.back(); } // Crea un globo a partir de otro globo void BalloonManager::createChildBalloon(const std::shared_ptr &balloon, const std::string &direction) { const float vx = direction == "LEFT" ? BALLOON_VELX_NEGATIVE : BALLOON_VELX_POSITIVE; const auto lower_size = static_cast(static_cast(balloon->getSize()) - 1); auto b = createBalloon(0, balloon->getPosY(), balloon->getType(), lower_size, vx, balloon_speed_, 0); const int x = direction == "LEFT" ? balloon->getPosX() + (balloon->getWidth() / 3) : balloon->getPosX() + 2 * (balloon->getWidth() / 3); b->alignTo(x); b->setVelY(b->getType() == BalloonType::BALLOON ? -2.50f : BALLOON_VELX_NEGATIVE * 2.0f); if (balloon->isStopped()) { b->stop(); } if (balloon->isUsingReversedColor()) { b->useReverseColor(); } } // Crea una PowerBall void BalloonManager::createPowerBall() { constexpr int values = 6; constexpr int pos_y = -BALLOON_SIZE[4]; constexpr int creation_time = 0; const auto left = param.game.play_area.rect.x; const auto center = param.game.play_area.center_x - (BALLOON_SIZE[4] / 2); const auto right = param.game.play_area.rect.w - BALLOON_SIZE[4]; const auto luck = rand() % values; const int x[values] = {left, left, center, center, right, right}; const float vx[values] = {BALLOON_VELX_POSITIVE, BALLOON_VELX_POSITIVE, BALLOON_VELX_POSITIVE, BALLOON_VELX_NEGATIVE, BALLOON_VELX_NEGATIVE, BALLOON_VELX_NEGATIVE}; balloons_.emplace_back(std::make_unique(x[luck], pos_y, BalloonType::POWERBALL, BalloonSize::SIZE4, vx[luck], balloon_speed_, creation_time, balloon_textures_[4], balloon_animations_[4])); power_ball_enabled_ = true; power_ball_counter_ = 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 int BalloonManager::popBalloon(std::shared_ptr balloon) { Stage::addPower(1); int score = 0; if (balloon->getType() == BalloonType::POWERBALL) { score = destroyAllBalloons(); power_ball_enabled_ = false; balloon_deploy_counter_ = 20; } else { score = balloon->getScore(); if (balloon->getSize() != BalloonSize::SIZE1) { 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(); } return score; } // Explosiona un globo. Lo destruye = no crea otros globos int BalloonManager::destroyBalloon(std::shared_ptr &balloon) { 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 BalloonSize::SIZE4: score = BALLOON_SCORE[3] + (2 * BALLOON_SCORE[2]) + (4 * BALLOON_SCORE[1]) + (8 * BALLOON_SCORE[0]); break; case BalloonSize::SIZE3: score = BALLOON_SCORE[2] + (2 * BALLOON_SCORE[1]) + (4 * BALLOON_SCORE[0]); break; case BalloonSize::SIZE2: score = BALLOON_SCORE[1] + (2 * BALLOON_SCORE[0]); break; case BalloonSize::SIZE1: score = BALLOON_SCORE[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 int BalloonManager::destroyAllBalloons() { int score = 0; for (auto &balloon : balloons_) { score += destroyBalloon(balloon); } balloon_deploy_counter_ = 300; JA_PlaySound(Resource::get()->getSound("powerball.wav")); Screen::get()->flash(flash_color, 100); 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(); } } // Recarga las texturas void BalloonManager::reLoad() { for (auto &texture : balloon_textures_) { texture->reLoad(); } } // Crea dos globos gordos void BalloonManager::createTwoBigBalloons() { const auto set = balloon_formations_->getSet(0, 1); const auto numEnemies = set.number_of_balloons; for (int i = 0; i < numEnemies; ++i) { auto p = set.init[i]; createBalloon(p.x, p.y, p.type, p.size, p.vel_x, balloon_speed_, p.creation_counter); } } // Obtiene el nivel de ameza actual generado por los globos int BalloonManager::getMenace() { return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) { return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); }); }