Files
coffee_crisis_arcade_edition/source/scoreboard.cpp
Sergio f6228ae0c1 iwyu
clang-format
2025-07-20 19:33:06 +02:00

442 lines
16 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 "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::instance = nullptr;
// [SINGLETON] Crearemos el objeto score_board con esta función estática
void Scoreboard::init() {
Scoreboard::instance = new Scoreboard();
}
// [SINGLETON] Destruiremos el objeto score_board con esta función estática
void Scoreboard::destroy() {
delete Scoreboard::instance;
}
// [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_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<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 vector de colores para el nombre
iniNameColors();
}
Scoreboard::~Scoreboard() {
if (background_ != nullptr) {
SDL_DestroyTexture(background_);
}
for (auto *texture : panel_texture_) {
if (texture != nullptr) {
SDL_DestroyTexture(texture);
}
}
}
// 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 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_);
renderPanelContent(i);
}
// Deja el renderizador apuntando donde estaba
SDL_SetRenderTarget(renderer_, temp);
}
void Scoreboard::renderPanelContent(size_t panel_index) {
switch (panel_[panel_index].mode) {
case ScoreboardMode::SCORE:
renderScoreMode(panel_index);
break;
case ScoreboardMode::DEMO:
renderDemoMode();
break;
case ScoreboardMode::WAITING:
renderWaitingMode();
break;
case ScoreboardMode::GAME_OVER:
renderGameOverMode();
break;
case ScoreboardMode::STAGE_INFO:
renderStageInfoMode();
break;
case ScoreboardMode::CONTINUE:
renderContinueMode(panel_index);
break;
case ScoreboardMode::ENTER_NAME:
renderEnterNameMode(panel_index);
break;
case ScoreboardMode::SHOW_NAME:
renderShowNameMode(panel_index);
break;
case ScoreboardMode::GAME_COMPLETED:
renderGameCompletedMode(panel_index);
break;
default:
break;
}
}
void Scoreboard::renderScoreMode(size_t panel_index) {
// SCORE
text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_1_.x, slot4_1_.y, name_[panel_index], 1, text_color1_);
text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_2_.x, slot4_2_.y, updateScoreText(score_[panel_index]), 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_[panel_index]).substr(0, 3), 1, text_color2_);
}
void Scoreboard::renderDemoMode() {
// 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_);
}
}
void Scoreboard::renderWaitingMode() {
// 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_);
}
}
void Scoreboard::renderGameOverMode() {
// 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_);
}
}
void Scoreboard::renderStageInfoMode() {
// 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_.empty() ? "" : hi_score_name_ + " - ";
text_scoreboard_->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_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[panel_index], 1, text_color1_);
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[panel_index]), 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_[panel_index]), 1, text_color2_);
}
void Scoreboard::renderEnterNameMode(size_t panel_index) {
// SCORE
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[panel_index], 1, text_color1_);
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[panel_index]), 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_);
renderNameInputField(panel_index);
}
void Scoreboard::renderNameInputField(size_t panel_index) {
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_[panel_index] ? text_color2_ : text_color1_;
if (j != selector_pos_[panel_index] || time_counter_ % 3 == 0) {
// Dibuja la linea
if (j >= selector_pos_[panel_index]) {
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_[panel_index].size()) {
text_scoreboard_->writeColored(rect.x, rect.y, record_name_[panel_index].substr(j, 1), COLOR);
}
}
rect.x += 7;
}
}
void Scoreboard::renderShowNameMode(size_t panel_index) {
// SCORE
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[panel_index], 1, text_color1_);
text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[panel_index]), 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_[panel_index], 1, getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
/* TEXTO A LA IZQUIERDA */
// text_scoreboard_->writeColored(enter_name_pos_.x, enter_name_pos_.y, record_name_[panelIndex], getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
}
void Scoreboard::renderGameCompletedMode(size_t panel_index) {
// 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_[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 < 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 = 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_ != 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);
}
// 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));
}