From caf04e3a7ec3c37ad6c193c486cc5b02fd746b36 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Tue, 5 Nov 2024 22:06:15 +0100 Subject: [PATCH] Treballant en BalloonManager --- source/balloon_manager.cpp | 361 +++++++++++++++++++++++++++++++++++++ source/balloon_manager.h | 99 ++++++++++ source/game.cpp | 350 ++--------------------------------- source/game.h | 31 +--- 4 files changed, 476 insertions(+), 365 deletions(-) create mode 100644 source/balloon_manager.cpp create mode 100644 source/balloon_manager.h diff --git a/source/balloon_manager.cpp b/source/balloon_manager.cpp new file mode 100644 index 0000000..0d4a280 --- /dev/null +++ b/source/balloon_manager.cpp @@ -0,0 +1,361 @@ +#include "balloon_manager.h" +#include // Para accumulate +#include // Para find_if, clamp, min, remove_if +#include "balloon.h" // Para Balloon, BALLOON_SCORE, BALLOON_VE... +#include "balloon_formations.h" // Para BalloonFormations, BalloonFormatio... +#include "resource.h" +#include "screen.h" +#include "explosions.h" // Para Explosions +#include "jail_audio.h" + +// Constructor +BalloonManager::BalloonManager() + : explosions_(std::make_unique()), + balloon_formations_(std::make_unique()) +{ + // 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")); + } + + // 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 -- 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")); + } + + // 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")); + } + + 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(); + } +} + +// Renderiza los globos +void BalloonManager::renderBalloons() +{ + for (auto &balloon : balloons_) + { + balloon->render(); + } +} + +// Crea una formación de enemigos +void BalloonManager::deployBalloonFormation() +{ + // 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_ = 300; + } + 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_->getStage(current_stage_).balloon_pool.set[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; + } + } +} + +// Gestiona el nivel de amenaza +void BalloonManager::updateMenace() +{ + const auto stage = balloon_formations_->getStage(current_stage_); + const float percent = current_power_ / stage.power_to_complete; + const int difference = stage.max_menace - stage.min_menace; + + // Aumenta el nivel de amenaza en función de la puntuación + menace_threshold_ = stage.min_menace + (difference * percent); + + // Si el nivel de amenza es inferior al umbral + if (menace_current_ < menace_threshold_) + { + // Crea una formación de enemigos + deployBalloonFormation(); + + // Recalcula el nivel de amenaza con el nuevo globo + evaluateAndSetMenace(); + } +} + +// 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()); +} + +// Calcula y establece el valor de amenaza en funcion de los globos activos +void BalloonManager::evaluateAndSetMenace() +{ + menace_current_ = std::accumulate( + balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) + { return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); }); +} + +// 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); + b->alignTo(balloon->getPosX() + (balloon->getWidth() / 2)); + 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 auto values = 6; + constexpr auto pos_y = -BLOCK; + constexpr int creation_time = 300; + + const auto left = param.game.play_area.rect.x; + const auto center = param.game.play_area.center_x - (BALLOON_SIZE[3] / 2); + const auto right = param.game.play_area.rect.w - BALLOON_SIZE[3]; + + 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) +{ + for (auto &balloon : balloons_) + balloon->setSpeed(speed); +} + +// Actualiza la velocidad de los globos en funcion del poder acumulado de la fase +void BalloonManager::checkAndUpdateBalloonSpeed() +{ + if (difficulty_ != GameDifficulty::NORMAL) + return; + + const float percent = static_cast(current_power_) / balloon_formations_->getStage(current_stage_).power_to_complete; + const float thresholds[] = {0.2f, 0.4f, 0.6f, 0.8f}; + + for (size_t i = 0; i < std::size(thresholds); ++i) + if (balloon_speed_ == BALLOON_SPEED[i] && percent > thresholds[i]) + { + balloon_speed_ = BALLOON_SPEED[i + 1]; + setBalloonSpeed(balloon_speed_); + break; // Salir del bucle una vez actualizada la velocidad y aplicada + } +} + +// Explosiona un globo. Lo destruye y crea otros dos si es el caso +void BalloonManager::popBalloon(std::shared_ptr balloon) +{ + increaseStageCurrentPower(1); + balloons_popped_++; + + if (balloon->getType() == BalloonType::POWERBALL) + { + destroyAllBalloons(); + power_ball_enabled_ = false; + balloon_deploy_counter_ = 20; + } + else + { + 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(); + } + + // Recalcula el nivel de amenaza + evaluateAndSetMenace(); +} + +// Explosiona un globo. Lo destruye = no crea otros globos +void 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; + } + + // Otorga los puntos correspondientes al globo + for (auto &player : players_) + player->addScore(score * player->getScoreMultiplier() * difficulty_score_multiplier_); + updateHiScore(); + + // Aumenta el poder de la fase + const auto power = balloon->getPower(); + increaseStageCurrentPower(power); + balloons_popped_ += power; + + // Destruye el globo + explosions_->add(balloon->getPosX(), balloon->getPosY(), static_cast(balloon->getSize())); + balloon->pop(); +} + +// Destruye todos los globos +void BalloonManager::destroyAllBalloons() +{ + for (auto &balloon : balloons_) + destroyBalloon(balloon); + + balloon_deploy_counter_ = 300; + JA_PlaySound(Resource::get()->getSound("powerball.wav")); + Screen::get()->flash(flash_color, 100); + Screen::get()->shake(); +} + +// 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(); + } +} \ No newline at end of file diff --git a/source/balloon_manager.h b/source/balloon_manager.h new file mode 100644 index 0000000..841b487 --- /dev/null +++ b/source/balloon_manager.h @@ -0,0 +1,99 @@ +#pragma once + +#include "balloon.h" +class BalloonFormations; // lines 16-16 +class Explosions; // lines 18-18 + +class BalloonManager +{ +private: + std::vector> balloons_; // Vector con los globos + std::unique_ptr explosions_; // Objeto para dibujar explosiones + std::unique_ptr balloon_formations_; // Objeto para gestionar las oleadas enemigas + + std::vector> balloon_textures_; // Vector con las texturas de los globos + std::vector> explosions_textures_; // Vector con las texturas de las explosiones + + std::vector> balloon_animations_; // Vector con las animaciones de los globos + std::vector> explosions_animations_; // Vector con las animaciones de las explosiones + + float balloon_speed_; // Velocidad a la que se mueven los enemigos + float default_balloon_speed_; // Velocidad base de los enemigos, sin incrementar + int balloon_deploy_counter_ = 0; // Cuando se lanza una formación, se le da un valor y no sale otra hasta que llegue a cero + int balloons_popped_ = 0; // Lleva la cuenta de los globos explotados + bool power_ball_enabled_ = false; // Indica si hay una powerball ya activa + int power_ball_counter_ = 0; // Contador de formaciones enemigas entre la aparicion de una PowerBall y otra + int last_balloon_deploy_ = 0; // Guarda cual ha sido la última formación desplegada para no repetir; + int menace_current_ = 0; // Nivel de amenaza actual + int menace_threshold_ = 0; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos + + // Crea una formación de enemigos + void deployBalloonFormation(); + + // Gestiona el nivel de amenaza + void updateMenace(); + + // Calcula y establece el valor de amenaza en funcion de los globos activos + void evaluateAndSetMenace(); + + // Actualiza la variable enemyDeployCounter + void updateBalloonDeployCounter(); + + // Indica si se puede crear una powerball + bool canPowerBallBeCreated(); + + // Calcula el poder actual de los globos en pantalla + int calculateScreenPower(); + + // Crea un globo nuevo en el vector de globos + std::shared_ptr createBalloon(float x, int y, BalloonType type, BalloonSize size, float velx, float speed, int creation_timer); + + // Crea un globo a partir de otro globo + void createChildBalloon(const std::shared_ptr &balloon, const std::string &direction); + + // Crea una PowerBall + void createPowerBall(); + + // Establece la velocidad de los globos + void setBalloonSpeed(float speed); + + // Actualiza la velocidad de los globos en funcion del poder acumulado de la fase + void checkAndUpdateBalloonSpeed(); + + // Explosiona un globo. Lo destruye y crea otros dos si es el caso + void popBalloon(std::shared_ptr balloon); + + // Explosiona un globo. Lo destruye = no crea otros globos + void destroyBalloon(std::shared_ptr &balloon); + + // Destruye todos los globos + void destroyAllBalloons(); + + // Detiene todos los globos + void stopAllBalloons(); + + // Pone en marcha todos los globos + void startAllBalloons(); + + // Cambia el color de todos los globos + void reverseColorsToAllBalloons(); + + // Cambia el color de todos los globos + void normalColorsToAllBalloons(); + +public: + // Constructor + BalloonManager(); + + // Destructor + ~BalloonManager() = default; + + // Actualiza + void update(); + + // Renderiza los globos + void renderBalloons(); + + // Vacia del vector de globos los globos que ya no sirven + void freeBalloons(); +}; \ No newline at end of file diff --git a/source/game.cpp b/source/game.cpp index 76dcdd3..20366ac 100644 --- a/source/game.cpp +++ b/source/game.cpp @@ -1,10 +1,8 @@ #include "game.h" #include "asset.h" // Para Asset #include "background.h" // Para Background -#include "balloon.h" // Para Balloon, BALLOON_SCORE, BALLOON_VE... -#include "balloon_formations.h" // Para BalloonFormations, BalloonFormatio... #include "bullet.h" // Para Bullet, BulletType, BulletMoveStatus -#include "explosions.h" // Para Explosions +#include "balloon_manager.h" #include "fade.h" // Para Fade, FadeType #include "global_inputs.h" // Para check #include "input.h" // Para InputType, Input, INPUT_DO_NOT_ALL... @@ -44,8 +42,6 @@ Game::Game(int player_id, int current_stage, bool demo) asset_(Asset::get()), input_(Input::get()), background_(std::make_unique()), - explosions_(std::make_unique()), - balloon_formations_(std::make_unique()), canvas_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.play_area.rect.w, param.game.play_area.rect.h)), fade_(std::make_unique()), current_stage_(current_stage) @@ -70,11 +66,6 @@ Game::Game(int player_id, int current_stage, bool demo) background_->setPos(param.game.play_area.rect); - 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]); - SDL_SetTextureBlendMode(canvas_, SDL_BLENDMODE_BLEND); // Inicializa el resto de variables @@ -137,23 +128,6 @@ void Game::setResources() game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_stop")); } - // 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")); - } - - // 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")); - } - // Texturas - Items { item_textures_.emplace_back(Resource::get()->getTexture("item_points1_disk.png")); @@ -186,23 +160,6 @@ void Game::setResources() player_animations_.emplace_back(Resource::get()->getAnimation("player_power.ani")); } - // 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")); - } - - // 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")); - } - // Animaciones -- Items { item_animations_.emplace_back(Resource::get()->getAnimation("item_points1_disk.ani")); @@ -214,50 +171,6 @@ void Game::setResources() } } -// Crea una formación de enemigos -void Game::deployBalloonFormation() -{ - // 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_ = 300; - } - 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_->getStage(current_stage_).balloon_pool.set[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; - } - } -} - // Aumenta el poder de la fase void Game::increaseStageCurrentPower(int power) { current_power_ += power; } @@ -458,169 +371,6 @@ void Game::checkState() } } -// Actualiza los globos -void Game::updateBalloons() -{ - for (auto balloon : balloons_) - balloon->update(); -} - -// Pinta en pantalla todos los globos activos -void Game::renderBalloons() -{ - for (auto &balloon : balloons_) - balloon->render(); -} - -// Crea un globo nuevo en el vector de globos -std::shared_ptr Game::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 Game::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); - b->alignTo(balloon->getPosX() + (balloon->getWidth() / 2)); - 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 Game::createPowerBall() -{ - constexpr auto values = 6; - constexpr auto pos_y = -BLOCK; - constexpr int creation_time = 300; - - const auto left = param.game.play_area.rect.x; - const auto center = param.game.play_area.center_x - (BALLOON_SIZE[3] / 2); - const auto right = param.game.play_area.rect.w - BALLOON_SIZE[3]; - - 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 Game::setBalloonSpeed(float speed) -{ - for (auto &balloon : balloons_) - balloon->setSpeed(speed); -} - -// Actualiza la velocidad de los globos en funcion del poder acumulado de la fase -void Game::checkAndUpdateBalloonSpeed() -{ - if (difficulty_ != GameDifficulty::NORMAL) - return; - - const float percent = static_cast(current_power_) / balloon_formations_->getStage(current_stage_).power_to_complete; - const float thresholds[] = {0.2f, 0.4f, 0.6f, 0.8f}; - - for (size_t i = 0; i < std::size(thresholds); ++i) - if (balloon_speed_ == BALLOON_SPEED[i] && percent > thresholds[i]) - { - balloon_speed_ = BALLOON_SPEED[i + 1]; - setBalloonSpeed(balloon_speed_); - break; // Salir del bucle una vez actualizada la velocidad y aplicada - } -} - -// Explosiona un globo. Lo destruye y crea otros dos si es el caso -void Game::popBalloon(std::shared_ptr balloon) -{ - increaseStageCurrentPower(1); - balloons_popped_++; - - if (balloon->getType() == BalloonType::POWERBALL) - { - destroyAllBalloons(); - power_ball_enabled_ = false; - balloon_deploy_counter_ = 20; - } - else - { - 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(); - } - - // Recalcula el nivel de amenaza - evaluateAndSetMenace(); -} - -// Explosiona un globo. Lo destruye = no crea otros globos -void Game::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; - } - - // Otorga los puntos correspondientes al globo - for (auto &player : players_) - player->addScore(score * player->getScoreMultiplier() * difficulty_score_multiplier_); - updateHiScore(); - - // Aumenta el poder de la fase - const auto power = balloon->getPower(); - increaseStageCurrentPower(power); - balloons_popped_ += power; - - // Destruye el globo - explosions_->add(balloon->getPosX(), balloon->getPosY(), static_cast(balloon->getSize())); - balloon->pop(); -} - -// Destruye todos los globos -void Game::destroyAllBalloons() -{ - for (auto &balloon : balloons_) - destroyBalloon(balloon); - - balloon_deploy_counter_ = 300; - JA_PlaySound(Resource::get()->getSound("powerball.wav")); - screen_->flash(flash_color, 100); - screen_->shake(); -} - // Destruye todos los items void Game::destroyAllItems() { @@ -628,44 +378,6 @@ void Game::destroyAllItems() item->disable(); } -// Detiene todos los globos -void Game::stopAllBalloons() -{ - for (auto &balloon : balloons_) - balloon->stop(); -} - -// Pone en marcha todos los globos -void Game::startAllBalloons() -{ - for (auto &balloon : balloons_) - if (!balloon->isBeingCreated()) - balloon->start(); -} - -// Cambia el color de todos los globos -void Game::reverseColorsToAllBalloons() -{ - for (auto &balloon : balloons_) - if (balloon->isStopped()) - balloon->useReverseColor(); -} - -// Cambia el color de todos los globos -void Game::normalColorsToAllBalloons() -{ - for (auto &balloon : balloons_) - balloon->useNormalColor(); -} - -// Vacia del vector de globos los globos que ya no sirven -void Game::freeBalloons() -{ - auto it = std::remove_if(balloons_.begin(), balloons_.end(), [](const auto &balloon) - { return !balloon->isEnabled(); }); - balloons_.erase(it, balloons_.end()); -} - // Comprueba la colisión entre el jugador y los globos activos bool Game::checkPlayerBalloonCollision(std::shared_ptr &player) { @@ -916,7 +628,9 @@ ItemType Game::dropItem() else { if (helper_.need_coffee) + { helper_.item_coffee_odds++; + } } break; case 5: @@ -924,12 +638,16 @@ ItemType Game::dropItem() { helper_.item_coffee_machine_odds = ITEM_COFFEE_MACHINE_ODDS_; if (!coffee_machine_enabled_ && helper_.need_coffee_machine) + { return ItemType::COFFEE_MACHINE; + } } else { if (helper_.need_coffee_machine) + { helper_.item_coffee_machine_odds++; + } } break; default: @@ -1088,14 +806,6 @@ void Game::killPlayer(std::shared_ptr &player) } } -// Calcula y establece el valor de amenaza en funcion de los globos activos -void Game::evaluateAndSetMenace() -{ - menace_current_ = std::accumulate( - balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) - { return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); }); -} - // Actualiza y comprueba el valor de la variable void Game::updateTimeStopped() { @@ -1121,13 +831,6 @@ void Game::updateTimeStopped() disableTimeStopItem(); } -// Actualiza la variable enemyDeployCounter -void Game::updateBalloonDeployCounter() -{ - if (balloon_deploy_counter_ > 0) - --balloon_deploy_counter_; -} - // Actualiza el juego void Game::update() { @@ -1186,7 +889,7 @@ void Game::fillCanvas() renderItems(); renderSmartSprites(); explosions_->render(); - renderBalloons(); + balloon_manager_->renderBalloons(); renderBullets(); renderPathSprites(); renderPlayers(); @@ -1214,27 +917,6 @@ void Game::render() screen_->blit(); } -// Gestiona el nivel de amenaza -void Game::updateMenace() -{ - const auto stage = balloon_formations_->getStage(current_stage_); - const float percent = current_power_ / stage.power_to_complete; - const int difference = stage.max_menace - stage.min_menace; - - // Aumenta el nivel de amenaza en función de la puntuación - menace_threshold_ = stage.min_menace + (difference * percent); - - // Si el nivel de amenza es inferior al umbral - if (menace_current_ < menace_threshold_) - { - // Crea una formación de enemigos - deployBalloonFormation(); - - // Recalcula el nivel de amenaza con el nuevo globo - evaluateAndSetMenace(); - } -} - // Habilita el efecto del item de detener el tiempo void Game::enableTimeStopItem() { @@ -1278,16 +960,6 @@ void Game::run() (demo_.enabled) ? JA_EnableSound(options.audio.sound.enabled) : JA_StopMusic(); } -// Indica si se puede crear una powerball -bool Game::canPowerBallBeCreated() { return (!power_ball_enabled_) && (calculateScreenPower() > POWERBALL_SCREENPOWER_MINIMUM) && (power_ball_counter_ == 0); } - -// Calcula el poder actual de los globos en pantalla -int Game::calculateScreenPower() -{ - return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) - { return sum + (balloon->isEnabled() ? balloon->getPower() : 0); }); -} - // Inicializa las variables que contienen puntos de ruta para mover objetos void Game::initPaths() { @@ -1361,7 +1033,9 @@ void Game::updateHelper() } } else + { helper_.need_coffee = helper_.need_coffee_machine = false; + } } // Comprueba si todos los jugadores han terminado de jugar @@ -2079,7 +1753,7 @@ void Game::updateGame() checkPlayersStatusPlaying(); updateScoreboard(); updateBackground(); - updateBalloons(); + balloon_manager_->update(); explosions_->update(); moveBullets(); updateItems(); @@ -2103,7 +1777,7 @@ void Game::updateGame() void Game::cleanVectors() { freeBullets(); - freeBalloons(); + balloon_manager_->freeBalloons(); freeItems(); freeSmartSprites(); freePathSprites(); diff --git a/source/game.h b/source/game.h index 9474f91..385189e 100644 --- a/source/game.h +++ b/source/game.h @@ -5,16 +5,14 @@ #include // Para shared_ptr, unique_ptr #include // Para string #include // Para vector -#include "balloon.h" // Para Balloon +#include "balloon_manager.h" // Para BalloonManager #include "manage_hiscore_table.h" // Para HiScoreEntry #include "options.h" // Para Options, OptionsGame, options #include "player.h" // Para Player #include "utils.h" // Para Demo class Asset; // lines 14-14 class Background; // lines 15-15 -class BalloonFormations; // lines 16-16 class Bullet; // lines 17-17 -class Explosions; // lines 18-18 class Fade; // lines 19-19 class Input; // lines 20-20 class Item; // lines 21-21 @@ -124,13 +122,10 @@ private: Scoreboard *scoreboard_; // Objeto para dibujar el marcador std::unique_ptr background_; // Objeto para dibujar el fondo del juego - std::unique_ptr explosions_; // Objeto para dibujar explosiones - std::unique_ptr balloon_formations_; // Objeto para gestionar las oleadas enemigas SDL_Texture *canvas_; // Textura para dibujar la zona de juego std::vector> players_; // Vector con los jugadores - std::vector> balloons_; // Vector con los globos std::vector> bullets_; // Vector con las balas std::vector> items_; // Vector con los items std::vector> smart_sprites_; // Vector con los smartsprites @@ -138,19 +133,16 @@ private: std::shared_ptr bullet_texture_; // Textura para las balas std::vector> item_textures_; // Vector con las texturas de los items - std::vector> balloon_textures_; // Vector con las texturas de los globos - std::vector> explosions_textures_; // Vector con las texturas de las explosiones std::vector>> player_textures_; // Vector con todas las texturas de los jugadores; std::vector> game_text_textures_; // Vector con las texturas para los sprites con textos std::vector> item_animations_; // Vector con las animaciones de los items std::vector> player_animations_; // Vector con las animaciones del jugador - std::vector> balloon_animations_; // Vector con las animaciones de los globos - std::vector> explosions_animations_; // Vector con las animaciones de las explosiones - std::unique_ptr fade_; // Objeto para renderizar fades - std::vector paths_; // Vector con los recorridos precalculados almacenados + std::unique_ptr fade_; // Objeto para renderizar fades + std::unique_ptr balloon_manager_; // Objeto para gestionar los globos + std::vector paths_; // Vector con los recorridos precalculados almacenados // Variables HiScoreEntry hi_score_ = HiScoreEntry( @@ -165,20 +157,11 @@ private: bool coffee_machine_enabled_ = false; // Indica si hay una máquina de café en el terreno de juego bool hi_score_achieved_ = false; // Indica si se ha superado la puntuación máxima bool paused_ = false; // Indica si el juego está pausado (no se deberia de poder utilizar en el modo arcade) - bool power_ball_enabled_ = false; // Indica si hay una powerball ya activa - float balloon_speed_; // Velocidad a la que se mueven los enemigos - float default_balloon_speed_; // Velocidad base de los enemigos, sin incrementar float difficulty_score_multiplier_; // Multiplicador de puntos en función de la dificultad - int balloon_deploy_counter_ = 0; // Cuando se lanza una formación, se le da un valor y no sale otra hasta que llegue a cero - int balloons_popped_ = 0; // Lleva la cuenta de los globos explotados int counter_ = 0; // Contador para el juego int current_power_ = 0; // Poder actual almacenado para completar la fase int game_completed_counter_ = 0; // Contador para el tramo final, cuando se ha completado la partida y ya no aparecen más enemigos int game_over_counter_ = GAME_OVER_COUNTER_; // Contador para el estado de fin de partida - int last_balloon_deploy_ = 0; // Guarda cual ha sido la última formación desplegada para no repetir; - int menace_current_ = 0; // Nivel de amenaza actual - int menace_threshold_ = 0; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos - int power_ball_counter_ = 0; // Contador de formaciones enemigas entre la aparicion de una PowerBall y otra int time_stopped_counter_ = 0; // Temporizador para llevar la cuenta del tiempo detenido int total_power_to_complete_game_; // La suma del poder necesario para completar todas las fases GameState state_ = GameState::PLAYING; // Estado @@ -198,9 +181,6 @@ private: // Asigna texturas y animaciones void setResources(); - // Crea una formación de enemigos - void deployBalloonFormation(); - // Aumenta el poder de la fase void increaseStageCurrentPower(int power); @@ -342,9 +322,6 @@ private: // Actualiza y comprueba el valor de la variable void updateTimeStopped(); - // Gestiona el nivel de amenaza - void updateMenace(); - // Actualiza el fondo void updateBackground();