Files
coffee_crisis_arcade_edition/source/balloon_manager.cpp

353 lines
12 KiB
C++

#include "balloon_manager.h"
#include "stage.h"
#include <numeric> // Para accumulate
#include <algorithm> // 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 "game.h"
#include "screen.h"
#include "explosions.h" // Para Explosions
#include "jail_audio.h"
// Constructor
BalloonManager::BalloonManager()
: explosions_(std::make_unique<Explosions>()),
balloon_formations_(std::make_unique<BalloonFormations>()) { 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();
}
}
// 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(game_.getCurrentStage()).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;
}
}
}
// 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<Balloon> BalloonManager::createBalloon(float x, int y, BalloonType type, BalloonSize size, float velx, float speed, int creation_timer)
{
const int index = static_cast<int>(size);
balloons_.emplace_back(std::make_shared<Balloon>(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> &balloon, const std::string &direction)
{
const float vx = direction == "LEFT" ? BALLOON_VELX_NEGATIVE : BALLOON_VELX_POSITIVE;
const auto lower_size = static_cast<BalloonSize>(static_cast<int>(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<Balloon>(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<float>(game_.getCurrentPower()) / balloon_formations_->getStage(game_.getCurrentStage()).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> balloon)
{
game_.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<int>(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> &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();
game_.increaseStageCurrentPower(power);
balloons_popped_ += power;
// Destruye el globo
explosions_->add(balloon->getPosX(), balloon->getPosY(), static_cast<int>(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();
}
}
// 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);
}
}