#include "balloon.h" #include // Para clamp #include // Para fabs #include "animated_sprite.h" // Para AnimatedSprite #include "param.h" // Para Param, param, ParamBalloon, ParamGame #include "sprite.h" // Para Sprite #include "texture.h" // Para Texture #include "resource.h" #include "audio.h" // Constructor Balloon::Balloon(float x, float y, BalloonType type, BalloonSize size, float vel_x, float speed, Uint16 creation_timer, SDL_FRect play_area, std::shared_ptr texture, const std::vector &animation) : sprite_(std::make_unique(texture, animation)), x_(x), y_(y), vx_(vel_x), being_created_(creation_timer > 0), invulnerable_(creation_timer > 0), stopped_(creation_timer > 0), creation_counter_(creation_timer), creation_counter_ini_(creation_timer), type_(type), size_(size), speed_(speed), play_area_(play_area) { switch (type_) { case BalloonType::BALLOON: { vy_ = 0; max_vy_ = 3.0f; const int index = static_cast(size_); gravity_ = param.balloon.settings.at(index).grav; default_vy_ = param.balloon.settings.at(index).vel; h_ = w_ = BALLOON_SIZE[index]; power_ = BALLOON_POWER[index]; menace_ = BALLOON_MENACE[index]; score_ = BALLOON_SCORE[index]; bouncing_sound_ = BALLOON_BOUNCING_SOUND[index]; popping_sound_ = BALLOON_POPPING_SOUND[index]; break; } case BalloonType::FLOATER: { default_vy_ = max_vy_ = vy_ = fabs(vx_ * 2.0f); gravity_ = 0.00f; const int index = static_cast(size_); h_ = w_ = BALLOON_SIZE[index]; power_ = BALLOON_POWER[index]; menace_ = BALLOON_MENACE[index]; score_ = BALLOON_SCORE[index]; bouncing_sound_ = BALLOON_BOUNCING_SOUND[index]; popping_sound_ = BALLOON_POPPING_SOUND[index]; break; } case BalloonType::POWERBALL: { constexpr int index = 3; h_ = w_ = BALLOON_SIZE[4]; bouncing_sound_ = BALLOON_BOUNCING_SOUND[3]; popping_sound_ = "power_ball_explosion.wav"; power_ = score_ = menace_ = 0; vy_ = 0; max_vy_ = 3.0f; gravity_ = param.balloon.settings.at(index).grav; default_vy_ = param.balloon.settings.at(index).vel; sprite_->setRotate(creation_timer <= 0); sprite_->setRotateAmount(vx_ > 0.0f ? 2.0 : -2.0); break; } default: break; } // Configura el sprite sprite_->setWidth(w_); sprite_->setHeight(h_); shiftSprite(); // Alinea el circulo de colisión con el objeto collider_.r = w_ / 2; shiftColliders(); // Establece la animación a usar setAnimation(); } // Centra el globo en la posición X void Balloon::alignTo(int x) { x_ = static_cast(x - (w_ / 2)); const int min_x = play_area_.x; const int max_x = play_area_.w - w_; x_ = std::clamp(x_, static_cast(min_x), static_cast(max_x)); } // Pinta el globo en la pantalla void Balloon::render() { if (type_ == BalloonType::POWERBALL) { // Renderiza el fondo azul { auto sp = std::make_unique(sprite_->getTexture(), sprite_->getPosition()); sp->setSpriteClip(0, 0, BALLOON_SIZE[4], BALLOON_SIZE[4]); sp->render(); } // Renderiza la estrella if (!invulnerable_) { SDL_FPoint p = {24.0f, 24.0f}; sprite_->setRotatingCenter(p); sprite_->render(); } // Añade la máscara del borde y los reflejos { auto sp = std::make_unique(sprite_->getTexture(), sprite_->getPosition()); sp->setSpriteClip(BALLOON_SIZE[4] * 2, 0, BALLOON_SIZE[4], BALLOON_SIZE[4]); sp->render(); } } else { // Renderizado para el resto de globos if (isBeingCreated()) { // Renderizado con transparencia sprite_->getTexture()->setAlpha(255 - (int)((float)creation_counter_ * (255.0f / (float)creation_counter_ini_))); sprite_->render(); sprite_->getTexture()->setAlpha(255); } else { // Renderizado normal sprite_->render(); } } } // Actualiza la posición y estados del globo void Balloon::move() { // Comprueba si se puede mover if (!isStopped()) { // Mueve el globo en horizontal x_ += vx_ * speed_; // Colisión en las partes laterales de la zona de juego const int clip = 2; const float min_x = play_area_.x - clip; const float max_x = play_area_.x + play_area_.w - w_ + clip; if (x_ < min_x || x_ > max_x) { if (bouncing_sound_enabled_) playSound(bouncing_sound_); x_ = std::clamp(x_, min_x, max_x); vx_ = -vx_; // Activa el efecto de rebote o invierte la rotación if (type_ == BalloonType::POWERBALL) { sprite_->switchRotate(); } else { enableBounce(); } } // Mueve el globo en vertical y_ += vy_ * speed_; // Colisión en la parte superior solo si el globo va de subida if (vy_ < 0) { const int min_y = play_area_.y; if (y_ < min_y) { if (bouncing_sound_enabled_) playSound(bouncing_sound_); y_ = min_y; vy_ = -vy_; enableBounce(); } } // Colisión en la parte inferior de la zona de juego const int max_y = play_area_.y + play_area_.h - h_; if (y_ > max_y) { if (bouncing_sound_enabled_) playSound(bouncing_sound_); y_ = max_y; vy_ = -default_vy_; if (type_ != BalloonType::POWERBALL) { enableBounce(); } else { setInvulnerable(false); } } /* Para aplicar la gravedad, el diseño original la aplicaba en cada iteración del bucle Al añadir el modificador de velocidad se reduce la distancia que recorre el objeto y por tanto recibe mas gravedad. Para solucionarlo se va a aplicar la gravedad cuando se haya recorrido una distancia igual a la velocidad en Y, que era el cálculo inicial */ // Incrementa la variable que calcula la distancia acumulada en Y travel_y_ += speed_; // Si la distancia acumulada en Y es igual a la velocidad, se aplica la gravedad if (travel_y_ >= 1.0f) { // Quita el excedente travel_y_ -= 1.0f; // Aplica la gravedad al objeto sin pasarse de una velocidad máxima vy_ += gravity_; } } } // Actualiza al globo a su posicion, animación y controla los contadores void Balloon::update() { move(); updateState(); updateBounce(); shiftSprite(); shiftColliders(); sprite_->update(); ++counter_; } // Actualiza los estados del globo void Balloon::updateState() { // Si se está creando if (isBeingCreated()) { // Actualiza el valor de las variables stop(); setInvulnerable(true); if (creation_counter_ > 0) { // Desplaza lentamente el globo hacia abajo y hacia un lado if (creation_counter_ % 10 == 0) { y_++; x_ += vx_; // Comprueba no se salga por los laterales const int min_x = play_area_.x; const int max_x = play_area_.w - w_; if (x_ < min_x || x_ > max_x) { // Corrige y cambia el sentido de la velocidad x_ -= vx_; vx_ = -vx_; } } --creation_counter_; } else { // El contador ha llegado a cero being_created_ = false; start(); setInvulnerable(false); setAnimation(); } } } // Establece la animación correspondiente al estado void Balloon::setAnimation() { std::string creating_animation; std::string normal_animation; switch (type_) { case BalloonType::POWERBALL: creating_animation = "powerball"; normal_animation = "powerball"; break; case BalloonType::FLOATER: creating_animation = param.balloon.color.at(2); normal_animation = param.balloon.color.at(3); break; default: creating_animation = param.balloon.color.at(0); normal_animation = param.balloon.color.at(1); break; } // Establece el frame de animación if (use_reversed_colors_) sprite_->setCurrentAnimation(creating_animation); else sprite_->setCurrentAnimation(isBeingCreated() ? creating_animation : normal_animation); } // Detiene el globo void Balloon::stop() { stopped_ = true; if (isPowerBall()) { sprite_->setRotate(!stopped_); } } // Pone el globo en movimiento void Balloon::start() { stopped_ = false; if (isPowerBall()) { sprite_->setRotate(!stopped_); } } // Alinea el circulo de colisión con la posición del objeto globo void Balloon::shiftColliders() { collider_.x = static_cast(x_) + collider_.r; collider_.y = static_cast(y_) + collider_.r; } // Alinea el sprite con la posición del objeto globo void Balloon::shiftSprite() { sprite_->setPosX(x_); sprite_->setPosY(y_); } // Establece el nivel de zoom del sprite void Balloon::zoomSprite() { sprite_->setZoomW(bouncing_.zoomW); sprite_->setZoomH(bouncing_.zoomH); } // Activa el efecto void Balloon::enableBounce() { bouncing_.enabled = true; bouncing_.reset(); zoomSprite(); } // Detiene el efecto void Balloon::disableBounce() { bouncing_.enabled = false; bouncing_.reset(); zoomSprite(); } // Aplica el efecto void Balloon::updateBounce() { if (bouncing_.enabled) { const int index = bouncing_.counter / bouncing_.speed; bouncing_.zoomW = bouncing_.w[index]; bouncing_.zoomH = bouncing_.h[index]; zoomSprite(); if (++bouncing_.counter / bouncing_.speed >= MAX_BOUNCE) { disableBounce(); } } } // Pone el color alternativo en el globo void Balloon::useReverseColor() { if (!isBeingCreated()) { use_reversed_colors_ = true; setAnimation(); } } // Pone el color normal en el globo void Balloon::useNormalColor() { use_reversed_colors_ = false; setAnimation(); } // Reproduce sonido void Balloon::playSound(const std::string &name) { if (!sound_enabled_) return; static auto audio = Audio::get(); audio->playSound(name); } // Explota el globo void Balloon::pop(bool should_sound) { if (should_sound) { if (poping_sound_enabled_) playSound(popping_sound_); } enabled_ = false; }