976 lines
27 KiB
C++
976 lines
27 KiB
C++
#include "game/entities/balloon.h"
|
|
|
|
#include <algorithm> // for std::max
|
|
#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 (frame-based)
|
|
this->vel_x_ = velx;
|
|
vel_y_ = 0;
|
|
max_vel_y_ = 3.0F;
|
|
gravity_ = 0.09F;
|
|
default_vel_y_ = 2.6F;
|
|
|
|
// Time-based equivalents (x60 per vel, x3600 per gravetat)
|
|
vel_x_s_ = velx * 60.0F;
|
|
vel_y_s_ = 0.0F;
|
|
max_vel_y_s_ = 180.0F;
|
|
gravity_s_ = 324.0F;
|
|
default_vel_y_s_ = 156.0F;
|
|
|
|
// Puntos que da el globo al ser destruido
|
|
score_ = SCORE_1;
|
|
|
|
// Amenaza que genera el globo
|
|
menace_ = 1;
|
|
|
|
break;
|
|
|
|
case BALLOON_2:
|
|
width_ = WIDTH_2;
|
|
height_ = WIDTH_2;
|
|
size_ = SIZE_2;
|
|
power_ = 3;
|
|
|
|
this->vel_x_ = velx;
|
|
vel_y_ = 0;
|
|
max_vel_y_ = 3.0F;
|
|
gravity_ = 0.10F;
|
|
default_vel_y_ = 3.5F;
|
|
|
|
vel_x_s_ = velx * 60.0F;
|
|
vel_y_s_ = 0.0F;
|
|
max_vel_y_s_ = 180.0F;
|
|
gravity_s_ = 360.0F;
|
|
default_vel_y_s_ = 210.0F;
|
|
|
|
score_ = SCORE_2;
|
|
menace_ = 2;
|
|
break;
|
|
|
|
case BALLOON_3:
|
|
width_ = WIDTH_3;
|
|
height_ = WIDTH_3;
|
|
size_ = SIZE_3;
|
|
power_ = 7;
|
|
|
|
this->vel_x_ = velx;
|
|
vel_y_ = 0;
|
|
max_vel_y_ = 3.0F;
|
|
gravity_ = 0.10F;
|
|
default_vel_y_ = 4.50F;
|
|
|
|
vel_x_s_ = velx * 60.0F;
|
|
vel_y_s_ = 0.0F;
|
|
max_vel_y_s_ = 180.0F;
|
|
gravity_s_ = 360.0F;
|
|
default_vel_y_s_ = 270.0F;
|
|
|
|
score_ = SCORE_3;
|
|
menace_ = 4;
|
|
break;
|
|
|
|
case BALLOON_4:
|
|
width_ = WIDTH_4;
|
|
height_ = WIDTH_4;
|
|
size_ = SIZE_4;
|
|
power_ = 15;
|
|
|
|
this->vel_x_ = velx;
|
|
vel_y_ = 0;
|
|
max_vel_y_ = 3.0F;
|
|
gravity_ = 0.10F;
|
|
default_vel_y_ = 4.95F;
|
|
|
|
vel_x_s_ = velx * 60.0F;
|
|
vel_y_s_ = 0.0F;
|
|
max_vel_y_s_ = 180.0F;
|
|
gravity_s_ = 360.0F;
|
|
default_vel_y_s_ = 297.0F;
|
|
|
|
score_ = SCORE_4;
|
|
menace_ = 8;
|
|
break;
|
|
|
|
case HEXAGON_1:
|
|
width_ = WIDTH_1;
|
|
height_ = WIDTH_1;
|
|
size_ = SIZE_1;
|
|
power_ = 1;
|
|
|
|
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;
|
|
|
|
vel_x_s_ = velx * 60.0F;
|
|
vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
|
|
max_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
|
|
gravity_s_ = 0.0F;
|
|
default_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
|
|
|
|
score_ = SCORE_1;
|
|
menace_ = 1;
|
|
break;
|
|
|
|
case HEXAGON_2:
|
|
width_ = WIDTH_2;
|
|
height_ = WIDTH_2;
|
|
size_ = SIZE_2;
|
|
power_ = 3;
|
|
|
|
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;
|
|
|
|
vel_x_s_ = velx * 60.0F;
|
|
vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
|
|
max_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
|
|
gravity_s_ = 0.0F;
|
|
default_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
|
|
|
|
score_ = SCORE_2;
|
|
menace_ = 2;
|
|
break;
|
|
|
|
case HEXAGON_3:
|
|
width_ = WIDTH_3;
|
|
height_ = WIDTH_3;
|
|
size_ = SIZE_3;
|
|
power_ = 7;
|
|
|
|
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;
|
|
|
|
vel_x_s_ = velx * 60.0F;
|
|
vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
|
|
max_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
|
|
gravity_s_ = 0.0F;
|
|
default_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
|
|
|
|
score_ = SCORE_3;
|
|
menace_ = 4;
|
|
break;
|
|
|
|
case HEXAGON_4:
|
|
width_ = WIDTH_4;
|
|
height_ = WIDTH_4;
|
|
size_ = SIZE_4;
|
|
power_ = 15;
|
|
|
|
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;
|
|
|
|
vel_x_s_ = velx * 60.0F;
|
|
vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
|
|
max_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
|
|
gravity_s_ = 0.0F;
|
|
default_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
|
|
|
|
score_ = SCORE_4;
|
|
menace_ = 8;
|
|
break;
|
|
|
|
case POWER_BALL:
|
|
width_ = WIDTH_4;
|
|
height_ = WIDTH_4;
|
|
size_ = 4;
|
|
power_ = 0;
|
|
|
|
this->vel_x_ = velx;
|
|
vel_y_ = 0;
|
|
max_vel_y_ = 3.0F;
|
|
gravity_ = 0.10F;
|
|
default_vel_y_ = 4.95F;
|
|
|
|
vel_x_s_ = velx * 60.0F;
|
|
vel_y_s_ = 0.0F;
|
|
max_vel_y_s_ = 180.0F;
|
|
gravity_s_ = 360.0F;
|
|
default_vel_y_s_ = 297.0F;
|
|
|
|
// Puntos que da el globo al ser destruido
|
|
score_ = 0;
|
|
|
|
// Amenaza que genera el globo
|
|
menace_ = 0;
|
|
|
|
// Configura la rotació del sprite. L'activació/desactivació es
|
|
// delega a `setStop()`, que la sincronitza amb l'estat stopped_
|
|
// sempre que canvia. setRotateSpeed(1) evita la UB del `counter_
|
|
// % 0` dins MovingSprite::rotate().
|
|
sprite_->setRotateSpeed(1);
|
|
sprite_->setRotateAmount(vel_x_ > 0.0F ? 2.0 : -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;
|
|
stopped_counter_s_ = 0.0F;
|
|
blinking_ = false;
|
|
visible_ = true;
|
|
creation_counter_ = creationtimer;
|
|
creation_counter_ini_ = creationtimer;
|
|
creation_counter_s_ = static_cast<float>(creationtimer) / 60.0F;
|
|
creation_counter_ini_s_ = creation_counter_s_;
|
|
creation_phase_s_ = 0.0F;
|
|
bounce_phase_s_ = 0.0F;
|
|
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());
|
|
}
|
|
}
|
|
|
|
// Actualiza la posición y estados del globo (time-based).
|
|
// Reemplaça l'acumulador `travel_y_` per integració contínua: gravetat i
|
|
// posició s'apliquen escalades per `speed_` (tempo del joc) cada tick,
|
|
// equivalent en mitjana a l'original però sense l'efecte chunky.
|
|
void Balloon::move(float dt_s) {
|
|
if (!isStopped()) {
|
|
// Eix X
|
|
pos_x_ += vel_x_s_ * speed_ * dt_s;
|
|
|
|
if ((pos_x_ < PLAY_AREA_LEFT) || (pos_x_ + width_ > PLAY_AREA_RIGHT)) {
|
|
pos_x_ -= vel_x_s_ * speed_ * dt_s;
|
|
vel_x_s_ = -vel_x_s_;
|
|
vel_x_ = -vel_x_; // mantenir frame-based sincronitzat (UB-free per a sprite->switchRotate)
|
|
sprite_->switchRotate();
|
|
if (kind_ != POWER_BALL) { bounceStart(); }
|
|
}
|
|
|
|
// Eix Y
|
|
pos_y_ += vel_y_s_ * speed_ * dt_s;
|
|
|
|
if (pos_y_ < PLAY_AREA_TOP) {
|
|
pos_y_ = PLAY_AREA_TOP;
|
|
vel_y_s_ = -vel_y_s_;
|
|
vel_y_ = -vel_y_;
|
|
if (kind_ != POWER_BALL) { bounceStart(); }
|
|
}
|
|
|
|
if (pos_y_ + height_ > PLAY_AREA_BOTTOM) {
|
|
pos_y_ = PLAY_AREA_BOTTOM - height_;
|
|
vel_y_s_ = -default_vel_y_s_;
|
|
vel_y_ = -default_vel_y_;
|
|
if (kind_ != POWER_BALL) { bounceStart(); }
|
|
}
|
|
|
|
// Gravetat contínua (el tempo `speed_` escala també la gravetat per
|
|
// mantenir el mateix patró de rebot que la versió frame-based).
|
|
vel_y_s_ += gravity_s_ * speed_ * dt_s;
|
|
|
|
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;
|
|
creation_counter_s_ = 0.0F;
|
|
creation_counter_ini_s_ = 0.0F;
|
|
creation_phase_s_ = 0.0F;
|
|
bounce_phase_s_ = 0.0F;
|
|
default_vel_y_ = 0.0F;
|
|
default_vel_y_s_ = 0.0F;
|
|
enabled_ = false;
|
|
gravity_ = 0.0F;
|
|
gravity_s_ = 0.0F;
|
|
height_ = 0;
|
|
invulnerable_ = false;
|
|
kind_ = 0;
|
|
max_vel_y_ = 0.0F;
|
|
max_vel_y_s_ = 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;
|
|
stopped_counter_s_ = 0.0F;
|
|
travel_y_ = 0;
|
|
vel_x_ = 0.0F;
|
|
vel_y_ = 0.0F;
|
|
vel_x_s_ = 0.0F;
|
|
vel_y_s_ = 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 al globo (time-based)
|
|
void Balloon::update(float dt_s) {
|
|
if (enabled_) {
|
|
// MovingSprite::update(dt_s) avança la rotació (entre altres). La posició
|
|
// del sprite la posa move(dt_s) directament des de pos_x_/pos_y_.
|
|
sprite_->MovingSprite::update(dt_s);
|
|
move(dt_s);
|
|
updateAnimation(dt_s);
|
|
updateColliders();
|
|
updateState(dt_s);
|
|
updateBounce(dt_s);
|
|
}
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
}
|
|
|
|
// Actualiza los estados del globo (time-based)
|
|
void Balloon::updateState(float dt_s) {
|
|
if (isPopping()) {
|
|
updateStatePopping();
|
|
}
|
|
|
|
if (isBeingCreated()) {
|
|
updateStateBeingCreated(dt_s);
|
|
} else if (isStopped()) {
|
|
updateStateStopped(dt_s);
|
|
}
|
|
}
|
|
|
|
// 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); // reactiva la rotació de la PowerBall si escau
|
|
setVisible(true);
|
|
setInvulnerable(false);
|
|
}
|
|
}
|
|
|
|
// Rama de updateState: globo creándose (time-based). Manté el chunk pattern
|
|
// original: cada CREATION_STEP_S s'aplica un step de drift.
|
|
void Balloon::updateStateBeingCreated(float dt_s) {
|
|
setStop(true);
|
|
setInvulnerable(true);
|
|
|
|
if (creation_counter_s_ > 0.0F) {
|
|
creation_phase_s_ += dt_s;
|
|
while (creation_phase_s_ >= CREATION_STEP_S) {
|
|
creation_phase_s_ -= CREATION_STEP_S;
|
|
pos_y_ += 1.0F;
|
|
pos_x_ += vel_x_; // drift idèntic a l'original (vel_x_ en px/frame)
|
|
|
|
if ((pos_x_ < PLAY_AREA_LEFT) || (pos_x_ > (PLAY_AREA_RIGHT - width_))) {
|
|
pos_x_ -= vel_x_;
|
|
vel_x_ = -vel_x_;
|
|
vel_x_s_ = -vel_x_s_;
|
|
}
|
|
|
|
sprite_->setPosX(getPosX());
|
|
sprite_->setPosY(getPosY());
|
|
updateColliders();
|
|
}
|
|
|
|
creation_counter_s_ = std::max(0.0F, creation_counter_s_ - dt_s);
|
|
creation_counter_ = static_cast<Uint16>(creation_counter_s_ * 60.0F);
|
|
} else {
|
|
setBeingCreated(false);
|
|
setStop(false);
|
|
setVisible(true);
|
|
setInvulnerable(false);
|
|
}
|
|
}
|
|
|
|
// Rama de updateState: globo detenido (no creándose)
|
|
void Balloon::updateStateStopped() {
|
|
// Reduce el contador
|
|
if (stopped_counter_ > 0) {
|
|
stopped_counter_--;
|
|
}
|
|
// Quitarles el estado "detenido" si no estan explosionando
|
|
else if (!isPopping()) {
|
|
setStop(false); // reactiva la rotació de la PowerBall si escau
|
|
}
|
|
}
|
|
|
|
// Rama de updateState: globo detenido (time-based)
|
|
void Balloon::updateStateStopped(float dt_s) {
|
|
if (stopped_counter_s_ > 0.0F) {
|
|
stopped_counter_s_ = std::max(0.0F, stopped_counter_s_ - dt_s);
|
|
stopped_counter_ = static_cast<Uint16>(stopped_counter_s_ * 60.0F);
|
|
} else if (!isPopping()) {
|
|
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();
|
|
}
|
|
|
|
// Establece la animación correspondiente (time-based)
|
|
void Balloon::updateAnimation(float dt_s) {
|
|
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";
|
|
}
|
|
|
|
if (isPopping()) {
|
|
sprite_->setCurrentAnimation("pop");
|
|
} else if (isBeingCreated()) {
|
|
sprite_->setCurrentAnimation(creating_animation);
|
|
} else {
|
|
sprite_->setCurrentAnimation(normal_animation);
|
|
}
|
|
|
|
sprite_->animate(dt_s);
|
|
}
|
|
|
|
// 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;
|
|
this->vel_y_s_ = vel_y * 60.0F;
|
|
}
|
|
|
|
// 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. Per a la PowerBall sincronitza la
|
|
// rotació del sprite: stopped=true ⇒ no roda, stopped=false ⇒ roda. Així,
|
|
// vingui d'on vingui la crida (FSM intern, Game::stopAll/startAllBalloons,
|
|
// fi de creació, etc.), la rotació sempre queda coherent amb l'estat.
|
|
void Balloon::setStop(bool state) {
|
|
stopped_ = state;
|
|
if (kind_ == POWER_BALL) {
|
|
sprite_->setRotate(!stopped_);
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
stopped_counter_s_ = static_cast<float>(time) / 60.0F;
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Aplica l'efecte de rebote (time-based). Manté la mateixa cadència: cada
|
|
// `bouncing_.speed` "frames" (a 60Hz) avança un slot del vector w/h.
|
|
void Balloon::updateBounce(float dt_s) {
|
|
if (!bouncing_.enabled) { return; }
|
|
|
|
bounce_phase_s_ += dt_s;
|
|
const float STEP_S = static_cast<float>(bouncing_.speed) * BOUNCE_STEP_S;
|
|
while (bounce_phase_s_ >= STEP_S) {
|
|
bounce_phase_s_ -= STEP_S;
|
|
bouncing_.counter++;
|
|
}
|
|
|
|
const int IDX = bouncing_.counter / bouncing_.speed;
|
|
if (IDX > (MAX_BOUNCE - 1)) {
|
|
bounceStop();
|
|
bounce_phase_s_ = 0.0F;
|
|
return;
|
|
}
|
|
|
|
bouncing_.zoom_width = bouncing_.w[IDX];
|
|
bouncing_.zoom_height = bouncing_.h[IDX];
|
|
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));
|
|
} |