diff --git a/source/enter_name.h b/source/enter_name.h index 999857a..8f60dce 100644 --- a/source/enter_name.h +++ b/source/enter_name.h @@ -20,10 +20,12 @@ class EnterName { void addCharacter(); // Añade el carácter seleccionado al nombre void removeLastCharacter(); // Elimina el último carácter del nombre - auto getFinalName() -> std::string; // Obtiene el nombre final (o aleatorio si vacío) - [[nodiscard]] auto getCurrentName() const -> std::string { return name_; } // Obtiene el nombre actual en proceso - [[nodiscard]] auto getSelectedCharacter(int offset = 0) const -> std::string; // Devuelve el carácter seleccionado con offset relativo - [[nodiscard]] auto getCarousel(int size) const -> std::string; // Devuelve el carrusel de caracteres (size debe ser impar) + auto getFinalName() -> std::string; // Obtiene el nombre final (o aleatorio si vacío) + [[nodiscard]] auto getCurrentName() const -> std::string { return name_; } // Obtiene el nombre actual en proceso + [[nodiscard]] auto getSelectedCharacter(int offset = 0) const -> std::string; // Devuelve el carácter seleccionado con offset relativo + [[nodiscard]] auto getCarousel(int size) const -> std::string; // Devuelve el carrusel de caracteres (size debe ser impar) + [[nodiscard]] auto getSelectedIndex() const -> int { return selected_index_; } // Obtiene el índice del carácter seleccionado + [[nodiscard]] auto getCharacterList() const -> const std::string& { return character_list_; } // Obtiene la lista completa de caracteres private: // --- Variables de estado --- diff --git a/source/player.cpp b/source/player.cpp index 8eec7c6..4c6fa59 100644 --- a/source/player.cpp +++ b/source/player.cpp @@ -542,7 +542,7 @@ void Player::updateScoreboard() { case State::ENTERING_NAME_GAME_COMPLETED: { Scoreboard::get()->setEnterName(scoreboard_panel_, enter_name_->getCurrentName()); Scoreboard::get()->setCharacterSelected(scoreboard_panel_, enter_name_->getSelectedCharacter()); - Scoreboard::get()->setCarousel(scoreboard_panel_, enter_name_->getCarousel(7)); + Scoreboard::get()->setCarouselAnimation(scoreboard_panel_, enter_name_->getSelectedIndex(), enter_name_.get()); break; } default: diff --git a/source/scoreboard.cpp b/source/scoreboard.cpp index bea3fc8..1de9205 100644 --- a/source/scoreboard.cpp +++ b/source/scoreboard.cpp @@ -49,6 +49,8 @@ Scoreboard::Scoreboard() score_.at(i) = 0; mult_.at(i) = 0; continue_counter_.at(i) = 0; + carousel_prev_index_.at(i) = -1; // Inicializar a -1 para detectar primera inicialización + enter_name_ref_.at(i) = nullptr; } panel_.at(static_cast(Id::LEFT)).mode = Mode::SCORE; @@ -90,38 +92,52 @@ Scoreboard::~Scoreboard() { } } -// Establece el carrusel y detecta cambios para iniciar animación -void Scoreboard::setCarousel(Id id, const std::string& carousel) { +// Configura la animación del carrusel +void Scoreboard::setCarouselAnimation(Id id, int selected_index, EnterName* enter_name_ptr) { size_t idx = static_cast(id); - if (carousel.empty()) { - carousel_.at(idx) = carousel; + // Guardar referencia a EnterName + enter_name_ref_.at(idx) = enter_name_ptr; + + if (!enter_name_ptr || selected_index < 0) { return; } - // Calcular el índice central del nuevo carrusel - int new_center_index = carousel.size() / 2; - - // Si es la primera vez o cambió el índice, establecer nuevo target - if (carousel_.at(idx).empty()) { - // Primera inicialización: posicionar directamente sin animar - carousel_position_.at(idx) = static_cast(new_center_index); - carousel_target_.at(idx) = static_cast(new_center_index); - carousel_prev_index_.at(idx) = new_center_index; + // Primera inicialización: posicionar directamente sin animar + if (carousel_prev_index_.at(idx) == -1) { + carousel_position_.at(idx) = static_cast(selected_index); + carousel_target_.at(idx) = static_cast(selected_index); + carousel_prev_index_.at(idx) = selected_index; } else { - // Calcular índice central del carrusel anterior - int prev_center_index = carousel_.at(idx).size() / 2; + // Detectar cambio en el índice del carácter seleccionado + int prev_index = carousel_prev_index_.at(idx); - // Detectar si cambió (LEFT o RIGHT fue presionado) - if (new_center_index != prev_center_index) { - // Establecer nuevo target (la animación se hará en updateCarouselAnimation) - carousel_target_.at(idx) = static_cast(new_center_index); + if (selected_index != prev_index) { + // Calcular dirección del movimiento + int direction = selected_index - prev_index; + + // Obtener tamaño de la lista para manejar wrap-around + const int LIST_SIZE = enter_name_ptr->getCharacterList().size(); + + // Manejar wrap-around circular + if (direction > LIST_SIZE / 2) { + direction = -(LIST_SIZE - direction); // Wrap backward (ej: Z → A) + } else if (direction < -LIST_SIZE / 2) { + direction = LIST_SIZE + direction; // Wrap forward (ej: A → Z) + } + + // Normalizar a -1 o +1 + direction = (direction > 0) ? 1 : ((direction < 0) ? -1 : 0); + + if (direction != 0) { + // Actualizar target con movimiento relativo + carousel_target_.at(idx) = carousel_position_.at(idx) + static_cast(direction); + + // Guardar nuevo índice + carousel_prev_index_.at(idx) = selected_index; + } } - - carousel_prev_index_.at(idx) = new_center_index; } - - carousel_.at(idx) = carousel; } // Transforma un valor numérico en una cadena de 7 cifras @@ -498,8 +514,15 @@ void Scoreboard::renderSeparator() { // Pinta el carrusel de caracteres con efecto de color LERP y animación suave void Scoreboard::renderCarousel(size_t panel_index, int center_x, int y) { - const std::string& carousel = carousel_.at(panel_index); - if (carousel.empty()) { + // Obtener referencia a EnterName + EnterName* enter_name = enter_name_ref_.at(panel_index); + if (!enter_name) { + return; + } + + // Obtener la lista completa de caracteres + const std::string& char_list = enter_name->getCharacterList(); + if (char_list.empty()) { return; } @@ -510,11 +533,11 @@ void Scoreboard::renderCarousel(size_t panel_index, int center_x, int y) { constexpr int VISIBLE_LETTERS = 9; constexpr int HALF_VISIBLE = VISIBLE_LETTERS / 2; // 4 - // Posición flotante actual del carrusel + // Posición flotante actual del carrusel (índice en character_list_) const float carousel_pos = carousel_position_.at(panel_index); - // Calcular ancho promedio de una letra (asumimos ancho uniforme para simplificar) - std::string sample_char(1, carousel[carousel.size() / 2]); + // Calcular ancho promedio de una letra (asumimos ancho uniforme) + std::string sample_char(1, char_list[0]); const int AVG_CHAR_WIDTH = text_->length(sample_char, 1); const int CHAR_STEP = AVG_CHAR_WIDTH + EXTRA_SPACING; @@ -522,28 +545,29 @@ void Scoreboard::renderCarousel(size_t panel_index, int center_x, int y) { const float fractional_offset = carousel_pos - std::floor(carousel_pos); const int pixel_offset = static_cast(fractional_offset * CHAR_STEP); - // Índice base (centro del carrusel) + // Índice base en character_list_ (centro del carrusel) const int base_index = static_cast(std::floor(carousel_pos)); + const int char_list_size = static_cast(char_list.size()); // Calcular posición X inicial (centrar el conjunto de 9 letras) int start_x = center_x - (HALF_VISIBLE * CHAR_STEP) - (AVG_CHAR_WIDTH / 2) - pixel_offset; + // Detectar si el carrusel está en movimiento + const bool is_animating = std::abs(carousel_pos - carousel_target_.at(panel_index)) > 0.01f; + // Renderizar las 9 letras visibles for (int i = -HALF_VISIBLE; i <= HALF_VISIBLE; ++i) { - // Índice real en la lista de caracteres (con wrap-around circular) + // Índice real en character_list_ (con wrap-around circular) int char_index = base_index + i; - const int carousel_size = static_cast(carousel.size()); // Wrap-around circular - while (char_index < 0) { - char_index += carousel_size; - } - while (char_index >= carousel_size) { - char_index -= carousel_size; + char_index = char_index % char_list_size; + if (char_index < 0) { + char_index += char_list_size; } - // Obtener el carácter - std::string single_char(1, carousel[char_index]); + // Obtener el carácter directamente de character_list_ + std::string single_char(1, char_list[char_index]); // Calcular distancia flotante al centro visual const float distance_from_center = std::abs(static_cast(i) + fractional_offset); @@ -551,13 +575,22 @@ void Scoreboard::renderCarousel(size_t panel_index, int center_x, int y) { // Calcular color con LERP dinámico Color letter_color; - if (distance_from_center < 0.5f) { - // Letra central: animated_color_ + if (distance_from_center < 0.5f && !is_animating) { + // Letra central Y carrusel quieto: animated_color_ letter_color = animated_color_; } else { - // Letras laterales: LERP desde text_color1_ hacia color_ - // Factor: 0.0 cerca del centro, 0.85 en los extremos (85% fade) - const float LERP_FACTOR = std::min((distance_from_center - 0.5f) / (HALF_VISIBLE - 0.5f), 1.0f) * 0.85f; + // Todas las demás letras O carrusel en movimiento: LERP + float base_lerp; + if (distance_from_center < 0.5f) { + // Letra muy cerca del centro durante animación: 0% (color brillante) + base_lerp = 0.0f; + } else { + // Letras alejadas: lerp proporcional a la distancia + base_lerp = (distance_from_center - 0.5f) / (HALF_VISIBLE - 0.5f); + base_lerp = std::min(base_lerp, 1.0f); + } + + const float LERP_FACTOR = base_lerp * 0.85f; letter_color = text_color1_.LERP(color_, LERP_FACTOR); } diff --git a/source/scoreboard.h b/source/scoreboard.h index 39dc56f..73e1798 100644 --- a/source/scoreboard.h +++ b/source/scoreboard.h @@ -8,6 +8,9 @@ #include // Para string, basic_string #include // Para vector +// Forward declarations +class EnterName; + #include "color.h" // Para Color class Sprite; @@ -65,7 +68,7 @@ class Scoreboard { void setPower(float power) { power_ = power; } void setEnterName(Id id, const std::string& enter_name) { enter_name_.at(static_cast(id)) = enter_name; } void setCharacterSelected(Id id, const std::string& character_selected) { character_selected_.at(static_cast(id)) = character_selected; } - void setCarousel(Id id, const std::string& carousel); // Implementación en .cpp para detectar cambios + void setCarouselAnimation(Id id, int selected_index, EnterName* enter_name_ptr); // Configura la animación del carrusel void setScore(Id id, int score) { score_.at(static_cast(id)) = score; } void setSelectorPos(Id id, int pos) { selector_pos_.at(static_cast(id)) = pos; } void setStage(int stage) { stage_ = stage; } @@ -83,10 +86,10 @@ class Scoreboard { std::array(Id::SIZE)> name_ = {}; // Nombre de cada jugador std::array(Id::SIZE)> enter_name_ = {}; // Nombre introducido para la tabla de records std::array(Id::SIZE)> character_selected_ = {}; // Caracter seleccionado - std::array(Id::SIZE)> carousel_ = {}; // Caracter seleccionado - std::array(Id::SIZE)> carousel_position_ = {}; // Posición actual del carrusel (animación suave) - std::array(Id::SIZE)> carousel_target_ = {}; // Posición objetivo del carrusel - std::array(Id::SIZE)> carousel_prev_index_ = {}; // Índice previo para detectar cambios + std::array(Id::SIZE)> enter_name_ref_ = {}; // Referencias a EnterName para obtener character_list_ + std::array(Id::SIZE)> carousel_position_ = {}; // Posición actual del carrusel (índice en character_list_) + std::array(Id::SIZE)> carousel_target_ = {}; // Posición objetivo del carrusel + std::array(Id::SIZE)> carousel_prev_index_ = {}; // Índice previo para detectar cambios std::array(Id::SIZE)> panel_ = {}; // Lista con todos los paneles del marcador Colors::Cycle name_color_cycle_; // Ciclo de colores para destacar el nombre una vez introducido Color animated_color_; // Color actual animado (ciclo automático cada 100ms)