Files
coffee-crisis/source/game/entities/balloon.cpp
T

791 lines
21 KiB
C++

#include "game/entities/balloon.h"
#include <cmath> // 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, const std::vector<std::string> *animation, SDL_Renderer *renderer) {
sprite_ = new AnimatedSprite(texture, renderer, "", animation);
disable();
enabled_ = true;
switch (kind) {
case BALLOON_1:
// Alto y ancho del objeto
width_ = WIDTH_1;
height_ = WIDTH_1;
size_ = 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_ = SCORE_1;
// Amenaza que genera el globo
menace_ = 1;
break;
case BALLOON_2:
// Alto y ancho del objeto
width_ = WIDTH_2;
height_ = WIDTH_2;
size_ = 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_ = SCORE_2;
// Amenaza que genera el globo
menace_ = 2;
break;
case BALLOON_3:
// Alto y ancho del objeto
width_ = WIDTH_3;
height_ = WIDTH_3;
size_ = 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_ = SCORE_3;
// Amenaza que genera el globo
menace_ = 4;
break;
case BALLOON_4:
// Alto y ancho del objeto
width_ = WIDTH_4;
height_ = WIDTH_4;
size_ = 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_ = SCORE_4;
// Amenaza que genera el globo
menace_ = 8;
break;
case HEXAGON_1:
// Alto y ancho del objeto
width_ = WIDTH_1;
height_ = WIDTH_1;
size_ = 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_ = SCORE_1;
// Amenaza que genera el globo
menace_ = 1;
break;
case HEXAGON_2:
// Alto y ancho del objeto
width_ = WIDTH_2;
height_ = WIDTH_2;
size_ = 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_ = SCORE_2;
// Amenaza que genera el globo
menace_ = 2;
break;
case HEXAGON_3:
// Alto y ancho del objeto
width_ = WIDTH_3;
height_ = WIDTH_3;
size_ = 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_ = SCORE_3;
// Amenaza que genera el globo
menace_ = 4;
break;
case HEXAGON_4:
// Alto y ancho del objeto
width_ = WIDTH_4;
height_ = WIDTH_4;
size_ = 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_ = SCORE_4;
// Amenaza que genera el globo
menace_ = 8;
break;
case POWER_BALL:
// Alto y ancho del objeto
width_ = WIDTH_4;
height_ = 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();
}
}
}