645 lines
25 KiB
C++
645 lines
25 KiB
C++
#include "scoreboard.h"
|
|
|
|
#include <SDL3/SDL.h> // Para SDL_DestroyTexture, SDL_SetRenderDrawColor, SDL_SetRenderTarget, SDL_CreateTexture, SDL_GetRenderTarget, SDL_GetTicks, SDL_RenderClear, SDL_RenderLine, SDL_RenderTexture, SDL_SetTextureBlendMode, SDL_FRect, SDL_BLENDMODE_BLEND, SDL_PixelFormat, SDL_Texture, SDL_TextureAccess
|
|
|
|
#include <algorithm> // Para max
|
|
#include <cmath> // Para roundf
|
|
#include <iomanip> // Para operator<<, setfill, setw
|
|
#include <sstream> // Para basic_ostream, basic_ostringstream, basic_ostream::operator<<, ostringstream
|
|
|
|
#include "color.h"
|
|
#include "enter_name.h" // Para NAME_SIZE
|
|
#include "lang.h" // Para getText
|
|
#include "param.h" // Para Param, ParamScoreboard, param
|
|
#include "resource.h" // Para Resource
|
|
#include "screen.h" // Para Screen
|
|
#include "sprite.h" // Para Sprite
|
|
#include "text.h" // Para Text, Text::CENTER, Text::COLOR
|
|
#include "texture.h" // Para Texture
|
|
|
|
// .at(SINGLETON) Hay que definir las variables estáticas, desde el .h sólo la hemos declarado
|
|
Scoreboard* Scoreboard::instance = nullptr;
|
|
|
|
// .at(SINGLETON) Crearemos el objeto score_board con esta función estática
|
|
void Scoreboard::init() {
|
|
Scoreboard::instance = new Scoreboard();
|
|
}
|
|
|
|
// .at(SINGLETON) Destruiremos el objeto score_board con esta función estática
|
|
void Scoreboard::destroy() {
|
|
delete Scoreboard::instance;
|
|
}
|
|
|
|
// .at(SINGLETON) Con este método obtenemos el objeto score_board y podemos trabajar con él
|
|
auto Scoreboard::get() -> Scoreboard* {
|
|
return Scoreboard::instance;
|
|
}
|
|
|
|
// Constructor
|
|
Scoreboard::Scoreboard()
|
|
: renderer_(Screen::get()->getRenderer()),
|
|
game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")),
|
|
power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)),
|
|
text_(Resource::get()->getText("8bithud")) {
|
|
// Inicializa variables
|
|
for (size_t i = 0; i < static_cast<size_t>(Id::SIZE); ++i) {
|
|
name_.at(i).clear();
|
|
enter_name_.at(i).clear();
|
|
selector_pos_.at(i) = 0;
|
|
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;
|
|
previous_mode_.at(i) = Mode::SCORE;
|
|
text_slide_offset_.at(i) = 0.0f;
|
|
is_sliding_.at(i) = false;
|
|
}
|
|
|
|
panel_.at(static_cast<size_t>(Id::LEFT)).mode = Mode::SCORE;
|
|
panel_.at(static_cast<size_t>(Id::RIGHT)).mode = Mode::SCORE;
|
|
panel_.at(static_cast<size_t>(Id::CENTER)).mode = Mode::STAGE_INFO;
|
|
|
|
// Recalcula las anclas de los elementos
|
|
recalculateAnchors();
|
|
power_meter_sprite_->setPosition(SDL_FRect{
|
|
static_cast<float>(slot4_2_.x - 20),
|
|
slot4_2_.y,
|
|
40,
|
|
7});
|
|
|
|
// Crea la textura de fondo
|
|
background_ = nullptr;
|
|
createBackgroundTexture();
|
|
|
|
// Crea las texturas de los paneles
|
|
createPanelTextures();
|
|
|
|
// Rellena la textura de fondo
|
|
fillBackgroundTexture();
|
|
|
|
// Inicializa el ciclo de colores para el nombre
|
|
name_color_cycle_ = Colors::generateMirroredCycle(color_.INVERSE(), ColorCycleStyle::VIBRANT);
|
|
animated_color_ = name_color_cycle_.at(0);
|
|
}
|
|
|
|
Scoreboard::~Scoreboard() {
|
|
if (background_ != nullptr) {
|
|
SDL_DestroyTexture(background_);
|
|
}
|
|
|
|
for (auto* texture : panel_texture_) {
|
|
if (texture != nullptr) {
|
|
SDL_DestroyTexture(texture);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Configura la animación del carrusel
|
|
void Scoreboard::setCarouselAnimation(Id id, int selected_index, EnterName* enter_name_ptr) {
|
|
size_t idx = static_cast<size_t>(id);
|
|
|
|
// Guardar referencia a EnterName
|
|
enter_name_ref_.at(idx) = enter_name_ptr;
|
|
|
|
if (!enter_name_ptr || selected_index < 0) {
|
|
return;
|
|
}
|
|
|
|
// Primera inicialización: posicionar directamente sin animar
|
|
if (carousel_prev_index_.at(idx) == -1) {
|
|
carousel_position_.at(idx) = static_cast<float>(selected_index);
|
|
carousel_target_.at(idx) = static_cast<float>(selected_index);
|
|
carousel_prev_index_.at(idx) = selected_index;
|
|
} else {
|
|
// Detectar cambio en el índice del carácter seleccionado
|
|
int prev_index = carousel_prev_index_.at(idx);
|
|
|
|
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<float>(direction);
|
|
|
|
// Guardar nuevo índice
|
|
carousel_prev_index_.at(idx) = selected_index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Transforma un valor numérico en una cadena de 7 cifras
|
|
auto Scoreboard::updateScoreText(int num) -> std::string {
|
|
std::ostringstream oss;
|
|
oss << std::setw(7) << std::setfill('0') << num;
|
|
return oss.str();
|
|
}
|
|
|
|
// Actualiza el contador
|
|
void Scoreboard::updateTimeCounter() {
|
|
constexpr int TICKS_SPEED = 100;
|
|
|
|
if (SDL_GetTicks() - ticks_ > TICKS_SPEED) {
|
|
ticks_ = SDL_GetTicks();
|
|
++time_counter_;
|
|
}
|
|
}
|
|
|
|
// Actualiza el índice del color animado del nombre
|
|
void Scoreboard::updateNameColorIndex() {
|
|
constexpr Uint64 COLOR_UPDATE_INTERVAL = 100; // 100ms entre cambios de color
|
|
|
|
if (SDL_GetTicks() - name_color_last_update_ >= COLOR_UPDATE_INTERVAL) {
|
|
++name_color_index_;
|
|
name_color_last_update_ = SDL_GetTicks();
|
|
}
|
|
|
|
// Precalcular el color actual del ciclo
|
|
animated_color_ = name_color_cycle_.at(name_color_index_ % name_color_cycle_.size());
|
|
}
|
|
|
|
// Actualiza la animación del carrusel
|
|
void Scoreboard::updateCarouselAnimation(float deltaTime) {
|
|
constexpr float CAROUSEL_SPEED = 8.0f; // Posiciones por segundo
|
|
|
|
for (size_t i = 0; i < carousel_position_.size(); ++i) {
|
|
// Solo animar si no hemos llegado al target
|
|
if (std::abs(carousel_position_.at(i) - carousel_target_.at(i)) > 0.01f) {
|
|
// Determinar dirección
|
|
float direction = (carousel_target_.at(i) > carousel_position_.at(i)) ? 1.0f : -1.0f;
|
|
|
|
// Calcular movimiento
|
|
float movement = CAROUSEL_SPEED * deltaTime * direction;
|
|
|
|
// Mover, pero no sobrepasar el target
|
|
float new_position = carousel_position_.at(i) + movement;
|
|
|
|
// Clamp para no sobrepasar
|
|
if (direction > 0) {
|
|
carousel_position_.at(i) = std::min(new_position, carousel_target_.at(i));
|
|
} else {
|
|
carousel_position_.at(i) = std::max(new_position, carousel_target_.at(i));
|
|
}
|
|
} else {
|
|
// Forzar al target exacto cuando estamos muy cerca
|
|
carousel_position_.at(i) = carousel_target_.at(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Actualiza la animación de deslizamiento de texto (transición ENTER_NAME -> SHOW_NAME)
|
|
void Scoreboard::updateTextSlideAnimation(float deltaTime) {
|
|
const float ROW_SIZE = rect_.h / 4.0f; // Altura de una fila
|
|
|
|
for (size_t i = 0; i < static_cast<size_t>(Id::SIZE); ++i) {
|
|
Mode current_mode = panel_.at(i).mode;
|
|
|
|
// Detectar transición de ENTER_NAME a SHOW_NAME
|
|
if (previous_mode_.at(i) == Mode::ENTER_NAME && current_mode == Mode::SHOW_NAME) {
|
|
// Iniciar animación
|
|
is_sliding_.at(i) = true;
|
|
text_slide_offset_.at(i) = 0.0f;
|
|
}
|
|
|
|
// Actualizar offset durante animación
|
|
if (is_sliding_.at(i)) {
|
|
// Incrementar offset basado en tiempo
|
|
float slide_speed = ROW_SIZE / TEXT_SLIDE_DURATION; // Píxeles por segundo
|
|
text_slide_offset_.at(i) += slide_speed * deltaTime;
|
|
|
|
// Terminar animación cuando se complete
|
|
if (text_slide_offset_.at(i) >= ROW_SIZE) {
|
|
text_slide_offset_.at(i) = ROW_SIZE;
|
|
is_sliding_.at(i) = false;
|
|
}
|
|
}
|
|
|
|
// Actualizar modo previo para la próxima iteración
|
|
previous_mode_.at(i) = current_mode;
|
|
}
|
|
}
|
|
|
|
// Actualiza la lógica del marcador
|
|
void Scoreboard::update(float deltaTime) {
|
|
fillBackgroundTexture();
|
|
updateTimeCounter();
|
|
updateNameColorIndex();
|
|
updateCarouselAnimation(deltaTime);
|
|
updateTextSlideAnimation(deltaTime);
|
|
}
|
|
|
|
// Pinta el marcador
|
|
void Scoreboard::render() {
|
|
SDL_RenderTexture(renderer_, background_, nullptr, &rect_);
|
|
}
|
|
|
|
// Establece el valor de la variable
|
|
void Scoreboard::setColor(Color color) {
|
|
// Actualiza las variables de colores
|
|
color_ = color;
|
|
text_color1_ = param.scoreboard.text_autocolor ? color_.LIGHTEN(100) : param.scoreboard.text_color1;
|
|
text_color2_ = param.scoreboard.text_autocolor ? color_.LIGHTEN(150) : param.scoreboard.text_color2;
|
|
|
|
// Aplica los colores
|
|
power_meter_sprite_->getTexture()->setColor(text_color2_);
|
|
fillBackgroundTexture();
|
|
name_color_cycle_ = Colors::generateMirroredCycle(color_.INVERSE(), ColorCycleStyle::VIBRANT);
|
|
}
|
|
|
|
// Establece el valor de la variable
|
|
void Scoreboard::setPos(SDL_FRect rect) {
|
|
rect_ = rect;
|
|
|
|
recalculateAnchors(); // Recalcula las anclas de los elementos
|
|
createBackgroundTexture(); // Crea la textura de fondo
|
|
createPanelTextures(); // Crea las texturas de los paneles
|
|
fillBackgroundTexture(); // Rellena la textura de fondo
|
|
}
|
|
|
|
// Rellena los diferentes paneles del marcador
|
|
void Scoreboard::fillPanelTextures() {
|
|
// Guarda a donde apunta actualmente el renderizador
|
|
auto* temp = SDL_GetRenderTarget(renderer_);
|
|
|
|
// Genera el contenido de cada panel_
|
|
for (size_t i = 0; i < static_cast<int>(Id::SIZE); ++i) {
|
|
// Cambia el destino del renderizador
|
|
SDL_SetRenderTarget(renderer_, panel_texture_.at(i));
|
|
|
|
// Dibuja el fondo de la textura
|
|
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
|
|
SDL_RenderClear(renderer_);
|
|
|
|
renderPanelContent(i);
|
|
}
|
|
|
|
// Deja el renderizador apuntando donde estaba
|
|
SDL_SetRenderTarget(renderer_, temp);
|
|
}
|
|
|
|
void Scoreboard::renderPanelContent(size_t panel_index) {
|
|
switch (panel_.at(panel_index).mode) {
|
|
case Mode::SCORE:
|
|
renderScoreMode(panel_index);
|
|
break;
|
|
case Mode::DEMO:
|
|
renderDemoMode();
|
|
break;
|
|
case Mode::WAITING:
|
|
renderWaitingMode();
|
|
break;
|
|
case Mode::GAME_OVER:
|
|
renderGameOverMode();
|
|
break;
|
|
case Mode::STAGE_INFO:
|
|
renderStageInfoMode();
|
|
break;
|
|
case Mode::CONTINUE:
|
|
renderContinueMode(panel_index);
|
|
break;
|
|
case Mode::ENTER_NAME:
|
|
renderEnterNameMode(panel_index);
|
|
break;
|
|
case Mode::SHOW_NAME:
|
|
renderShowNameMode(panel_index);
|
|
break;
|
|
case Mode::GAME_COMPLETED:
|
|
renderGameCompletedMode(panel_index);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Scoreboard::renderScoreMode(size_t panel_index) {
|
|
// SCORE
|
|
text_->writeDX(Text::COLOR | Text::CENTER, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
|
text_->writeDX(Text::COLOR | Text::CENTER, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
|
|
|
// MULT
|
|
text_->writeDX(Text::COLOR | Text::CENTER, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 3"), 1, text_color1_);
|
|
text_->writeDX(Text::COLOR | Text::CENTER, slot4_4_.x, slot4_4_.y, "x" + std::to_string(mult_.at(panel_index)).substr(0, 3), 1, text_color2_);
|
|
}
|
|
|
|
void Scoreboard::renderDemoMode() {
|
|
// DEMO MODE
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 6"), 1, text_color1_);
|
|
|
|
// PRESS START TO PLAY
|
|
if (time_counter_ % 10 < 8) {
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_);
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
|
|
}
|
|
}
|
|
|
|
void Scoreboard::renderWaitingMode() {
|
|
// GAME OVER
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
|
|
|
// PRESS START TO PLAY
|
|
if (time_counter_ % 10 < 8) {
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_);
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
|
|
}
|
|
}
|
|
|
|
void Scoreboard::renderGameOverMode() {
|
|
// GAME OVER
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
|
|
|
// PLEASE WAIT
|
|
if (time_counter_ % 10 < 8) {
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 12"), 1, text_color1_);
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 13"), 1, text_color1_);
|
|
}
|
|
}
|
|
|
|
void Scoreboard::renderStageInfoMode() {
|
|
// STAGE
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, Lang::getText("[SCOREBOARD] 5") + " " + std::to_string(stage_), 1, text_color1_);
|
|
|
|
// POWERMETER
|
|
power_meter_sprite_->setSpriteClip(0, 0, 40, 7);
|
|
power_meter_sprite_->render();
|
|
power_meter_sprite_->setSpriteClip(40, 0, int(power_ * 40.0F), 7);
|
|
power_meter_sprite_->render();
|
|
|
|
// HI-SCORE
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 4"), 1, text_color1_);
|
|
const std::string NAME = hi_score_name_.empty() ? "" : hi_score_name_ + " - ";
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, NAME + updateScoreText(hi_score_), 1, text_color2_);
|
|
}
|
|
|
|
void Scoreboard::renderContinueMode(size_t panel_index) {
|
|
// SCORE
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
|
|
|
// CONTINUE
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 10"), 1, text_color1_);
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, std::to_string(continue_counter_.at(panel_index)), 1, text_color2_);
|
|
}
|
|
|
|
void Scoreboard::renderEnterNameMode(size_t panel_index) {
|
|
/*
|
|
// SCORE
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
|
|
|
// ENTER NAME
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
|
|
|
renderNameInputField(panel_index);
|
|
*/
|
|
|
|
// SCORE
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
|
|
|
// ENTER NAME
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
|
|
|
// NAME
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, enter_name_.at(panel_index), 1, text_color2_);
|
|
|
|
// CARRUSEL
|
|
renderCarousel(panel_index, slot4_4_.x, slot4_4_.y);
|
|
}
|
|
|
|
void Scoreboard::renderShowNameMode(size_t panel_index) {
|
|
// Calcular offset de animación de deslizamiento
|
|
const float ROW_SIZE = rect_.h / 4.0f;
|
|
float y_offset = 0.0f;
|
|
|
|
if (is_sliding_.at(panel_index)) {
|
|
// Durante animación: offset va de -ROW_SIZE (arriba) a 0 (posición final)
|
|
y_offset = -ROW_SIZE + text_slide_offset_.at(panel_index);
|
|
}
|
|
|
|
// NOMBRE DEL JUGADOR (texto0 - entra desde arriba)
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + y_offset, name_.at(panel_index), 1, text_color1_);
|
|
|
|
// SCORE (texto1 - se desplaza hacia abajo)
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y + y_offset, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
|
|
|
// "ENTER NAME" (texto2 - se desplaza hacia abajo)
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y + y_offset, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
|
|
|
// NOMBRE INTRODUCIDO (texto3 - se desplaza hacia abajo)
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y + y_offset, enter_name_.at(panel_index), 1, animated_color_);
|
|
}
|
|
|
|
void Scoreboard::renderGameCompletedMode(size_t panel_index) {
|
|
// GAME OVER
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
|
|
|
// SCORE
|
|
if (time_counter_ % 10 < 8) {
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 14"), 1, text_color1_);
|
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
|
}
|
|
}
|
|
|
|
// Rellena la textura de fondo
|
|
void Scoreboard::fillBackgroundTexture() {
|
|
// Rellena los diferentes paneles del marcador
|
|
fillPanelTextures();
|
|
|
|
// Cambia el destino del renderizador
|
|
SDL_Texture* temp = SDL_GetRenderTarget(renderer_);
|
|
SDL_SetRenderTarget(renderer_, background_);
|
|
|
|
// Dibuja el fondo del marcador
|
|
SDL_SetRenderDrawColor(renderer_, color_.r, color_.g, color_.b, 255);
|
|
SDL_RenderClear(renderer_);
|
|
|
|
// Copia las texturas de los paneles
|
|
for (int i = 0; i < static_cast<int>(Id::SIZE); ++i) {
|
|
SDL_RenderTexture(renderer_, panel_texture_.at(i), nullptr, &panel_.at(i).pos);
|
|
}
|
|
|
|
// Dibuja la linea que separa la zona de juego del marcador
|
|
renderSeparator();
|
|
|
|
// Deja el renderizador apuntando donde estaba
|
|
SDL_SetRenderTarget(renderer_, temp);
|
|
}
|
|
|
|
// Recalcula las anclas de los elementos
|
|
void Scoreboard::recalculateAnchors() {
|
|
// Recalcula la posición y el tamaño de los paneles
|
|
const float PANEL_WIDTH = rect_.w / (float)static_cast<int>(Id::SIZE);
|
|
for (int i = 0; i < static_cast<int>(Id::SIZE); ++i) {
|
|
panel_.at(i).pos.x = roundf(PANEL_WIDTH * i);
|
|
panel_.at(i).pos.y = 0;
|
|
panel_.at(i).pos.w = roundf(PANEL_WIDTH * (i + 1)) - panel_.at(i).pos.x;
|
|
panel_.at(i).pos.h = rect_.h;
|
|
}
|
|
|
|
// Constantes para definir las zonas del panel_: 4 filas y 1 columna
|
|
const int ROW_SIZE = rect_.h / 4;
|
|
const int TEXT_HEIGHT = 7;
|
|
|
|
// Filas
|
|
const float ROW1 = 1 + (ROW_SIZE * 0) + (TEXT_HEIGHT / 2);
|
|
const float ROW2 = 1 + (ROW_SIZE * 1) + (TEXT_HEIGHT / 2) - 1;
|
|
const float ROW3 = 1 + (ROW_SIZE * 2) + (TEXT_HEIGHT / 2) - 2;
|
|
const float ROW4 = 1 + (ROW_SIZE * 3) + (TEXT_HEIGHT / 2) - 3;
|
|
|
|
// Columna
|
|
const float COL = PANEL_WIDTH / 2;
|
|
|
|
// Slots de 4
|
|
slot4_1_ = {.x = COL, .y = ROW1};
|
|
slot4_2_ = {.x = COL, .y = ROW2};
|
|
slot4_3_ = {.x = COL, .y = ROW3};
|
|
slot4_4_ = {.x = COL, .y = ROW4};
|
|
|
|
// Primer cuadrado para poner el nombre de record
|
|
const int ENTER_NAME_LENGTH = text_->length(std::string(EnterName::MAX_NAME_SIZE, 'A'));
|
|
enter_name_pos_.x = COL - (ENTER_NAME_LENGTH / 2);
|
|
enter_name_pos_.y = ROW4;
|
|
|
|
// Recoloca los sprites
|
|
if (power_meter_sprite_) {
|
|
power_meter_sprite_->setX(slot4_2_.x - 20);
|
|
power_meter_sprite_->setY(slot4_2_.y);
|
|
}
|
|
}
|
|
|
|
// Crea la textura de fondo
|
|
void Scoreboard::createBackgroundTexture() {
|
|
// Elimina la textura en caso de existir
|
|
if (background_ != nullptr) {
|
|
SDL_DestroyTexture(background_);
|
|
}
|
|
|
|
// Recrea la textura de fondo
|
|
background_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h);
|
|
SDL_SetTextureBlendMode(background_, SDL_BLENDMODE_BLEND);
|
|
}
|
|
|
|
// Crea las texturas de los paneles
|
|
void Scoreboard::createPanelTextures() {
|
|
// Elimina las texturas en caso de existir
|
|
for (auto* texture : panel_texture_) {
|
|
if (texture != nullptr) {
|
|
SDL_DestroyTexture(texture);
|
|
}
|
|
}
|
|
panel_texture_.clear();
|
|
|
|
// Crea las texturas para cada panel_
|
|
for (auto& i : panel_) {
|
|
SDL_Texture* tex = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, i.pos.w, i.pos.h);
|
|
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
|
panel_texture_.push_back(tex);
|
|
}
|
|
}
|
|
|
|
// Dibuja la linea que separa la zona de juego del marcador
|
|
void Scoreboard::renderSeparator() {
|
|
// Dibuja la linea que separa el marcador de la zona de juego
|
|
auto color = param.scoreboard.separator_autocolor ? color_.DARKEN() : param.scoreboard.separator_color;
|
|
SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 255);
|
|
SDL_RenderLine(renderer_, 0, 0, rect_.w, 0);
|
|
}
|
|
|
|
// 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) {
|
|
// 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;
|
|
}
|
|
|
|
// Espacio extra entre letras
|
|
constexpr int EXTRA_SPACING = 2;
|
|
|
|
// Carrusel extendido: usar constante de clase
|
|
constexpr int HALF_VISIBLE = CAROUSEL_VISIBLE_LETTERS / 2; // 4
|
|
|
|
// 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)
|
|
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;
|
|
|
|
// Calcular offset de píxeles basado en la parte fraccionaria de carousel_pos
|
|
const float fractional_offset = carousel_pos - std::floor(carousel_pos);
|
|
const int pixel_offset = static_cast<int>(fractional_offset * CHAR_STEP);
|
|
|
|
// Índice base en character_list_ (centro del carrusel)
|
|
const int base_index = static_cast<int>(std::floor(carousel_pos));
|
|
const int char_list_size = static_cast<int>(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;
|
|
|
|
// Renderizar las 9 letras visibles
|
|
for (int i = -HALF_VISIBLE; i <= HALF_VISIBLE; ++i) {
|
|
// Índice real en character_list_ (con wrap-around circular)
|
|
int char_index = base_index + i;
|
|
|
|
// Wrap-around circular
|
|
char_index = char_index % char_list_size;
|
|
if (char_index < 0) {
|
|
char_index += char_list_size;
|
|
}
|
|
|
|
// Obtener el carácter directamente de character_list_
|
|
std::string single_char(1, char_list[char_index]);
|
|
|
|
// Calcular distancia flotante al centro visual basada en posición real del carácter
|
|
float distance_from_center = std::abs(static_cast<float>(char_index) - carousel_pos);
|
|
|
|
// Manejar wrap-around circular: elegir el camino más corto
|
|
if (distance_from_center > static_cast<float>(char_list_size) / 2.0f) {
|
|
distance_from_center = static_cast<float>(char_list_size) - distance_from_center;
|
|
}
|
|
|
|
// Calcular color con LERP dinámico continuo
|
|
Color letter_color;
|
|
|
|
if (distance_from_center < 0.5f) {
|
|
// Letra cerca del centro: LERP hacia animated_color_
|
|
// distance_from_center va de 0.0 (centro exacto) a 0.5 (borde)
|
|
float lerp_to_animated = distance_from_center / 0.5f; // 0.0 a 1.0
|
|
letter_color = animated_color_.LERP(text_color1_, lerp_to_animated);
|
|
} else {
|
|
// Letras alejadas: LERP hacia color_ (fade out)
|
|
float 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);
|
|
}
|
|
|
|
// Calcular posición X de esta letra
|
|
const int letter_x = start_x + (i + HALF_VISIBLE) * CHAR_STEP;
|
|
|
|
// Pintar la letra
|
|
text_->writeDX(Text::COLOR, letter_x, y, single_char, 1, letter_color);
|
|
}
|
|
} |