Files
coffee_crisis_arcade_edition/source/balloon.h

303 lines
14 KiB
C++

#pragma once
#include <SDL3/SDL.h> // Para Uint8, Uint16, SDL_FRect, Uint32
#include <array> // Para array
#include <memory> // Para allocator, shared_ptr, unique_ptr
#include <string> // Para basic_string, string
#include <string_view> // Para string_view
#include <vector> // Para vector
#include "animated_sprite.h" // Para AnimatedSprite
#include "utils.h" // Para Circle
class Texture;
// --- Clase Balloon ---
class Balloon {
public:
// --- Constantes relacionadas con globos ---
static constexpr int MAX_BOUNCE = 10; // Cantidad de elementos del vector de deformación
static constexpr std::array<int, 4> SCORE = {50, 100, 200, 400};
static constexpr std::array<int, 4> POWER = {1, 3, 7, 15};
static constexpr std::array<int, 4> MENACE = {1, 2, 4, 8};
static constexpr std::array<int, 5> WIDTH = {10, 16, 26, 48, 49};
static constexpr std::array<std::string_view, 4> BOUNCING_SOUND = {
"balloon_bounce0.wav",
"balloon_bounce1.wav",
"balloon_bounce2.wav",
"balloon_bounce3.wav"};
static constexpr std::array<std::string_view, 4> POPPING_SOUND = {
"balloon_pop0.wav",
"balloon_pop1.wav",
"balloon_pop2.wav",
"balloon_pop3.wav"};
// Velocidades horizontales en pixels/ms (convertidas desde 0.7 pixels/frame a 60fps)
static constexpr float VELX_POSITIVE = 0.7F / (1000.0F / 60.0F); // ~0.042 pixels/ms
static constexpr float VELX_NEGATIVE = -0.7F / (1000.0F / 60.0F); // ~-0.042 pixels/ms
// Multiplicadores de tempo del juego (sin cambios, son puros multiplicadores)
static constexpr std::array<float, 5> GAME_TEMPO = {0.60F, 0.70F, 0.80F, 0.90F, 1.00F};
static constexpr int POWERBALL_SCREENPOWER_MINIMUM = 10;
static constexpr int POWERBALL_COUNTER = 8;
// --- Enums ---
enum class Size : Uint8 {
SMALL = 0, // Tamaño pequeño
MEDIUM = 1, // Tamaño mediano
LARGE = 2, // Tamaño grande
EXTRALARGE = 3, // Tamaño extra grande
};
enum class Type : Uint8 {
BALLOON = 0, // Globo normal
FLOATER = 1, // Globo flotante
POWERBALL = 2, // Globo de poder
};
// --- Estructura para manejo de sonido ---
struct Sound {
std::string bouncing_file; // Archivo de sonido al rebotar
std::string popping_file; // Archivo de sonido al explotar
bool bouncing_enabled = false; // Si debe sonar el globo al rebotar
bool poping_enabled = true; // Si debe sonar el globo al explotar
bool enabled = true; // Indica si los globos deben hacer algun sonido
};
// --- Estructura de configuración para inicialización ---
struct Config {
float x = 0.0F;
float y = 0.0F;
Type type = Type::BALLOON;
Size size = Size::EXTRALARGE;
float vel_x = VELX_POSITIVE;
float game_tempo = GAME_TEMPO.at(0);
Uint16 creation_counter = 0;
SDL_FRect play_area = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F};
std::shared_ptr<Texture> texture = nullptr;
std::vector<std::string> animation;
Sound sound;
};
// --- Constructores y destructor ---
Balloon(const Config& config);
~Balloon() = default;
// --- Métodos principales ---
void alignTo(int x); // Centra el globo en la posición X
void render(); // Pinta el globo en la pantalla
void move(float deltaTime); // Actualiza la posición y estados del globo (time-based)
void update(float deltaTime); // Actualiza el globo (posición, animación, contadores) (time-based)
void stop(); // Detiene el globo
void start(); // Pone el globo en movimiento
void pop(bool should_sound = false); // Explota el globo
// --- Métodos de color ---
void useReverseColor(); // Pone el color alternativo en el globo
void useNormalColor(); // Pone el color normal en el globo
// --- Getters ---
[[nodiscard]] auto getPosX() const -> float { return x_; }
[[nodiscard]] auto getPosY() const -> float { return y_; }
[[nodiscard]] auto getWidth() const -> int { return w_; }
[[nodiscard]] auto getHeight() const -> int { return h_; }
[[nodiscard]] auto getSize() const -> Size { return size_; }
[[nodiscard]] auto getType() const -> Type { return type_; }
[[nodiscard]] auto getScore() const -> Uint16 { return score_; }
auto getCollider() -> Circle& { return collider_; }
[[nodiscard]] auto getMenace() const -> Uint8 { return isEnabled() ? menace_ : 0; }
[[nodiscard]] auto getPower() const -> Uint8 { return power_; }
[[nodiscard]] auto isStopped() const -> bool { return stopped_; }
[[nodiscard]] auto isPowerBall() const -> bool { return type_ == Type::POWERBALL; }
[[nodiscard]] auto isInvulnerable() const -> bool { return invulnerable_; }
[[nodiscard]] auto isBeingCreated() const -> bool { return being_created_; }
[[nodiscard]] auto isEnabled() const -> bool { return enabled_; }
[[nodiscard]] auto isUsingReversedColor() const -> bool { return use_reversed_colors_; }
[[nodiscard]] auto canBePopped() const -> bool { return !isBeingCreated(); }
// --- Setters ---
void setVelY(float vel_y) { vy_ = vel_y; }
void setGameTempo(float tempo) { game_tempo_ = tempo; }
void setInvulnerable(bool value) { invulnerable_ = value; }
void setBouncingSound(bool value) { sound_.bouncing_enabled = value; }
void setPoppingSound(bool value) { sound_.poping_enabled = value; }
void setSound(bool value) { sound_.enabled = value; }
private:
// --- Estructura para el efecto de rebote ---
struct BounceEffect {
private:
static constexpr int BOUNCE_FRAMES = 10; // Cantidad de elementos del vector de deformación
// Tablas de valores predefinidos para el efecto de rebote
static constexpr std::array<float, BOUNCE_FRAMES> HORIZONTAL_ZOOM_VALUES = {
1.10F,
1.05F,
1.00F,
0.95F,
0.90F,
0.95F,
1.00F,
1.02F,
1.05F,
1.02F};
static constexpr std::array<float, BOUNCE_FRAMES> VERTICAL_ZOOM_VALUES = {
0.90F,
0.95F,
1.00F,
1.05F,
1.10F,
1.05F,
1.00F,
0.98F,
0.95F,
0.98F};
// Estado del efecto
bool enabled_ = false; // Si el efecto está activo
Uint8 counter_ = 0; // Contador para el efecto
Uint8 speed_ = 2; // Velocidad del efecto
// Valores actuales de transformación
float horizontal_zoom_ = 1.0F; // Zoom en anchura
float verical_zoom_ = 1.0F; // Zoom en altura
float x_offset_ = 0.0F; // Desplazamiento X antes de pintar
float y_offset_ = 0.0F; // Desplazamiento Y antes de pintar
public:
// Constructor por defecto
BounceEffect() = default;
// Reinicia el efecto a sus valores iniciales
void reset() {
counter_ = 0;
horizontal_zoom_ = 1.0F;
verical_zoom_ = 1.0F;
x_offset_ = 0.0F;
y_offset_ = 0.0F;
}
// Aplica la deformación visual al sprite
void apply(AnimatedSprite* sprite) const {
if (sprite != nullptr) {
sprite->setHorizontalZoom(horizontal_zoom_);
sprite->setVerticalZoom(verical_zoom_);
}
}
// Activa el efecto de rebote
void enable(AnimatedSprite* sprite, Size balloon_size) {
// Los globos pequeños no tienen efecto de rebote
if (balloon_size == Size::SMALL) {
return;
}
enabled_ = true;
reset();
apply(sprite);
}
// Detiene el efecto de rebote
void disable(AnimatedSprite* sprite) {
enabled_ = false;
reset();
apply(sprite);
}
// Actualiza el efecto en cada frame
void update(AnimatedSprite* sprite) {
if (!enabled_) {
return;
}
// Calcula el índice basado en el contador y velocidad
const int INDEX = counter_ / speed_;
// Actualiza los valores de zoom desde las tablas predefinidas
horizontal_zoom_ = HORIZONTAL_ZOOM_VALUES.at(INDEX);
verical_zoom_ = VERTICAL_ZOOM_VALUES.at(INDEX);
// Aplica la deformación al sprite
apply(sprite);
// Incrementa el contador y verifica si el efecto debe terminar
if (++counter_ / speed_ >= BOUNCE_FRAMES) {
disable(sprite);
}
}
// Getters para acceso a los valores actuales
[[nodiscard]] auto isEnabled() const -> bool { return enabled_; }
[[nodiscard]] auto getHorizontalZoom() const -> float { return horizontal_zoom_; }
[[nodiscard]] auto getVerticalZoom() const -> float { return verical_zoom_; }
[[nodiscard]] auto getXOffset() const -> float { return x_offset_; }
[[nodiscard]] auto getYOffset() const -> float { return y_offset_; }
};
// --- Objetos y punteros ---
std::unique_ptr<AnimatedSprite> sprite_; // Sprite del objeto globo
// --- Variables de estado y físicas ---
float x_; // Posición X
float y_; // Posición Y
float w_; // Ancho
float h_; // Alto
float vx_; // Velocidad X
float vy_; // Velocidad Y
float gravity_; // Aceleración en Y
float default_vy_; // Velocidad inicial al rebotar
float max_vy_; // Máxima velocidad en Y
bool being_created_; // Si el globo se está creando
bool enabled_ = true; // Si el globo está activo
bool invulnerable_; // Si el globo es invulnerable
bool stopped_; // Si el globo está parado
bool use_reversed_colors_ = false; // Si se usa el color alternativo
Circle collider_; // Círculo de colisión
float creation_counter_; // Temporizador de creación
float creation_counter_ini_; // Valor inicial del temporizador de creación
Uint16 score_; // Puntos al destruir el globo
Type type_; // Tipo de globo
Size size_; // Tamaño de globo
Uint8 menace_; // Amenaza que genera el globo
Uint32 counter_ = 0; // Contador interno
float game_tempo_; // Multiplicador de tempo del juego
float movement_accumulator_ = 0.0f; // Acumulador para movimiento durante creación (deltaTime)
Uint8 power_; // Poder que alberga el globo
SDL_FRect play_area_; // Zona de movimiento del globo
Sound sound_; // Configuración de sonido del globo
BounceEffect bounce_effect_; // Efecto de rebote
// --- Posicionamiento y transformación ---
void shiftColliders(); // Alinea el círculo de colisión con el sprite
void shiftSprite(); // Alinea el sprite en pantalla
// --- Animación y sonido ---
void setAnimation(); // Establece la animación correspondiente
void playBouncingSound(); // Reproduce el sonido de rebote
void playPoppingSound(); // Reproduce el sonido de reventar
// --- Movimiento y física ---
void handleHorizontalMovement(float deltaTime); // Maneja el movimiento horizontal (time-based)
void handleVerticalMovement(float deltaTime); // Maneja el movimiento vertical (time-based)
void applyGravity(float deltaTime); // Aplica la gravedad al objeto (time-based)
// --- Rebote ---
void enableBounceEffect(); // Activa el efecto de rebote
void disableBounceEffect(); // Detiene el efecto de rebote
void updateBounceEffect(); // Actualiza el estado del rebote
void handleHorizontalBounce(float min_x, float max_x); // Maneja el rebote horizontal dentro de límites
// --- Colisiones ---
[[nodiscard]] auto isOutOfHorizontalBounds(float min_x, float max_x) const -> bool; // Verifica si está fuera de los límites horizontales
[[nodiscard]] auto shouldCheckTopCollision() const -> bool; // Determina si debe comprobarse la colisión superior
void handleTopCollision(); // Maneja la colisión superior
void handleBottomCollision(); // Maneja la colisión inferior
// --- Lógica de estado ---
void updateState(float deltaTime); // Actualiza los estados del globo (time-based)
};