#include "scoreboard.h" #include // Para SDL_DestroyTexture, SDL_SetRenderDrawColor #include // Para roundf #include // Para operator<<, setfill, setw #include // Para basic_ostringstream, basic_ostream, basic_os... #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 // [SINGLETON] Hay que definir las variables estáticas, desde el .h sólo la hemos declarado Scoreboard *Scoreboard::scoreboard_ = nullptr; // [SINGLETON] Crearemos el objeto score_board con esta función estática void Scoreboard::init() { Scoreboard::scoreboard_ = new Scoreboard(); } // [SINGLETON] Destruiremos el objeto score_board con esta función estática void Scoreboard::destroy() { delete Scoreboard::scoreboard_; } // [SINGLETON] Con este método obtenemos el objeto score_board y podemos trabajar con él Scoreboard *Scoreboard::get() { return Scoreboard::scoreboard_; } // Constructor Scoreboard::Scoreboard() : renderer_(Screen::get()->getRenderer()), game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")), power_meter_sprite_(std::make_unique(game_power_meter_texture_)), text_scoreboard_(Resource::get()->getText("8bithud")) { // Inicializa variables for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) { name_[i].clear(); record_name_[i].clear(); selector_pos_[i] = 0; score_[i] = 0; mult_[i] = 0; continue_counter_[i] = 0; } panel_[SCOREBOARD_LEFT_PANEL].mode = ScoreboardMode::SCORE; panel_[SCOREBOARD_RIGHT_PANEL].mode = ScoreboardMode::SCORE; panel_[SCOREBOARD_CENTER_PANEL].mode = ScoreboardMode::STAGE_INFO; // Recalcula las anclas de los elementos recalculateAnchors(); power_meter_sprite_->setPosition(SDL_FRect{ static_cast(slot4_2_.x - 20), static_cast(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 vector de colores para el nombre iniNameColors(); } Scoreboard::~Scoreboard() { if (background_) { SDL_DestroyTexture(background_); } for (auto texture : panel_texture_) { if (texture) { SDL_DestroyTexture(texture); } } } // Transforma un valor numérico en una cadena de 7 cifras std::string Scoreboard::updateScoreText(int num) { 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 la lógica del marcador void Scoreboard::update() { fillBackgroundTexture(); updateTimeCounter(); ++loop_counter_; } // 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(); iniNameColors(); } // 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 < SCOREBOARD_MAX_PANELS; ++i) { // Cambia el destino del renderizador SDL_SetRenderTarget(renderer_, panel_texture_[i]); // Dibuja el fondo de la textura SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); SDL_RenderClear(renderer_); switch (panel_[i].mode) { case ScoreboardMode::SCORE: { // SCORE text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_); text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_); // MULT text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 3"), 1, text_color1_); text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_4_.x, slot4_4_.y, "x" + std::to_string(mult_[i]).substr(0, 3), 1, text_color2_); break; } case ScoreboardMode::DEMO: { // DEMO MODE text_scoreboard_->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_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_); text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_); } break; } case ScoreboardMode::WAITING: { // GAME OVER text_scoreboard_->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_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_); text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_); } break; } case ScoreboardMode::GAME_OVER: { // GAME OVER text_scoreboard_->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_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 12"), 1, text_color1_); text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 13"), 1, text_color1_); } break; } case ScoreboardMode::STAGE_INFO: { // STAGE text_scoreboard_->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_scoreboard_->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_ == "" ? "" : hi_score_name_ + " - "; text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, name + updateScoreText(hi_score_), 1, text_color2_); break; } case ScoreboardMode::CONTINUE: { // SCORE text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_); text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_); // CONTINUE text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 10"), 1, text_color1_); text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, std::to_string(continue_counter_[i]), 1, text_color2_); break; } case ScoreboardMode::ENTER_NAME: { // SCORE text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_); text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_); // ENTER NAME { text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_); SDL_FRect rect = {enter_name_pos_.x, enter_name_pos_.y, 5.0f, 7.0f}; // Recorre todos los slots de letras del nombre for (size_t j = 0; j < NAME_SIZE; ++j) { // Selecciona el color const Color color = j < selector_pos_[i] ? text_color2_ : text_color1_; if (j != selector_pos_[i] || time_counter_ % 3 == 0) { // Dibuja la linea if (j >= selector_pos_[i]) { SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 255); SDL_RenderLine(renderer_, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h); } // Dibuja la letra if (j < record_name_[i].size()) { text_scoreboard_->writeColored(rect.x, rect.y, record_name_[i].substr(j, 1), color); } } rect.x += 7; } } break; } case ScoreboardMode::SHOW_NAME: { // SCORE text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_); text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_); // NAME text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_); /* TEXTO CENTRADO */ text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, record_name_[i], 1, getColorLikeKnightRider(name_colors_, loop_counter_ / 5)); /* TEXTO A LA IZQUIERDA */ // text_scoreboard_->writeColored(enter_name_pos_.x, enter_name_pos_.y, record_name_[i], getColorLikeKnightRider(name_colors_, loop_counter_ / 5)); break; } case ScoreboardMode::GAME_COMPLETED: { // GAME OVER text_scoreboard_->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_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 14"), 1, text_color1_); text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, updateScoreText(score_[i]), 1, text_color2_); } } default: break; } } // Deja el renderizador apuntando donde estaba SDL_SetRenderTarget(renderer_, temp); } // 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 < SCOREBOARD_MAX_PANELS; ++i) { SDL_RenderTexture(renderer_, panel_texture_[i], nullptr, &panel_[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 = (float)rect_.w / (float)SCOREBOARD_MAX_PANELS; for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) { panel_[i].pos.x = roundf(panel_width * i); panel_[i].pos.y = 0; panel_[i].pos.w = roundf(panel_width * (i + 1)) - panel_[i].pos.x; panel_[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_ = {COL, ROW1}; slot4_2_ = {COL, ROW2}; slot4_3_ = {COL, ROW3}; slot4_4_ = {COL, ROW4}; // Primer cuadrado para poner el nombre de record const int enter_name_lenght = text_scoreboard_->lenght(std::string(NAME_SIZE, 'A')); enter_name_pos_.x = COL - (enter_name_lenght / 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_) { 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 (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) { SDL_Texture *tex = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, panel_[i].pos.w, panel_[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); } // Inicializa el vector de colores para el nombre void Scoreboard::iniNameColors() { Color color = color_.INVERSE(); name_colors_.clear(); name_colors_.emplace_back(color.LIGHTEN(50)); name_colors_.emplace_back(color.LIGHTEN(25)); name_colors_.emplace_back(color); name_colors_.emplace_back(color.DARKEN(25)); }