422 lines
9.8 KiB
C++
422 lines
9.8 KiB
C++
#include "balloon.h"
|
|
#include <algorithm> // Para clamp
|
|
#include <cmath> // 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
|
|
|
|
// Constructor
|
|
Balloon::Balloon(float x, float y, BalloonType type, BalloonSize size, float vel_x, float speed, Uint16 creation_timer, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation)
|
|
: sprite_(std::make_unique<AnimatedSprite>(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)
|
|
{
|
|
switch (type_)
|
|
{
|
|
case BalloonType::BALLOON:
|
|
{
|
|
vy_ = 0;
|
|
max_vy_ = 3.0f;
|
|
|
|
const int index = static_cast<int>(size_);
|
|
gravity_ = param.balloon.at(index).grav;
|
|
default_vy_ = param.balloon.at(index).vel;
|
|
h_ = w_ = BALLOON_SIZE[index];
|
|
power_ = BALLOON_POWER[index];
|
|
menace_ = BALLOON_MENACE[index];
|
|
score_ = BALLOON_SCORE[index];
|
|
|
|
break;
|
|
}
|
|
|
|
case BalloonType::FLOATER:
|
|
{
|
|
default_vy_ = max_vy_ = vy_ = fabs(vx_ * 2.0f);
|
|
gravity_ = 0.00f;
|
|
|
|
const int index = static_cast<int>(size_);
|
|
h_ = w_ = BALLOON_SIZE[index];
|
|
power_ = BALLOON_POWER[index];
|
|
menace_ = BALLOON_MENACE[index];
|
|
score_ = BALLOON_SCORE[index];
|
|
|
|
break;
|
|
}
|
|
|
|
case BalloonType::POWERBALL:
|
|
{
|
|
const int index = 3;
|
|
h_ = w_ = BALLOON_SIZE[4];
|
|
power_ = score_ = menace_ = 0;
|
|
|
|
vy_ = 0;
|
|
max_vy_ = 3.0f;
|
|
gravity_ = param.balloon.at(index).grav;
|
|
default_vy_ = param.balloon.at(index).vel;
|
|
|
|
sprite_->disableRotate();
|
|
sprite_->setRotateSpeed(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<float>(x - (w_ / 2));
|
|
const int min_x = param.game.play_area.rect.x;
|
|
const int max_x = param.game.play_area.rect.w - w_;
|
|
x_ = std::clamp(x_, static_cast<float>(min_x), static_cast<float>(max_x));
|
|
}
|
|
|
|
// Pinta el globo en la pantalla
|
|
void Balloon::render()
|
|
{
|
|
if (type_ == BalloonType::POWERBALL)
|
|
{
|
|
// Renderizado para la PowerBall
|
|
SDL_Point p = {24, 24};
|
|
sprite_->setRotatingCenter(&p);
|
|
sprite_->render();
|
|
// Añade la máscara del borde y los reflejos
|
|
auto sp = std::make_unique<Sprite>(sprite_->getTexture(), sprite_->getPosition());
|
|
sp->setSpriteClip(BALLOON_SIZE[4], 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
|
|
{
|
|
if (bouncing_.enabled)
|
|
{
|
|
// Renderizado con efecto de bouncing
|
|
sprite_->setPos(x_ + bouncing_.despX, y_ + bouncing_.despY);
|
|
sprite_->render();
|
|
// sprite_->setPos(x_ - bouncing_.despX, y_ - bouncing_.despY);
|
|
}
|
|
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 = param.game.play_area.rect.x - clip;
|
|
const float max_x = param.game.play_area.rect.w - w_ + clip;
|
|
if (x_ < min_x || x_ > max_x)
|
|
{
|
|
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 de la zona de juego excepto para la PowerBall
|
|
if (type_ != BalloonType::POWERBALL)
|
|
{
|
|
const int min_y = param.game.play_area.rect.y;
|
|
if (y_ < min_y)
|
|
{
|
|
y_ = min_y;
|
|
vy_ = -vy_;
|
|
enableBounce();
|
|
}
|
|
}
|
|
|
|
// Colisión en la parte inferior de la zona de juego
|
|
const int max_y = param.game.play_area.rect.h - h_;
|
|
if (y_ > max_y)
|
|
{
|
|
y_ = max_y;
|
|
vy_ = -default_vy_;
|
|
if (type_ != BalloonType::POWERBALL)
|
|
{
|
|
enableBounce();
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
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_;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Deshabilita el globo
|
|
void Balloon::disable() { enabled_ = false; }
|
|
|
|
// Explosiona el globo
|
|
void Balloon::pop() { disable(); }
|
|
|
|
// 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 = param.game.play_area.rect.x;
|
|
const int max_x = param.game.play_area.rect.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 = "red";
|
|
normal_animation = "green";
|
|
break;
|
|
default:
|
|
creating_animation = "blue";
|
|
normal_animation = "orange";
|
|
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 (type_ == BalloonType::POWERBALL)
|
|
sprite_->disableRotate();
|
|
}
|
|
|
|
// Pone el globo en movimiento
|
|
void Balloon::start()
|
|
{
|
|
stopped_ = false;
|
|
if (type_ == BalloonType::POWERBALL)
|
|
sprite_->enableRotate();
|
|
}
|
|
|
|
// Alinea el circulo de colisión con la posición del objeto globo
|
|
void Balloon::shiftColliders()
|
|
{
|
|
collider_.x = static_cast<int>(x_) + collider_.r;
|
|
collider_.y = static_cast<int>(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();
|
|
|
|
const auto spriteClip = sprite_->getSpriteClip();
|
|
bouncing_.despX = spriteClip.w * (1.0f - bouncing_.zoomW);
|
|
bouncing_.despY = spriteClip.h * (1.0f - bouncing_.zoomH);
|
|
|
|
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();
|
|
}
|
|
|
|
// Getters
|
|
float Balloon::getPosX() const { return x_; }
|
|
float Balloon::getPosY() const { return y_; }
|
|
int Balloon::getWidth() const { return w_; }
|
|
int Balloon::getHeight() const { return h_; }
|
|
BalloonSize Balloon::getSize() const { return size_; }
|
|
BalloonType Balloon::getType() const { return type_; }
|
|
Uint16 Balloon::getScore() const { return score_; }
|
|
Circle &Balloon::getCollider() { return collider_; }
|
|
Uint8 Balloon::getMenace() const { return isEnabled() ? menace_ : 0; }
|
|
Uint8 Balloon::getPower() const { return power_; }
|
|
bool Balloon::isStopped() const { return stopped_; }
|
|
bool Balloon::isInvulnerable() const { return invulnerable_; }
|
|
bool Balloon::isBeingCreated() const { return being_created_; }
|
|
bool Balloon::isEnabled() const { return enabled_; }
|
|
bool Balloon::isUsingReversedColor() { return use_reversed_colors_; }
|
|
bool Balloon::canBePopped() const { return !isBeingCreated(); }
|
|
|
|
// Setters
|
|
void Balloon::setVelY(float vel_y) { vy_ = vel_y; }
|
|
void Balloon::setSpeed(float speed) { speed_ = speed; }
|
|
void Balloon::setInvulnerable(bool value) { invulnerable_ = value; } |