#include "game/entities/balloon.h" #include // for std::fabs #include "core/rendering/animatedsprite.h" // for AnimatedSprite #include "core/rendering/movingsprite.h" // for MovingSprite #include "core/rendering/sprite.h" // for Sprite #include "core/rendering/texture.h" // for Texture #include "game/defaults.hpp" // for PLAY_AREA_LEFT, PLAY_AREA_RIGHT, PLAY_AR... // Constructor Balloon::Balloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 creationtimer, Texture *texture, std::vector *animation, SDL_Renderer *renderer) { sprite_ = new AnimatedSprite(texture, renderer, "", animation); disable(); enabled_ = true; switch (kind) { case BALLOON_1: // Alto y ancho del objeto width_ = BALLOON_WIDTH_1; height_ = BALLOON_WIDTH_1; size_ = BALLOON_SIZE_1; power_ = 1; // Inicializa los valores de velocidad y gravedad this->vel_x_ = velx; vel_y_ = 0; max_vel_y_ = 3.0F; gravity_ = 0.09F; default_vel_y_ = 2.6F; // Puntos que da el globo al ser destruido score_ = BALLOON_SCORE_1; // Amenaza que genera el globo menace_ = 1; break; case BALLOON_2: // Alto y ancho del objeto width_ = BALLOON_WIDTH_2; height_ = BALLOON_WIDTH_2; size_ = BALLOON_SIZE_2; power_ = 3; // Inicializa los valores de velocidad y gravedad this->vel_x_ = velx; vel_y_ = 0; max_vel_y_ = 3.0F; gravity_ = 0.10F; default_vel_y_ = 3.5F; // Puntos que da el globo al ser destruido score_ = BALLOON_SCORE_2; // Amenaza que genera el globo menace_ = 2; break; case BALLOON_3: // Alto y ancho del objeto width_ = BALLOON_WIDTH_3; height_ = BALLOON_WIDTH_3; size_ = BALLOON_SIZE_3; power_ = 7; // Inicializa los valores de velocidad y gravedad this->vel_x_ = velx; vel_y_ = 0; max_vel_y_ = 3.0F; gravity_ = 0.10F; default_vel_y_ = 4.50F; // Puntos que da el globo al ser destruido score_ = BALLOON_SCORE_3; // Amenaza que genera el globo menace_ = 4; break; case BALLOON_4: // Alto y ancho del objeto width_ = BALLOON_WIDTH_4; height_ = BALLOON_WIDTH_4; size_ = BALLOON_SIZE_4; power_ = 15; // Inicializa los valores de velocidad y gravedad this->vel_x_ = velx; vel_y_ = 0; max_vel_y_ = 3.0F; gravity_ = 0.10F; default_vel_y_ = 4.95F; // Puntos que da el globo al ser destruido score_ = BALLOON_SCORE_4; // Amenaza que genera el globo menace_ = 8; break; case HEXAGON_1: // Alto y ancho del objeto width_ = BALLOON_WIDTH_1; height_ = BALLOON_WIDTH_1; size_ = BALLOON_SIZE_1; power_ = 1; // Inicializa los valores de velocidad y gravedad this->vel_x_ = velx; vel_y_ = std::fabs(velx) * 2; max_vel_y_ = std::fabs(velx) * 2; gravity_ = 0.00F; default_vel_y_ = std::fabs(velx) * 2; // Puntos que da el globo al ser destruido score_ = BALLOON_SCORE_1; // Amenaza que genera el globo menace_ = 1; break; case HEXAGON_2: // Alto y ancho del objeto width_ = BALLOON_WIDTH_2; height_ = BALLOON_WIDTH_2; size_ = BALLOON_SIZE_2; power_ = 3; // Inicializa los valores de velocidad y gravedad this->vel_x_ = velx; vel_y_ = std::fabs(velx) * 2; max_vel_y_ = std::fabs(velx) * 2; gravity_ = 0.00F; default_vel_y_ = std::fabs(velx) * 2; // Puntos que da el globo al ser destruido score_ = BALLOON_SCORE_2; // Amenaza que genera el globo menace_ = 2; break; case HEXAGON_3: // Alto y ancho del objeto width_ = BALLOON_WIDTH_3; height_ = BALLOON_WIDTH_3; size_ = BALLOON_SIZE_3; power_ = 7; // Inicializa los valores de velocidad y gravedad this->vel_x_ = velx; vel_y_ = std::fabs(velx) * 2; max_vel_y_ = std::fabs(velx) * 2; gravity_ = 0.00F; default_vel_y_ = std::fabs(velx) * 2; // Puntos que da el globo al ser destruido score_ = BALLOON_SCORE_3; // Amenaza que genera el globo menace_ = 4; break; case HEXAGON_4: // Alto y ancho del objeto width_ = BALLOON_WIDTH_4; height_ = BALLOON_WIDTH_4; size_ = BALLOON_SIZE_4; power_ = 15; // Inicializa los valores de velocidad y gravedad this->vel_x_ = velx; vel_y_ = std::fabs(velx) * 2; max_vel_y_ = std::fabs(velx) * 2; gravity_ = 0.00F; default_vel_y_ = std::fabs(velx) * 2; // Puntos que da el globo al ser destruido score_ = BALLOON_SCORE_4; // Amenaza que genera el globo menace_ = 8; break; case POWER_BALL: // Alto y ancho del objeto width_ = BALLOON_WIDTH_4; height_ = BALLOON_WIDTH_4; size_ = 4; power_ = 0; // Inicializa los valores de velocidad y gravedad this->vel_x_ = velx; vel_y_ = 0; max_vel_y_ = 3.0F; gravity_ = 0.10F; default_vel_y_ = 4.95F; // Puntos que da el globo al ser destruido score_ = 0; // Amenaza que genera el globo menace_ = 0; // Añade rotación al sprite sprite_->setRotate(false); sprite_->setRotateSpeed(0); if (vel_x_ > 0.0F) { sprite_->setRotateAmount(2.0); } else { sprite_->setRotateAmount(-2.0); } break; default: break; } // Posición inicial pos_x_ = x; pos_y_ = y; // Valores para el efecto de rebote bouncing_.enabled = false; bouncing_.counter = 0; bouncing_.speed = 2; bouncing_.zoom_width = 1.0F; bouncing_.zoom_height = 1.0F; bouncing_.desp_x = 0.0F; bouncing_.desp_y = 0.0F; bouncing_.w = {1.10F, 1.05F, 1.00F, 0.95F, 0.90F, 0.95F, 1.00F, 1.02F, 1.05F, 1.02F}; bouncing_.h = {0.90F, 0.95F, 1.00F, 1.05F, 1.10F, 1.05F, 1.00F, 0.98F, 0.95F, 0.98F}; // Alto y ancho del sprite sprite_->setWidth(width_); sprite_->setHeight(height_); // Posición X,Y del sprite sprite_->setPosX((int)pos_x_); sprite_->setPosY((int)pos_y_); // Tamaño del circulo de colisión collider_.r = width_ / 2; // Alinea el circulo de colisión con el objeto updateColliders(); // Inicializa variables stopped_ = true; stopped_counter_ = 0; blinking_ = false; visible_ = true; creation_counter_ = creationtimer; creation_counter_ini_ = creationtimer; popping_ = false; // Valores iniciales dependentes del timer being_created_ = creation_counter_ != 0; invulnerable_ = being_created_; counter_ = 0; travel_y_ = 1.0F; this->speed_ = speed; // Tipo this->kind_ = kind; } // Destructor Balloon::~Balloon() { delete sprite_; } // Centra el globo en la posición X void Balloon::allignTo(int x) { pos_x_ = float(x - (width_ / 2)); if (pos_x_ < PLAY_AREA_LEFT) { pos_x_ = PLAY_AREA_LEFT + 1; } else if ((pos_x_ + width_) > PLAY_AREA_RIGHT) { pos_x_ = float(PLAY_AREA_RIGHT - width_ - 1); } // Posición X,Y del sprite sprite_->setPosX(getPosX()); sprite_->setPosY(getPosY()); // Alinea el circulo de colisión con el objeto updateColliders(); } // Pinta el globo en la pantalla void Balloon::render() { if ((visible_) && (enabled_)) { if (bouncing_.enabled) { if (kind_ != POWER_BALL) { // Aplica desplazamiento para el zoom sprite_->setPosX(getPosX() + bouncing_.desp_x); sprite_->setPosY(getPosY() + bouncing_.desp_y); sprite_->render(); sprite_->setPosX(getPosX() - bouncing_.desp_x); sprite_->setPosY(getPosY() - bouncing_.desp_y); } } else if (isBeingCreated()) { // Aplica alpha blending sprite_->getTexture()->setAlpha(255 - (int)((float)creation_counter_ * (255.0F / (float)creation_counter_ini_))); sprite_->render(); if (kind_ == POWER_BALL) { auto *sp = new Sprite(sprite_->getRect(), sprite_->getTexture(), sprite_->getRenderer()); sp->setSpriteClip(407, 0, 37, 37); sp->render(); delete sp; } sprite_->getTexture()->setAlpha(255); } else { sprite_->render(); if (kind_ == POWER_BALL and !popping_) { auto *sp = new Sprite(sprite_->getRect(), sprite_->getTexture(), sprite_->getRenderer()); sp->setSpriteClip(407, 0, 37, 37); sp->render(); delete sp; } } } } // Actualiza la posición y estados del globo void Balloon::move() { // Comprueba si se puede mover if (!isStopped()) { // Lo mueve a izquierda o derecha pos_x_ += (vel_x_ * speed_); // Si queda fuera de pantalla, corregimos su posición y cambiamos su sentido if ((pos_x_ < PLAY_AREA_LEFT) || (pos_x_ + width_ > PLAY_AREA_RIGHT)) { // Corrige posición pos_x_ -= (vel_x_ * speed_); // Invierte sentido vel_x_ = -vel_x_; // Invierte la rotación sprite_->switchRotate(); // Activa el efecto de rebote if (kind_ != POWER_BALL) { bounceStart(); } } // Mueve el globo hacia arriba o hacia abajo pos_y_ += (vel_y_ * speed_); // Si se sale por arriba if (pos_y_ < PLAY_AREA_TOP) { // Corrige pos_y_ = PLAY_AREA_TOP; // Invierte sentido vel_y_ = -vel_y_; // Activa el efecto de rebote if (kind_ != POWER_BALL) { bounceStart(); } } // Si el globo se sale por la parte inferior if (pos_y_ + height_ > PLAY_AREA_BOTTOM) { // Corrige pos_y_ = PLAY_AREA_BOTTOM - height_; // Invierte colocando una velocidad por defecto vel_y_ = -default_vel_y_; // Activa el efecto de rebote if (kind_ != POWER_BALL) { bounceStart(); } } /* 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 vel_y_ += gravity_; // Al parecer esta asignación se quedó sin hacer y ahora el juego no funciona // correctamente si se aplica, así que se deja sin efecto // velY = std::min(velY, maxVelY); } // Actualiza la posición del sprite sprite_->setPosX(getPosX()); sprite_->setPosY(getPosY()); } } // Deshabilita el globo y pone a cero todos los valores void Balloon::disable() { being_created_ = false; blinking_ = false; collider_.r = 0; collider_.x = 0; collider_.y = 0; counter_ = 0; creation_counter_ = 0; creation_counter_ini_ = 0; default_vel_y_ = 0.0F; enabled_ = false; gravity_ = 0.0F; height_ = 0; invulnerable_ = false; kind_ = 0; max_vel_y_ = 0.0F; menace_ = 0; popping_ = false; pos_x_ = 0.0F; pos_y_ = 0.0F; power_ = 0; score_ = 0; size_ = 0; speed_ = 0; stopped_ = false; stopped_counter_ = 0; travel_y_ = 0; vel_x_ = 0.0F; vel_y_ = 0.0F; visible_ = false; width_ = 0; sprite_->clear(); } // Explosiona el globo void Balloon::pop() { setPopping(true); sprite_->disableRotate(); setStop(true); setStoppedTimer(2000); setInvulnerable(true); menace_ = 0; } // Actualiza al globo a su posicion, animación y controla los contadores void Balloon::update() { if (enabled_) { sprite_->MovingSprite::update(); move(); updateAnimation(); updateColliders(); updateState(); updateBounce(); counter_++; } } // Actualiza los estados del globo void Balloon::updateState() { if (isPopping()) { updateStatePopping(); } if (isBeingCreated()) { updateStateBeingCreated(); } // Solo comprueba el estado detenido cuando no se está creando else if (isStopped()) { updateStateStopped(); } } // Rama de updateState: globo explotando void Balloon::updateStatePopping() { setInvulnerable(true); setStop(true); if (sprite_->animationIsCompleted()) { disable(); } } // Rama de updateState: globo creándose void Balloon::updateStateBeingCreated() { setStop(true); setInvulnerable(true); // Todavia tiene tiempo en el contador if (creation_counter_ > 0) { // Desplaza lentamente el globo hacia abajo y hacia un lado if (creation_counter_ % 10 == 0) { pos_y_++; pos_x_ += vel_x_; // Comprueba no se salga por los laterales if ((pos_x_ < PLAY_AREA_LEFT) || (pos_x_ > (PLAY_AREA_RIGHT - width_))) { // Corrige y cambia el sentido de la velocidad pos_x_ -= vel_x_; vel_x_ = -vel_x_; } // Actualiza la posición del sprite sprite_->setPosX(getPosX()); sprite_->setPosY(getPosY()); // Actualiza la posición del circulo de colisión updateColliders(); } creation_counter_--; } // El contador ha llegado a cero else { setBeingCreated(false); setStop(false); setVisible(true); setInvulnerable(false); if (kind_ == POWER_BALL) { sprite_->setRotate(true); } } } // Rama de updateState: globo detenido (no creándose) void Balloon::updateStateStopped() { // Si es una powerball deja de rodar if (kind_ == POWER_BALL) { sprite_->setRotate(false); } // Reduce el contador if (stopped_counter_ > 0) { stopped_counter_--; } // Quitarles el estado "detenido" si no estan explosionando else if (!isPopping()) { // Si es una powerball vuelve a rodar if (kind_ == POWER_BALL) { sprite_->setRotate(true); } setStop(false); } } // Establece la animación correspondiente al estado void Balloon::updateAnimation() { std::string creating_animation = "blue"; std::string normal_animation = "orange"; if (kind_ == POWER_BALL) { creating_animation = "powerball"; normal_animation = "powerball"; } else if (getClass() == HEXAGON_CLASS) { creating_animation = "red"; normal_animation = "green"; } // Establece el frame de animación if (isPopping()) { sprite_->setCurrentAnimation("pop"); } else if (isBeingCreated()) { sprite_->setCurrentAnimation(creating_animation); } else { sprite_->setCurrentAnimation(normal_animation); } sprite_->animate(); } // Comprueba si el globo está habilitado auto Balloon::isEnabled() const -> bool { return enabled_; } // Obtiene del valor de la variable auto Balloon::getPosX() const -> float { return pos_x_; } // Obtiene del valor de la variable auto Balloon::getPosY() const -> float { return pos_y_; } // Obtiene del valor de la variable auto Balloon::getVelY() const -> float { return vel_y_; } // Obtiene del valor de la variable auto Balloon::getWidth() const -> int { return width_; } // Obtiene del valor de la variable auto Balloon::getHeight() const -> int { return height_; } // Establece el valor de la variable void Balloon::setVelY(float vel_y) { this->vel_y_ = vel_y; } // Establece el valor de la variable void Balloon::setSpeed(float speed) { this->speed_ = speed; } // Obtiene del valor de la variable auto Balloon::getKind() const -> int { return kind_; } // Obtiene del valor de la variable auto Balloon::getSize() const -> Uint8 { return size_; } // Obtiene la clase a la que pertenece el globo auto Balloon::getClass() const -> Uint8 { if ((kind_ >= BALLOON_1) && (kind_ <= BALLOON_4)) { return BALLOON_CLASS; } if ((kind_ >= HEXAGON_1) && (kind_ <= HEXAGON_4)) { return HEXAGON_CLASS; } return BALLOON_CLASS; } // Establece el valor de la variable void Balloon::setStop(bool state) { stopped_ = state; } // Obtiene del valor de la variable auto Balloon::isStopped() const -> bool { return stopped_; } // Establece el valor de la variable void Balloon::setBlink(bool value) { blinking_ = value; } // Obtiene del valor de la variable auto Balloon::isBlinking() const -> bool { return blinking_; } // Establece el valor de la variable void Balloon::setVisible(bool value) { visible_ = value; } // Obtiene del valor de la variable auto Balloon::isVisible() const -> bool { return visible_; } // Establece el valor de la variable void Balloon::setInvulnerable(bool value) { invulnerable_ = value; } // Obtiene del valor de la variable auto Balloon::isInvulnerable() const -> bool { return invulnerable_; } // Establece el valor de la variable void Balloon::setBeingCreated(bool value) { being_created_ = value; } // Obtiene del valor de la variable auto Balloon::isBeingCreated() const -> bool { return being_created_; } // Establece el valor de la variable void Balloon::setPopping(bool value) { popping_ = value; } // Obtiene del valor de la variable auto Balloon::isPopping() const -> bool { return popping_; } // Establece el valor de la variable void Balloon::setStoppedTimer(Uint16 time) { stopped_counter_ = time; } // Obtiene del valor de la variable auto Balloon::getStoppedTimer() const -> Uint16 { return stopped_counter_; } // Obtiene del valor de la variable auto Balloon::getScore() const -> Uint16 { return score_; } // Obtiene el circulo de colisión auto Balloon::getCollider() -> Circle & { return collider_; } // Alinea el circulo de colisión con la posición del objeto globo void Balloon::updateColliders() { collider_.x = Uint16(pos_x_ + collider_.r); collider_.y = pos_y_ + collider_.r; } // Obtiene le valor de la variable auto Balloon::getMenace() const -> Uint8 { if (isEnabled()) { return menace_; } return 0; } // Obtiene le valor de la variable auto Balloon::getPower() const -> Uint8 { return power_; } void Balloon::bounceStart() { bouncing_.enabled = true; bouncing_.zoom_width = 1; bouncing_.zoom_height = 1; sprite_->setZoomW(bouncing_.zoom_width); sprite_->setZoomH(bouncing_.zoom_height); bouncing_.desp_x = 0; bouncing_.desp_y = 0; } void Balloon::bounceStop() { bouncing_.enabled = false; bouncing_.counter = 0; bouncing_.zoom_width = 1.0F; bouncing_.zoom_height = 1.0F; sprite_->setZoomW(bouncing_.zoom_width); sprite_->setZoomH(bouncing_.zoom_height); bouncing_.desp_x = 0.0F; bouncing_.desp_y = 0.0F; } void Balloon::updateBounce() { if (bouncing_.enabled) { bouncing_.zoom_width = bouncing_.w[bouncing_.counter / bouncing_.speed]; bouncing_.zoom_height = bouncing_.h[bouncing_.counter / bouncing_.speed]; sprite_->setZoomW(bouncing_.zoom_width); sprite_->setZoomH(bouncing_.zoom_height); bouncing_.desp_x = (sprite_->getSpriteClip().w - (sprite_->getSpriteClip().w * bouncing_.zoom_width)); bouncing_.desp_y = (sprite_->getSpriteClip().h - (sprite_->getSpriteClip().h * bouncing_.zoom_height)); bouncing_.counter++; if ((bouncing_.counter / bouncing_.speed) > (MAX_BOUNCE - 1)) { bounceStop(); } } }