diff --git a/source/param.cpp b/source/param.cpp index 04f55e1..c971902 100644 --- a/source/param.cpp +++ b/source/param.cpp @@ -30,7 +30,8 @@ void initParam() { param.game.play_area.rect = {0, 0, param.game.width, 216}; param.game.name_entry_idle_time = 10; param.game.name_entry_total_time = 60; - param.game.speed = 15; + // param.game.speed = 15; + param.game.speed = 1000 / 60; param.game.hit_stop = true; param.game.hit_stop_ms = 300; precalculateZones(); diff --git a/source/sections/game.cpp b/source/sections/game.cpp index 285f561..16c2a42 100644 --- a/source/sections/game.cpp +++ b/source/sections/game.cpp @@ -851,12 +851,13 @@ void Game::handlePlayerCollision(std::shared_ptr &player, std::shared_pt pauseMusic(); auto position = getCollisionPoint(player->getCollider(), balloon->getCollider()); putHitOnScreen(position); - SDL_Delay(param.game.hit_stop_ms); - hit_.disable(); - resumeMusic(); + //SDL_Delay(param.game.hit_stop_ms); + enableSlowMotion(); + //hit_.disable(); + //resumeMusic(); } screen_->shake(); - playSound("voice_no.wav"); + //playSound("voice_no.wav"); player->setPlayingState(Player::State::ROLLING); sendPlayerToTheBack(player); if (allPlayersAreNotPlaying()) { @@ -887,6 +888,94 @@ void Game::updateTimeStopped() { } } +void Game::updateSlowMotion() { + if (!slow_game_enabled && slow_phase == SlowPhase::NONE) { + return; // No hacer nada si no está activado + } + + Uint32 current_time = SDL_GetTicks(); + + switch (slow_phase) { + case SlowPhase::NONE: + if (slow_game_enabled) { + // Iniciar fase 1: ralentizar + slow_phase = SlowPhase::SLOWING_DOWN; + original_speed = param.game.speed; + maintain_start_time = current_time; // Reutilizar para controlar tiempo de transición + } + break; + + case SlowPhase::SLOWING_DOWN: + { + // Fase 1: Transición suave hacia velocidad lenta basada en TIEMPO + Uint32 transition_duration = slowing_down_duration; + Uint32 elapsed = current_time - maintain_start_time; + + if (elapsed < transition_duration) { + float progress = (float)elapsed / transition_duration; + float smoothed_progress = easeOutCubic(progress); + + param.game.speed = original_speed + + (target_slow_speed - original_speed) * smoothed_progress; + } else { + // Transición completada, pasar a fase 2 + param.game.speed = target_slow_speed; + slow_phase = SlowPhase::MAINTAINING; + maintain_start_time = current_time; // Resetear para fase 2 + } + } + break; + + case SlowPhase::MAINTAINING: + // Fase 2: Comportamiento según la constante CENTRAL_PHASE_BEHAVIOR + if constexpr (CENTRAL_PHASE_BEHAVIOR == CentralPhaseType::SLOW_MOTION) { + // Mantener velocidad lenta durante un tiempo + if (current_time - maintain_start_time >= maintain_duration) { + // Tiempo cumplido, pasar a fase 3 + slow_phase = SlowPhase::SPEEDING_UP; + maintain_start_time = current_time; // Resetear para fase 3 + } + } else if constexpr (CENTRAL_PHASE_BEHAVIOR == CentralPhaseType::FREEZE_TIME) { + // Congelar el tiempo completamente + // No actualizar param.game.speed, mantenerlo "congelado" + // (se podría poner a un valor muy alto para casi pausar) + param.game.speed = 999999; // Prácticamente congela el update del juego + + if (current_time - maintain_start_time >= maintain_duration) { + // Tiempo cumplido, pasar a fase 3 + slow_phase = SlowPhase::SPEEDING_UP; + maintain_start_time = current_time; // Resetear para fase 3 + } + } + break; + + case SlowPhase::SPEEDING_UP: + { + // Fase 3: Transición suave de vuelta a velocidad normal basada en TIEMPO + Uint32 transition_duration = speeding_up_duration; + Uint32 elapsed = current_time - maintain_start_time; + + if (elapsed < transition_duration) { + float progress = (float)elapsed / transition_duration; + float smoothed_progress = easeInCubic(progress); + + param.game.speed = target_slow_speed + + (original_speed - target_slow_speed) * smoothed_progress; + } else { + // Transición completada, finalizar + param.game.speed = original_speed; + slow_phase = SlowPhase::NONE; + slow_game_enabled = false; // Resetear flag + + hit_.disable(); + resumeMusic(); + playSound("voice_no.wav"); + } + } + break; + } +} + // Actualiza el juego void Game::update() { if (SDL_GetTicks() - ticks_ > param.game.speed) { @@ -902,6 +991,8 @@ void Game::update() { fillCanvas(); } + updateSlowMotion(); + static const auto audio = Audio::get(); audio->update(); } @@ -1899,8 +1990,10 @@ void Game::checkDebugEvents(const SDL_Event &event) { switch (event.key.key) { case SDLK_1: // Crea una powerball { + enableSlowMotion(); + // balloon_manager_->createPowerBall(); - throwCoffee(players_.at(0)->getPosX() + (players_.at(0)->getWidth() / 2), players_.at(0)->getPosY() + (players_.at(0)->getHeight() / 2)); + //throwCoffee(players_.at(0)->getPosX() + (players_.at(0)->getWidth() / 2), players_.at(0)->getPosY() + (players_.at(0)->getHeight() / 2)); break; } case SDLK_2: // Activa o desactiva la aparición de globos diff --git a/source/sections/game.h b/source/sections/game.h index f8ebb5f..e246f0e 100644 --- a/source/sections/game.h +++ b/source/sections/game.h @@ -300,6 +300,32 @@ class Game { void sendPlayerToTheBack(const std::shared_ptr &player); // Mueve el jugador para pintarlo al fondo de la lista de jugadores void sendPlayerToTheFront(const std::shared_ptr &player); // Mueve el jugador para pintarlo el primero de la lista de jugadores + bool slow_game_enabled = false; + enum class SlowPhase { NONE, + SLOWING_DOWN, + MAINTAINING, + SPEEDING_UP }; + SlowPhase slow_phase = SlowPhase::NONE; + + // Constante para elegir comportamiento de la fase central + enum class CentralPhaseType { + SLOW_MOTION, + FREEZE_TIME + }; + static const CentralPhaseType CENTRAL_PHASE_BEHAVIOR = CentralPhaseType::FREEZE_TIME; + // Cambia a CentralPhaseType::FREEZE_TIME para congelar el tiempo + + float original_speed; + float target_slow_speed = 1000.0f / 15.0f; // Por ejemplo, 30 FPS + + Uint32 maintain_start_time = 0; + Uint32 slowing_down_duration = 0; // 2 segundos manteniendo velocidad lenta + Uint32 maintain_duration = 600; // 2 segundos manteniendo velocidad lenta + Uint32 speeding_up_duration = 2000; // 2 segundos manteniendo velocidad lenta + + void updateSlowMotion(); + void enableSlowMotion() { slow_game_enabled = true; } + // SISTEMA DE GRABACIÓN (CONDICIONAL) #ifdef RECORDING void updateRecording(); // Actualiza variables durante modo de grabación diff --git a/source/utils.cpp b/source/utils.cpp index 3a8bed6..5de8c3c 100644 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -262,6 +262,33 @@ auto easeOutElastic(double time) -> double { return pow(2, -10 * time) * sin((time * 10 - 0.75) * C4) + 1; } +// Ease Out Expo - Muy suave al final (más dramático) +auto easeOutExpo(double time) -> double { + return time == 1.0f ? 1.0f : 1.0f - pow(2.0f, -10.0f * time); +} + +// Ease In Expo - Arranque muy gradual +auto easeInExpo(double time) -> double { + return time == 0.0f ? 0.0f : pow(2.0f, 10.0f * (time - 1.0f)); +} + +// Ease Out Back - Con un pequeño "rebote" +auto easeOutBack(double time) -> double { + const double C1 = 1.70158f; + const double C3 = C1 + 1.0f; + return 1.0f + C3 * pow(time - 1.0f, 3.0f) + C1 * pow(time - 1.0f, 2.0f); +} + +// Ease Out Cubic - Desaceleración suave al final +auto easeOutCubic(double time) -> double { + return 1.0f - pow(1.0f - time, 3.0f); +} + +// Ease In Cubic - Aceleración gradual +auto easeInCubic(double time) -> double { + return time * time * time; +} + // Comprueba si una vector contiene una cadena auto stringInVector(const std::vector &vec, const std::string &str) -> bool { return std::find(vec.begin(), vec.end(), str) != vec.end(); diff --git a/source/utils.h b/source/utils.h index 0357f4e..de85b13 100644 --- a/source/utils.h +++ b/source/utils.h @@ -107,6 +107,11 @@ auto easeInOutExpo(double time) -> double; auto easeOutBounce(double time) -> double; auto easeOutElastic(double time) -> double; auto easeInElastic(double time) -> double; +auto easeOutExpo(double time) -> double; +auto easeInExpo(double time) -> double; +auto easeOutBack(double time) -> double; +auto easeOutCubic(double time) -> double; +auto easeInCubic(double time) -> double; // Utilidades varias auto stringInVector(const std::vector &vec, const std::string &str) -> bool; // Comprueba si un vector contiene una cadena