#pragma once #include // Para Uint8, Uint16, SDL_FRect, Uint32 #include // Para array #include // Para allocator, shared_ptr, unique_ptr #include // Para basic_string, string #include // Para string_view #include // 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 SCORE = {50, 100, 200, 400}; static constexpr std::array POWER = {1, 3, 7, 15}; static constexpr std::array MENACE = {1, 2, 4, 8}; static constexpr std::array WIDTH = {10, 16, 26, 48, 49}; static constexpr std::array BOUNCING_SOUND = { "balloon_bounce0.wav", "balloon_bounce1.wav", "balloon_bounce2.wav", "balloon_bounce3.wav"}; static constexpr std::array POPPING_SOUND = { "balloon_pop0.wav", "balloon_pop1.wav", "balloon_pop2.wav", "balloon_pop3.wav"}; // Velocidades horizontales en pixels/segundo (convertidas desde 0.7 pixels/frame a 60fps) static constexpr float VELX_POSITIVE = 0.7F * 60.0F; // 42 pixels/segundo static constexpr float VELX_NEGATIVE = -0.7F * 60.0F; // -42 pixels/segundo // Multiplicadores de tempo del juego (sin cambios, son puros multiplicadores) static constexpr std::array 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); float creation_counter = 0.0f; SDL_FRect play_area = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F}; std::shared_ptr texture = nullptr; std::vector 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 setVelX(float vel_x) { vx_ = vel_x; } void alterVelX(float percent) {vx_ *= percent; } 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 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 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 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) };