#include "fade.hpp" #include #include #include #include "color.hpp" #include "param.hpp" #include "screen.hpp" // Constructor Fade::Fade() : renderer_(Screen::get()->getRenderer()) { // Crea la textura donde dibujar el fade backbuffer_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height); SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND); // Inicializa las variables init(); } // Destructor Fade::~Fade() { SDL_DestroyTexture(backbuffer_); } // Inicializa las variables void Fade::init() { type_ = Type::CENTER; mode_ = Mode::OUT; r_ = 0; g_ = 0; b_ = 0; a_ = 0; post_duration_ = 0; post_start_time_ = 0; pre_duration_ = 0; pre_start_time_ = 0; fading_duration_ = param.fade.random_squares_duration_ms; // Duración por defecto para FADING fading_start_time_ = 0; num_squares_width_ = param.fade.num_squares_width; num_squares_height_ = param.fade.num_squares_height; square_transition_duration_ = fading_duration_ / 4; // 25% del tiempo total para la transición individual } // Resetea algunas variables para volver a hacer el fade sin perder ciertos parametros void Fade::reset() { state_ = State::NOT_ENABLED; post_start_time_ = 0; pre_start_time_ = 0; fading_start_time_ = 0; value_ = 0; // La duración del fade se mantiene, se puede cambiar con setDuration() } // Pinta una transición en pantalla void Fade::render() { if (state_ != State::NOT_ENABLED) { // Para fade IN terminado, no renderizar (auto-desactivación visual) if (state_ == State::FINISHED && mode_ == Mode::IN) { return; } SDL_RenderTexture(renderer_, backbuffer_, nullptr, nullptr); } } // Actualiza las variables internas void Fade::update(float delta_time) { switch (state_) { case State::PRE: updatePreState(); break; case State::FADING: updateFadingState(); break; case State::POST: updatePostState(); break; default: break; } } void Fade::updatePreState() { Uint32 elapsed_time = SDL_GetTicks() - pre_start_time_; if (elapsed_time >= static_cast(pre_duration_)) { state_ = State::FADING; fading_start_time_ = SDL_GetTicks(); // Inicia el temporizador del fade AQUI } } void Fade::updateFadingState() { switch (type_) { case Type::FULLSCREEN: updateFullscreenFade(); break; case Type::CENTER: updateCenterFade(); break; case Type::RANDOM_SQUARE: updateRandomSquareFade(); break; case Type::RANDOM_SQUARE2: updateRandomSquare2Fade(); break; case Type::DIAGONAL: updateDiagonalFade(); break; case Type::VENETIAN: updateVenetianFade(); break; default: break; } } void Fade::changeToPostState() { state_ = State::POST; post_start_time_ = SDL_GetTicks(); } void Fade::updatePostState() { Uint32 elapsed_time = SDL_GetTicks() - post_start_time_; if (elapsed_time >= static_cast(post_duration_)) { state_ = State::FINISHED; } // Mantener el estado final del fade Uint8 post_alpha = a_; if (type_ == Type::RANDOM_SQUARE2 || type_ == Type::DIAGONAL) { post_alpha = (mode_ == Mode::OUT) ? 255 : 0; } else { post_alpha = (mode_ == Mode::OUT) ? 255 : 0; } cleanBackbuffer(r_, g_, b_, post_alpha); } void Fade::updateFullscreenFade() { Uint32 elapsed_time = SDL_GetTicks() - fading_start_time_; float progress = std::min(static_cast(elapsed_time) / fading_duration_, 1.0F); // Modifica la transparencia basada en el progreso Uint8 current_alpha = static_cast(progress * 255.0f); a_ = (mode_ == Mode::OUT) ? current_alpha : 255 - current_alpha; SDL_SetTextureAlphaMod(backbuffer_, a_); value_ = static_cast(progress * 100); // Comprueba si ha terminado if (elapsed_time >= static_cast(fading_duration_)) { changeToPostState(); } } void Fade::updateCenterFade() { Uint32 elapsed_time = SDL_GetTicks() - fading_start_time_; float progress = std::min(static_cast(elapsed_time) / fading_duration_, 1.0F); // Calcula la altura de las barras float rect_height = progress * (param.game.height / 2.0f); if (mode_ == Mode::IN) { rect_height = (param.game.height / 2.0f) - rect_height; } rect1_.h = rect_height; rect2_.h = rect_height; rect2_.y = param.game.height - rect_height; drawCenterFadeRectangles(); value_ = static_cast(progress * 100); // Comprueba si ha terminado if (elapsed_time >= static_cast(fading_duration_)) { a_ = (mode_ == Mode::OUT) ? 255 : 0; changeToPostState(); } } void Fade::drawCenterFadeRectangles() { auto* temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, backbuffer_); cleanBackbuffer(r_, g_, b_, 0); // Limpiar para modo IN SDL_SetRenderDrawColor(renderer_, r_, g_, b_, 255); SDL_RenderFillRect(renderer_, &rect1_); SDL_RenderFillRect(renderer_, &rect2_); SDL_SetRenderTarget(renderer_, temp); } void Fade::updateRandomSquareFade() { Uint32 elapsed_time = SDL_GetTicks() - fading_start_time_; float progress = std::min(static_cast(elapsed_time) / fading_duration_, 1.0F); // Calcula cuántos cuadrados deberían estar activos int total_squares = num_squares_width_ * num_squares_height_; int active_squares = static_cast(progress * total_squares); drawRandomSquares(active_squares); value_ = static_cast(progress * 100); // Comprueba si ha terminado if (elapsed_time >= static_cast(fading_duration_)) { changeToPostState(); } } void Fade::updateRandomSquare2Fade() { Uint32 elapsed_time = SDL_GetTicks() - fading_start_time_; int total_squares = num_squares_width_ * num_squares_height_; int activation_time = fading_duration_ - square_transition_duration_; activation_time = std::max(activation_time, square_transition_duration_); int squares_to_activate = 0; if (mode_ == Mode::OUT) { if (elapsed_time < static_cast(activation_time)) { float activation_progress = static_cast(elapsed_time) / activation_time; squares_to_activate = static_cast(activation_progress * total_squares); } else { squares_to_activate = total_squares; } for (int i = 0; i < squares_to_activate; ++i) { if (square_age_[i] == -1) square_age_[i] = elapsed_time; } } else { squares_to_activate = total_squares; float activation_progress = static_cast(elapsed_time) / activation_time; int squares_starting_transition = std::min(total_squares, std::max(1, static_cast(activation_progress * total_squares))); for (int i = 0; i < squares_starting_transition; ++i) { if (square_age_[i] == -1) square_age_[i] = elapsed_time; } } drawRandomSquares2(); value_ = calculateValue(0, total_squares, squares_to_activate); if (elapsed_time >= static_cast(fading_duration_)) { Uint8 final_alpha = (mode_ == Mode::OUT) ? 255 : 0; cleanBackbuffer(r_, g_, b_, final_alpha); changeToPostState(); } } void Fade::updateDiagonalFade() { Uint32 elapsed_time = SDL_GetTicks() - fading_start_time_; int activation_time = fading_duration_ - square_transition_duration_; activation_time = std::max(activation_time, square_transition_duration_); int max_diagonal = num_squares_width_ + num_squares_height_ - 1; int active_diagonals = 0; if (mode_ == Mode::OUT) { if (elapsed_time < static_cast(activation_time)) { float activation_progress = static_cast(elapsed_time) / activation_time; active_diagonals = static_cast(activation_progress * max_diagonal); } else { active_diagonals = max_diagonal; } for (int diagonal = 0; diagonal < active_diagonals; ++diagonal) { activateDiagonal(diagonal, elapsed_time); } } else { active_diagonals = max_diagonal; if (elapsed_time < static_cast(activation_time)) { float activation_progress = static_cast(elapsed_time) / activation_time; int diagonals_starting_transition = static_cast(activation_progress * max_diagonal); for (int diagonal = 0; diagonal < diagonals_starting_transition; ++diagonal) { activateDiagonal(diagonal, elapsed_time); } } else { for (int diagonal = 0; diagonal < max_diagonal; ++diagonal) { activateDiagonal(diagonal, elapsed_time); } } } drawDiagonal(); value_ = calculateValue(0, max_diagonal, active_diagonals); if (elapsed_time >= static_cast(fading_duration_)) { Uint8 final_alpha = (mode_ == Mode::OUT) ? 255 : 0; cleanBackbuffer(r_, g_, b_, final_alpha); changeToPostState(); } } void Fade::activateDiagonal(int diagonal_index, Uint32 current_time) { for (int x = 0; x < num_squares_width_; ++x) { int y = diagonal_index - x; if (y >= 0 && y < num_squares_height_) { int index = (y * num_squares_width_) + x; if (index >= 0 && index < static_cast(square_age_.size()) && square_age_[index] == -1) { square_age_[index] = current_time; } } } } void Fade::drawDiagonal() { auto* temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, backbuffer_); SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); SDL_RenderClear(renderer_); SDL_BlendMode blend_mode; SDL_GetRenderDrawBlendMode(renderer_, &blend_mode); SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND); Uint32 current_time = SDL_GetTicks() - fading_start_time_; for (size_t i = 0; i < square_.size(); ++i) { Uint8 current_alpha = 0; if (square_age_[i] == -1) { current_alpha = (mode_ == Mode::OUT) ? 0 : a_; } else { Uint32 square_elapsed = current_time - square_age_[i]; float progress = std::min(static_cast(square_elapsed) / square_transition_duration_, 1.0F); current_alpha = (mode_ == Mode::OUT) ? static_cast(progress * a_) : static_cast((1.0F - progress) * a_); } if (current_alpha > 0) { SDL_SetRenderDrawColor(renderer_, r_, g_, b_, current_alpha); SDL_RenderFillRect(renderer_, &square_[i]); } } SDL_SetRenderDrawBlendMode(renderer_, blend_mode); SDL_SetRenderTarget(renderer_, temp); } void Fade::drawRandomSquares(int active_count) { auto* temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, backbuffer_); // El fondo se prepara en activate() SDL_BlendMode blend_mode; SDL_GetRenderDrawBlendMode(renderer_, &blend_mode); SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE); SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_); // Dibuja solo los cuadrados activos for (int i = 0; i < active_count && i < static_cast(square_.size()); ++i) { SDL_RenderFillRect(renderer_, &square_[i]); } SDL_SetRenderDrawBlendMode(renderer_, blend_mode); SDL_SetRenderTarget(renderer_, temp); } void Fade::drawRandomSquares2() { auto* temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, backbuffer_); SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); SDL_RenderClear(renderer_); SDL_BlendMode blend_mode; SDL_GetRenderDrawBlendMode(renderer_, &blend_mode); SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND); Uint32 current_time = SDL_GetTicks() - fading_start_time_; for (size_t i = 0; i < square_.size(); ++i) { Uint8 current_alpha = 0; if (square_age_[i] == -1) { current_alpha = (mode_ == Mode::OUT) ? 0 : a_; } else { Uint32 square_elapsed = current_time - square_age_[i]; float progress = std::min(static_cast(square_elapsed) / square_transition_duration_, 1.0F); current_alpha = (mode_ == Mode::OUT) ? static_cast(progress * a_) : static_cast((1.0F - progress) * a_); } if (current_alpha > 0) { SDL_SetRenderDrawColor(renderer_, r_, g_, b_, current_alpha); SDL_RenderFillRect(renderer_, &square_[i]); } } SDL_SetRenderDrawBlendMode(renderer_, blend_mode); SDL_SetRenderTarget(renderer_, temp); } void Fade::updateVenetianFade() { Uint32 elapsed_time = SDL_GetTicks() - fading_start_time_; float progress = std::min(static_cast(elapsed_time) / fading_duration_, 1.0F); // Calcula la altura de las persianas float rect_height = progress * param.fade.venetian_size; if (mode_ == Mode::IN) { rect_height = param.fade.venetian_size - rect_height; } for (auto& rect : square_) { rect.h = rect_height; } drawVenetianBlinds(); value_ = static_cast(progress * 100); // Comprueba si ha terminado if (elapsed_time >= static_cast(fading_duration_)) { changeToPostState(); } } void Fade::drawVenetianBlinds() { auto* temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, backbuffer_); // Limpia la textura con el color base (transparente para OUT, opaco para IN) Uint8 initial_alpha = (mode_ == Mode::OUT) ? 0 : 255; cleanBackbuffer(r_, g_, b_, initial_alpha); SDL_BlendMode blend_mode; SDL_GetRenderDrawBlendMode(renderer_, &blend_mode); SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE); // Dibuja las persianas con el color opuesto al fondo Uint8 draw_alpha = (mode_ == Mode::OUT) ? 255 : 0; SDL_SetRenderDrawColor(renderer_, r_, g_, b_, draw_alpha); for (const auto& rect : square_) { SDL_RenderFillRect(renderer_, &rect); } SDL_SetRenderDrawBlendMode(renderer_, blend_mode); SDL_SetRenderTarget(renderer_, temp); } // Activa el fade void Fade::activate() { if (state_ != State::NOT_ENABLED) { return; } state_ = State::PRE; pre_start_time_ = SDL_GetTicks(); value_ = 0; // Preparación inicial de cada tipo switch (type_) { /*case Type::FULLSCREEN: cleanBackbuffer(r_, g_, b_, (mode_ == Mode::OUT) ? 0 : 255); SDL_SetTextureAlphaMod(backbuffer_, (mode_ == Mode::OUT) ? 255 : 0); break;*/ case Type::FULLSCREEN: { // La textura en sí siempre debe ser de un color sólido y opaco. // La transparencia se gestionará con la modulación de alfa. cleanBackbuffer(r_, g_, b_, 255); // Ahora, inicializamos la modulación de alfa correctamente: // - IN: Empieza opaco (255) y se desvanece a transparente. // - OUT: Empieza transparente (0) y se desvanece a opaco. const Uint8 initial_alpha = (mode_ == Mode::IN) ? 255 : 0; SDL_SetTextureAlphaMod(backbuffer_, initial_alpha); break; } case Type::CENTER: rect1_ = {.x = 0, .y = 0, .w = param.game.width, .h = 0}; rect2_ = {.x = 0, .y = param.game.height, .w = param.game.width, .h = 0}; a_ = 255; break; case Type::RANDOM_SQUARE: { rect1_ = {.x = 0, .y = 0, .w = static_cast(param.game.width / num_squares_width_), .h = static_cast(param.game.height / num_squares_height_)}; square_.clear(); for (int i = 0; i < num_squares_width_ * num_squares_height_; ++i) { rect1_.x = (i % num_squares_width_) * rect1_.w; rect1_.y = (i / num_squares_width_) * rect1_.h; square_.push_back(rect1_); } auto num = square_.size(); while (num > 1) { auto num_arreu = rand() % num; std::swap(square_[num_arreu], square_[--num]); } a_ = (mode_ == Mode::OUT) ? 255 : 0; cleanBackbuffer(r_, g_, b_, (mode_ == Mode::OUT) ? 0 : 255); break; } case Type::RANDOM_SQUARE2: case Type::DIAGONAL: { rect1_ = {.x = 0, .y = 0, .w = static_cast(param.game.width / num_squares_width_), .h = static_cast(param.game.height / num_squares_height_)}; square_.clear(); square_age_.assign(num_squares_width_ * num_squares_height_, -1); for (int i = 0; i < num_squares_width_ * num_squares_height_; ++i) { rect1_.x = (i % num_squares_width_) * rect1_.w; rect1_.y = (i / num_squares_width_) * rect1_.h; square_.push_back(rect1_); } if (type_ == Type::RANDOM_SQUARE2) { auto num = square_.size(); while (num > 1) { auto num_arreu = rand() % num; std::swap(square_[num_arreu], square_[--num]); // No es necesario desordenar square_age_ ya que todos son -1 } } Uint8 initial_alpha = (mode_ == Mode::OUT) ? 0 : 255; cleanBackbuffer(r_, g_, b_, initial_alpha); a_ = 255; square_transition_duration_ = std::max(fading_duration_ / 4, 100); break; } case Type::VENETIAN: { square_.clear(); rect1_ = {.x = 0, .y = 0, .w = param.game.width, .h = (mode_ == Mode::OUT) ? 0.0f : static_cast(param.fade.venetian_size)}; const int MAX = param.game.height / param.fade.venetian_size; for (int i = 0; i < MAX; ++i) { rect1_.y = i * param.fade.venetian_size; square_.push_back(rect1_); } break; } } } // Establece el color del fade void Fade::setColor(Uint8 r, Uint8 g, Uint8 b) { r_ = r; g_ = g; b_ = b; } // Establece el color del fade void Fade::setColor(Color color) { r_ = color.r; g_ = color.g; b_ = color.b; } // Limpia el backbuffer void Fade::cleanBackbuffer(Uint8 r, Uint8 g, Uint8 b, Uint8 a) { auto* temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, backbuffer_); SDL_SetRenderDrawColor(renderer_, r, g, b, a); SDL_RenderClear(renderer_); SDL_SetRenderTarget(renderer_, temp); } // Calcula el valor del estado del fade auto Fade::calculateValue(int min, int max, int current) -> int { if (current <= min) return 0; if (current >= max) return 100; return static_cast(100.0 * (current - min) / (max - min)); }