Files
coffee_crisis_arcade_edition/source/balloon_manager.cpp

377 lines
15 KiB
C++

#include "balloon_manager.h"
#include <algorithm> // Para remove_if
#include <array>
#include <cstdlib> // Para rand
#include <numeric> // 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_interface.h" // Para IStageInfo
#include "utils.h"
// Constructor
BalloonManager::BalloonManager(IStageInfo *stage_info)
: explosions_(std::make_unique<Explosions>()),
balloon_formations_(std::make_unique<BalloonFormations>()),
stage_info_(stage_info) { init(); }
// Inicializa
void BalloonManager::init() {
// Limpia
balloon_textures_.clear();
balloon_animations_.clear();
explosions_textures_.clear();
explosions_animations_.clear();
// 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_.at(0), explosions_animations_.at(0));
explosions_->addTexture(1, explosions_textures_.at(1), explosions_animations_.at(1));
explosions_->addTexture(2, explosions_textures_.at(2), explosions_animations_.at(2));
explosions_->addTexture(3, explosions_textures_.at(3), explosions_animations_.at(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 globos
void BalloonManager::deployRandomFormation(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())) {
createPowerBall(); // Crea una powerball
balloon_deploy_counter_ = 10; // Da un poco de margen para que se creen mas globos
} else {
// Decrementa el contador de despliegues de globos necesarios para la siguiente PowerBall
if (power_ball_counter_ > 0) {
--power_ball_counter_;
}
// Elige una formación enemiga la azar
const auto NUM_FORMATIONS = balloon_formations_->getPoolSize(stage);
int formation_id = rand() % NUM_FORMATIONS;
// Evita repetir la ultima formación enemiga desplegada
if (formation_id == last_balloon_deploy_) {
++formation_id %= NUM_FORMATIONS;
}
last_balloon_deploy_ = formation_id;
// Crea los globos de la formación
const auto BALLOONS = balloon_formations_->getFormationFromPool(stage, formation_id).balloons;
for (auto balloon : BALLOONS) {
createBalloon(balloon.x, balloon.y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, (creation_time_enabled_) ? balloon.creation_counter : 0);
}
// Reinicia el contador para el próximo despliegue
balloon_deploy_counter_ = DEFAULT_BALLOON_DEPLOY_COUNTER;
}
}
}
// Crea una formación de globos específica
void BalloonManager::deployFormation(int formation_id) {
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
for (auto balloon : BALLOONS) {
createBalloon(balloon.x, balloon.y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, balloon.creation_counter);
}
}
// Crea una formación de globos específica a una altura determinada
void BalloonManager::deployFormation(int formation_id, int y) {
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
for (auto balloon : BALLOONS) {
createBalloon(balloon.x, y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, balloon.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<Balloon> {
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)));
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> &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<Balloon::Size>(static_cast<int>(balloon->getSize()) - 1);
const int PARENT_HEIGHT = balloon->getHeight();
const int CHILD_HEIGHT = Balloon::WIDTH.at(static_cast<int>(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::WIDTH.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::WIDTH.at(4) / 2);
const float RIGHT = param.game.play_area.rect.w - Balloon::WIDTH.at(4);
const int LUCK = rand() % VALUES;
const std::array<float, VALUES> POS_X = {LEFT, LEFT, CENTER, CENTER, RIGHT, RIGHT};
const std::array<float, VALUES> 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<Balloon>(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> balloon) -> int {
stage_info_->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<int>(balloon->getSize()));
balloon->pop(true);
}
return score;
}
// Explosiona un globo. Lo destruye = no crea otros globos
auto BalloonManager::destroyBalloon(std::shared_ptr<Balloon> &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_info_->addPower(balloon->getPower());
// Destruye el globo
explosions_->add(balloon->getPosX(), balloon->getPosY(), static_cast<int>(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() {
deployFormation(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<int>(param.game.game_area.rect.w)) - Balloon::WIDTH.at(3);
const int Y = param.game.game_area.rect.y + (rand() % 50);
const auto SIZE = static_cast<Balloon::Size>(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);
}
}