408 lines
14 KiB
C++
408 lines
14 KiB
C++
#include "balloon_manager.h"
|
|
#include <stdlib.h> // Para rand
|
|
#include <algorithm> // Para remove_if
|
|
#include <numeric> // 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<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();
|
|
}
|
|
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
|
|
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)
|
|
{
|
|
if (can_deploy_balloons_)
|
|
{
|
|
const int index = static_cast<int>(size);
|
|
balloons_.emplace_back(std::make_shared<Balloon>(x, y, type, size, velx, speed, creation_timer, play_area_, balloon_textures_.at(index), balloon_animations_.at(index)));
|
|
return balloons_.back();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// Crea un globo a partir de otro globo
|
|
void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction)
|
|
{
|
|
if (can_deploy_balloons_)
|
|
{
|
|
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);
|
|
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()
|
|
{
|
|
if (can_deploy_balloons_)
|
|
{
|
|
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<Balloon>(x[luck], pos_y, BalloonType::POWERBALL, BalloonSize::SIZE4, vx[luck], balloon_speed_, creation_time, play_area_, balloon_textures_[4], balloon_animations_[4]));
|
|
balloons_.back()->setInvulnerable(true);
|
|
|
|
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> 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<int>(balloon->getSize()));
|
|
balloon->disable();
|
|
}
|
|
|
|
return score;
|
|
}
|
|
|
|
// Explosiona un globo. Lo destruye = no crea otros globos
|
|
int 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;
|
|
}
|
|
|
|
// Aumenta el poder de la fase
|
|
Stage::addPower(balloon->getPower());
|
|
|
|
// Destruye el globo
|
|
explosions_->add(balloon->getPosX(), balloon->getPosY(), static_cast<int>(balloon->getSize()));
|
|
balloon->disable();
|
|
|
|
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("power_ball_explosion.wav"));
|
|
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() % param.game.game_area.rect.w) - BALLOON_SIZE[3];
|
|
const int y = param.game.game_area.rect.y + (rand() % 50);
|
|
const BalloonSize size = static_cast<BalloonSize>(rand() % 4);
|
|
const float vel_x = (rand() % 2 == 0) ? BALLOON_VELX_POSITIVE : BALLOON_VELX_NEGATIVE;
|
|
const int creation_counter = 0;
|
|
createBalloon(x, y, BalloonType::BALLOON, size, vel_x, balloon_speed_, 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); });
|
|
}
|
|
|
|
// Establece el sonido de los globos
|
|
void BalloonManager::setSounds(bool value)
|
|
{
|
|
for (auto &balloon : balloons_)
|
|
{
|
|
balloon->setSound(value);
|
|
}
|
|
} |