diff --git a/config/param_320x240.txt b/config/param_320x240.txt index 3334701..1f8c1d5 100644 --- a/config/param_320x240.txt +++ b/config/param_320x240.txt @@ -90,7 +90,7 @@ service_menu.window_message.text_safety_margin 15.0f # Margen de segu service_menu.window_message.animation_duration 0.3f # Duración de animaciones de ventanas (segundos) # --- INTRO --- -intro.bg_color 4664BD # Color de fondo de la intro +intro.bg_color 41526F # Color de fondo de la intro intro.card_color CBDBFC # Color de las tarjetas en la intro intro.shadow_color 00000080 # Color de la sombra de las tarjetas en la intro intro.text_distance_from_bottom 48 # Posicion del texto diff --git a/config/param_320x256.txt b/config/param_320x256.txt index 8ec032f..76b81d0 100644 --- a/config/param_320x256.txt +++ b/config/param_320x256.txt @@ -90,7 +90,7 @@ service_menu.window_message.text_safety_margin 15.0f # Margen de segu service_menu.window_message.animation_duration 0.3f # Duración de animaciones de ventanas (segundos) # --- INTRO --- -intro.bg_color 4664BD # Color de fondo de la intro +intro.bg_color 41526F # Color de fondo de la intro intro.card_color CBDBFC # Color de las tarjetas en la intro intro.shadow_color 00000080 # Color de la sombra de las tarjetas en la intro intro.text_distance_from_bottom 48 # Posición del texto desde la parte inferior diff --git a/source/card_sprite.cpp b/source/card_sprite.cpp index e42dbd3..edd4d28 100644 --- a/source/card_sprite.cpp +++ b/source/card_sprite.cpp @@ -13,9 +13,9 @@ CardSprite::CardSprite(std::shared_ptr texture) entry_easing_(easeOutBounce) {} // Inicia la animación de entrada (solo si está en IDLE) -void CardSprite::enable() { +auto CardSprite::enable() -> bool { if (state_ != CardState::IDLE) { - return; + return false; } state_ = CardState::ENTERING; @@ -34,6 +34,7 @@ void CardSprite::enable() { rotate_.center = {pos_.w / 2.0F, pos_.h / 2.0F}; shadow_visible_ = true; + return true; } // Inicia la animación de salida (solo si está en LANDED) @@ -43,7 +44,7 @@ void CardSprite::startExit() { } state_ = CardState::EXITING; - shadow_visible_ = false; + shadow_visible_ = true; // Velocidad y aceleración de salida vx_ = exit_vx_; @@ -109,11 +110,21 @@ void CardSprite::updateEntering(float delta_time) { } } -// Animación de salida: movimiento + rotación continua +// Animación de salida: movimiento + rotación continua + zoom opcional void CardSprite::updateExiting(float delta_time) { move(delta_time); rotate(delta_time); + // Ganar altura gradualmente (zoom hacia el objetivo) + if (exit_zoom_speed_ > 0.0F && horizontal_zoom_ < exit_target_zoom_) { + float new_zoom = horizontal_zoom_ + exit_zoom_speed_ * delta_time; + if (new_zoom > exit_target_zoom_) { + new_zoom = exit_target_zoom_; + } + horizontal_zoom_ = new_zoom; + vertical_zoom_ = new_zoom; + } + if (isOffScreen()) { state_ = CardState::FINISHED; } @@ -153,9 +164,8 @@ void CardSprite::renderShadow() { // Offset respecto a la tarjeta: base + extra proporcional a la altura // La sombra se aleja en diagonal abajo-derecha (opuesta a la luz en 0,0) - static constexpr float HEIGHT_MULTIPLIER = 300.0F; - float offset_x = shadow_offset_x_ + height * HEIGHT_MULTIPLIER; - float offset_y = shadow_offset_y_ + height * HEIGHT_MULTIPLIER; + float offset_x = shadow_offset_x_ + height * SHADOW_HEIGHT_MULTIPLIER; + float offset_y = shadow_offset_y_ + height * SHADOW_HEIGHT_MULTIPLIER; shadow_texture_->render( pos_.x + offset_x, @@ -163,8 +173,8 @@ void CardSprite::renderShadow() { &sprite_clip_, shadow_zoom, shadow_zoom, - 0.0, - nullptr, + rotate_.angle, + &rotate_.center, flip_); } @@ -173,9 +183,9 @@ auto CardSprite::isOffScreen() const -> bool { float effective_width = pos_.w * horizontal_zoom_; float effective_height = pos_.h * vertical_zoom_; return (pos_.x + effective_width < -OFF_SCREEN_MARGIN || - pos_.x > screen_width_ + OFF_SCREEN_MARGIN || - pos_.y + effective_height < -OFF_SCREEN_MARGIN || - pos_.y > screen_height_ + OFF_SCREEN_MARGIN); + pos_.x > screen_width_ + OFF_SCREEN_MARGIN || + pos_.y + effective_height < -OFF_SCREEN_MARGIN || + pos_.y > screen_height_ + OFF_SCREEN_MARGIN); } // --- Consultas de estado --- @@ -225,6 +235,11 @@ void CardSprite::setExitParams(float vx, float vy, float ax, float ay, double ro exit_rotate_amount_ = rotate_amount; } +void CardSprite::setExitLift(float target_zoom, float zoom_speed) { + exit_target_zoom_ = target_zoom; + exit_zoom_speed_ = zoom_speed; +} + void CardSprite::setShadowTexture(std::shared_ptr texture) { shadow_texture_ = std::move(texture); } diff --git a/source/card_sprite.hpp b/source/card_sprite.hpp index fff894d..ee714e4 100644 --- a/source/card_sprite.hpp +++ b/source/card_sprite.hpp @@ -33,23 +33,24 @@ class CardSprite : public MovingSprite { void render() override; // --- Control de estado --- - void enable(); // Inicia la animación de entrada - void startExit(); // Inicia la animación de salida + auto enable() -> bool; // Inicia la animación de entrada (true si se activó) + void startExit(); // Inicia la animación de salida // --- Consultas de estado --- - [[nodiscard]] auto hasLanded() const -> bool; // ¿Ha aterrizado definitivamente? - [[nodiscard]] auto hasFirstTouch() const -> bool; // ¿Ha tocado la mesa por primera vez? (primer rebote) - [[nodiscard]] auto hasFinished() const -> bool; // ¿Ha terminado completamente? - [[nodiscard]] auto isExiting() const -> bool; // ¿Está saliendo de pantalla? - [[nodiscard]] auto getState() const -> CardState; // Estado actual + [[nodiscard]] auto hasLanded() const -> bool; // ¿Ha aterrizado definitivamente? + [[nodiscard]] auto hasFirstTouch() const -> bool; // ¿Ha tocado la mesa por primera vez? (primer rebote) + [[nodiscard]] auto hasFinished() const -> bool; // ¿Ha terminado completamente? + [[nodiscard]] auto isExiting() const -> bool; // ¿Está saliendo de pantalla? + [[nodiscard]] auto getState() const -> CardState; // Estado actual // --- Configuración de entrada --- void setEntryParams(float start_zoom, double start_angle, float duration_s, std::function easing); void setEntryPosition(float start_x, float start_y); // Posición inicial (borde de pantalla) - void setLandingPosition(float x, float y); // Posición final centrada + void setLandingPosition(float x, float y); // Posición final centrada // --- Configuración de salida --- void setExitParams(float vx, float vy, float ax, float ay, double rotate_amount); + void setExitLift(float target_zoom, float zoom_speed); // Ganar altura al salir (zoom > 1.0) // --- Sombra --- void setShadowTexture(std::shared_ptr texture); @@ -72,8 +73,8 @@ class CardSprite : public MovingSprite { float entry_duration_s_ = 1.5F; float entry_elapsed_ = 0.0F; std::function entry_easing_; - float entry_start_x_ = 0.0F; // Posición inicial X (borde) - float entry_start_y_ = 0.0F; // Posición inicial Y (borde) + float entry_start_x_ = 0.0F; // Posición inicial X (borde) + float entry_start_y_ = 0.0F; // Posición inicial Y (borde) float landing_x_ = 0.0F; float landing_y_ = 0.0F; @@ -83,6 +84,8 @@ class CardSprite : public MovingSprite { float exit_ax_ = 0.0F; float exit_ay_ = 0.0F; double exit_rotate_amount_ = 0.0; + float exit_target_zoom_ = 1.0F; // Zoom objetivo al salir (>1.0 = se eleva) + float exit_zoom_speed_ = 0.0F; // Velocidad de cambio de zoom por segundo // --- Sombra --- std::shared_ptr shadow_texture_; @@ -94,8 +97,9 @@ class CardSprite : public MovingSprite { float screen_width_ = 320.0F; float screen_height_ = 240.0F; - // --- Margen fuera de pantalla para considerar FINISHED --- - static constexpr float OFF_SCREEN_MARGIN = 50.0F; + // --- Constantes --- + static constexpr float OFF_SCREEN_MARGIN = 50.0F; // Margen fuera de pantalla para considerar FINISHED + static constexpr float SHADOW_HEIGHT_MULTIPLIER = 400.0F; // Pixels de separación de sombra por unidad de altura // --- Métodos internos --- void updateEntering(float delta_time); diff --git a/source/sections/intro.cpp b/source/sections/intro.cpp index 11fb9ef..4c22baa 100644 --- a/source/sections/intro.cpp +++ b/source/sections/intro.cpp @@ -2,9 +2,9 @@ #include // Para SDL_GetTicks, SDL_SetRenderDrawColor, SDL_FRect, SDL_RenderFillRect, SDL_GetRenderTarget, SDL_RenderClear, SDL_RenderRect, SDL_SetRenderTarget, SDL_BLENDMODE_BLEND, SDL_PixelFormat, SDL_PollEvent, SDL_RenderTexture, SDL_TextureAccess, SDL_Event, Uint64 -#include // Para array -#include // Para basic_string, string -#include // Para move +#include // Para array +#include // Para basic_string, string +#include // Para move #include "audio.hpp" // Para Audio #include "card_sprite.hpp" // Para CardSprite @@ -57,9 +57,20 @@ void Intro::checkInput() { // Actualiza las escenas de la intro void Intro::updateScenes() { - // Cuando la tarjeta actual toca la mesa por primera vez, la anterior sale despedida - if (scene_ > 0 && card_sprites_.at(scene_)->hasFirstTouch()) { - card_sprites_.at(scene_ - 1)->startExit(); + // Sonido al lanzar la tarjeta (enable() devuelve true solo la primera vez) + if (card_sprites_.at(scene_)->enable()) { + Audio::get()->playSound(SFX_CARD_THROW); + } + + // Cuando la tarjeta actual toca la mesa por primera vez: shake + sonido + la anterior sale despedida + if (!shake_done_ && card_sprites_.at(scene_)->hasFirstTouch()) { + Screen::get()->shake(); + Audio::get()->playSound(SFX_CARD_IMPACT); + shake_done_ = true; + + if (scene_ > 0) { + card_sprites_.at(scene_ - 1)->startExit(); + } } switch (scene_) { @@ -87,9 +98,6 @@ void Intro::updateScenes() { } void Intro::updateScene0() { - // Primera imagen - UPV: activa la tarjeta - card_sprites_.at(0)->enable(); - // Primer texto cuando aterriza if (card_sprites_.at(0)->hasLanded() && !texts_.at(0)->hasFinished()) { texts_.at(0)->setEnabled(true); @@ -109,13 +117,11 @@ void Intro::updateScene0() { if (texts_.at(2)->hasFinished()) { texts_.at(2)->setEnabled(false); scene_++; + shake_done_ = false; } } void Intro::updateScene1() { - // Segunda imagen - Máquina - card_sprites_.at(1)->enable(); - // Texto cuando aterriza if (card_sprites_.at(1)->hasLanded() && !texts_.at(3)->hasFinished()) { texts_.at(3)->setEnabled(true); @@ -125,12 +131,12 @@ void Intro::updateScene1() { if (texts_.at(3)->hasFinished()) { texts_.at(3)->setEnabled(false); scene_++; + shake_done_ = false; } } void Intro::updateScene2() { // Tercera imagen - GRITO: tarjeta y texto a la vez - card_sprites_.at(2)->enable(); if (!texts_.at(4)->hasFinished()) { texts_.at(4)->setEnabled(true); } @@ -139,13 +145,12 @@ void Intro::updateScene2() { if (card_sprites_.at(2)->hasLanded() && texts_.at(4)->hasFinished()) { texts_.at(4)->setEnabled(false); scene_++; + shake_done_ = false; } } void Intro::updateScene3() { // Cuarta imagen - Reflexión - card_sprites_.at(3)->enable(); - if (!texts_.at(5)->hasFinished()) { texts_.at(5)->setEnabled(true); } @@ -159,13 +164,12 @@ void Intro::updateScene3() { if (card_sprites_.at(3)->hasLanded() && texts_.at(6)->hasFinished()) { texts_.at(6)->setEnabled(false); scene_++; + shake_done_ = false; } } void Intro::updateScene4() { // Quinta imagen - Patada - card_sprites_.at(4)->enable(); - if (!texts_.at(7)->hasFinished()) { texts_.at(7)->setEnabled(true); } @@ -174,13 +178,12 @@ void Intro::updateScene4() { if (card_sprites_.at(4)->hasLanded() && texts_.at(7)->hasFinished()) { texts_.at(7)->setEnabled(false); scene_++; + shake_done_ = false; } } void Intro::updateScene5() { // Sexta imagen - Globos de café - card_sprites_.at(5)->enable(); - if (!texts_.at(8)->hasFinished()) { texts_.at(8)->setEnabled(true); } @@ -213,6 +216,11 @@ void Intro::update(float delta_time) { switch (state_) { case State::SCENES: + // Pausa inicial antes de empezar + if (initial_elapsed_ < INITIAL_DELAY_S) { + initial_elapsed_ += delta_time; + break; + } updateSprites(delta_time); updateTexts(delta_time); updateScenes(); @@ -349,14 +357,14 @@ void Intro::initSprites() { // Configuración por tarjeta: posición de entrada, ángulo, salida // Cada tarjeta viene de un borde diferente (gente alrededor de una mesa lanzando cartas al centro) struct CardConfig { - float entry_x; // Posición inicial X - float entry_y; // Posición inicial Y - double entry_angle; // Ángulo de entrada - float exit_vx; // Velocidad de salida X - float exit_vy; // Velocidad de salida Y - float exit_ax; // Aceleración de salida X - float exit_ay; // Aceleración de salida Y - double exit_rotation; // Velocidad de rotación de salida + float entry_x; // Posición inicial X + float entry_y; // Posición inicial Y + double entry_angle; // Ángulo de entrada + float exit_vx; // Velocidad de salida X + float exit_vy; // Velocidad de salida Y + float exit_ax; // Aceleración de salida X + float exit_ay; // Aceleración de salida Y + double exit_rotation; // Velocidad de rotación de salida }; const float W = param.game.width; @@ -367,17 +375,17 @@ void Intro::initSprites() { const CardConfig CARD_CONFIGS[] = { // 0: Entra desde la izquierda. La 1 entra desde la derecha → sale empujada hacia la izquierda - {-CARD_WIDTH, Y_DEST - 20.0F, CARD_ANGLE_0, -S, S * 0.1F, -A, 0.0F, -R}, + {-CARD_WIDTH, Y_DEST - 20.0F, CARD_ANGLE_0, -S, S * 0.1F, -A, 0.0F, -R}, // 1: Entra desde la derecha. La 2 entra desde arriba → sale empujada hacia abajo - {W + CARD_WIDTH, Y_DEST + 15.0F, CARD_ANGLE_1, S * 0.15F, S, 0.0F, A, R * 1.1}, + {W + CARD_WIDTH, Y_DEST + 15.0F, CARD_ANGLE_1, S * 0.15F, S, 0.0F, A, R * 1.1}, // 2: Entra desde arriba. La 3 entra desde abajo → sale empujada hacia arriba - {X_DEST + 30.0F, -CARD_HEIGHT, CARD_ANGLE_2, -S * 0.15F, -S, 0.0F, -A, -R * 0.9}, + {X_DEST + 30.0F, -CARD_HEIGHT, CARD_ANGLE_2, -S * 0.15F, -S, 0.0F, -A, -R * 0.9}, // 3: Entra desde abajo. La 4 entra desde arriba-izquierda → sale empujada hacia abajo-derecha - {X_DEST - 25.0F, H + CARD_HEIGHT, CARD_ANGLE_3, S * 0.8F, S * 0.6F, A * 0.5F, A * 0.4F, R}, + {X_DEST - 25.0F, H + CARD_HEIGHT, CARD_ANGLE_3, S * 0.8F, S * 0.6F, A * 0.5F, A * 0.4F, R}, // 4: Entra desde arriba-izquierda. La 5 entra desde derecha-abajo → sale empujada hacia arriba-izquierda - {-CARD_WIDTH * 0.5F, -CARD_HEIGHT, CARD_ANGLE_4, -S * 0.7F, -S * 0.5F, -A * 0.5F, -A * 0.3F, -R * 1.2}, + {-CARD_WIDTH * 0.5F, -CARD_HEIGHT, CARD_ANGLE_4, -S * 0.7F, -S * 0.5F, -A * 0.5F, -A * 0.3F, -R * 1.2}, // 5: Entra desde la derecha-abajo. Última: sale hacia la izquierda suave (viento) - {W + CARD_WIDTH, H * 0.6F, CARD_ANGLE_5, -S * 0.6F, -S * 0.1F, -A * 0.5F, 0.0F, -R * 0.7}, + {W + CARD_WIDTH, H * 0.6F, CARD_ANGLE_5, -S * 0.6F, -S * 0.1F, -A * 0.5F, 0.0F, -R * 0.7}, }; // Inicializa los CardSprites @@ -408,6 +416,11 @@ void Intro::initSprites() { // Límites de pantalla card->setScreenBounds(param.game.width, param.game.height); + // Última tarjeta: gana algo de altura al salir (se la lleva el viento) + if (i == TOTAL_SPRITES - 1) { + card->setExitLift(1.2F, 0.15F); // Hasta zoom 1.2, a 0.15/s + } + card_sprites_.push_back(std::move(card)); } } diff --git a/source/sections/intro.hpp b/source/sections/intro.hpp index 92f7ede..d841658 100644 --- a/source/sections/intro.hpp +++ b/source/sections/intro.hpp @@ -36,14 +36,19 @@ class Intro { private: // --- Constantes de tiempo (en segundos) --- static constexpr float TEXT_DISPLAY_DURATION_S = 3.0F; // Duración de visualización de texto - static constexpr float POST_BG_STOP_DELAY_S = 1.0F; // Retraso antes de detener el fondo - static constexpr float POST_END_DELAY_S = 1.0F; // Retraso antes de finalizar intro + static constexpr float POST_BG_STOP_DELAY_S = 1.0F; // Retraso antes de detener el fondo + static constexpr float POST_END_DELAY_S = 1.0F; // Retraso antes de finalizar intro + static constexpr float INITIAL_DELAY_S = 2.0F; // Pausa antes de empezar las escenas + + // --- Constantes de sonido --- + static constexpr const char* SFX_CARD_THROW = "service_menu_select.wav"; // Sonido al lanzar una tarjeta + static constexpr const char* SFX_CARD_IMPACT = "player_collision.wav"; // Sonido al impactar en la mesa // --- Constantes de layout --- static constexpr float CARD_BORDER_SIZE = 2.0F; // Tamaño del borde de tarjetas static constexpr float SHADOW_OFFSET = 8.0F; // Desplazamiento de sombra static constexpr float TILED_BG_SPEED = 18.0F; // Velocidad del fondo mosaico (pixels/segundo) - static constexpr int TEXT_KERNING = -2; // Espaciado entre caracteres + static constexpr int TEXT_KERNING = -2; // Espaciado entre caracteres // --- Constantes de velocidades de texto (segundos entre caracteres, menor = más rápido) --- static constexpr float TEXT_SPEED_ULTRA_FAST = 0.0167F; // Ultra rápida (1 frame a 60fps) @@ -55,11 +60,11 @@ class Intro { static constexpr float TEXT_SPEED_ULTRA_SLOW = 0.333F; // Ultra lenta (20 frames a 60fps) // --- Constantes de animaciones de tarjetas --- - static constexpr float CARD_ENTRY_DURATION_S = 1.5F; // Duración de la animación de entrada - static constexpr float CARD_START_ZOOM = 1.8F; // Zoom inicial (como si estuviera cerca) - static constexpr float CARD_EXIT_SPEED = 400.0F; // Velocidad base de salida (pixels/s) - static constexpr float CARD_EXIT_ACCEL = 200.0F; // Aceleración de salida (pixels/s²) - static constexpr double CARD_EXIT_ROTATION = 450.0; // Velocidad de rotación en salida (grados/s) + static constexpr float CARD_ENTRY_DURATION_S = 1.5F; // Duración de la animación de entrada + static constexpr float CARD_START_ZOOM = 1.8F; // Zoom inicial (como si estuviera cerca) + static constexpr float CARD_EXIT_SPEED = 400.0F; // Velocidad base de salida (pixels/s) + static constexpr float CARD_EXIT_ACCEL = 200.0F; // Aceleración de salida (pixels/s²) + static constexpr double CARD_EXIT_ROTATION = 450.0; // Velocidad de rotación en salida (grados/s) // --- Ángulos iniciales de entrada por tarjeta (grados) --- static constexpr double CARD_ANGLE_0 = 12.0; @@ -92,6 +97,8 @@ class Intro { PostState post_state_ = PostState::STOP_BG; // Estado POST float state_start_time_ = 0.0F; // Tiempo de inicio del estado actual (segundos) Color bg_color_ = param.intro.bg_color; // Color de fondo + bool shake_done_ = false; // Evita shake repetido en la misma escena + float initial_elapsed_ = 0.0F; // Tiempo acumulado antes de empezar // --- Métodos internos --- void update(float delta_time); // Actualiza las variables del objeto