399 lines
15 KiB
C++
399 lines
15 KiB
C++
#include "game/scenes/hiscore_table.hpp"
|
|
|
|
#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderTarget
|
|
|
|
#include <algorithm> // Para max
|
|
#include <cstdlib> // Para rand, size_t
|
|
#include <functional> // Para function
|
|
#include <utility> // Para std::cmp_less
|
|
#include <vector> // Para vector
|
|
|
|
#include "core/audio/audio.hpp" // Para Audio
|
|
#include "core/input/global_inputs.hpp" // Para check
|
|
#include "core/input/input.hpp" // Para Input
|
|
#include "core/locale/lang.hpp" // Para getText
|
|
#include "core/rendering/background.hpp" // Para Background
|
|
#include "core/rendering/fade.hpp" // Para Fade, FadeMode, FadeType
|
|
#include "core/rendering/screen.hpp" // Para Screen
|
|
#include "core/rendering/sprite/path_sprite.hpp" // Para PathSprite, Path, PathType
|
|
#include "core/rendering/sprite/sprite.hpp" // Para Sprite
|
|
#include "core/rendering/text.hpp" // Para Text, Text::SHADOW, Text::COLOR
|
|
#include "core/rendering/texture.hpp" // Para Texture
|
|
#include "core/resources/resource.hpp" // Para Resource
|
|
#include "core/system/global_events.hpp" // Para check
|
|
#include "core/system/section.hpp" // Para Name, name, Options, options
|
|
#include "game/gameplay/manage_hiscore_table.hpp" // Para HiScoreEntry
|
|
#include "game/options.hpp" // Para SettingsOptions, settings
|
|
#include "utils/color.hpp" // Para Color, easeOutQuint, Colors::NO_COLOR_MOD
|
|
#include "utils/param.hpp" // Para Param, param, ParamGame, ParamFade
|
|
#include "utils/utils.hpp"
|
|
|
|
// Constructor
|
|
HiScoreTable::HiScoreTable()
|
|
: renderer_(Screen::get()->getRenderer()),
|
|
backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)),
|
|
fade_(std::make_unique<Fade>()),
|
|
background_(std::make_unique<Background>()),
|
|
|
|
view_area_(SDL_FRect{.x = 0, .y = 0, .w = param.game.width, .h = param.game.height}),
|
|
fade_mode_(Fade::Mode::IN),
|
|
background_fade_color_(Color(0, 0, 0)) {
|
|
// Inicializa el resto
|
|
Section::name = Section::Name::HI_SCORE_TABLE;
|
|
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
|
|
initFade();
|
|
initBackground();
|
|
iniEntryColors();
|
|
createSprites();
|
|
|
|
// Inicializa el timer de delta time y arranca la música
|
|
last_time_ = SDL_GetTicks();
|
|
Audio::get()->playMusic("title.ogg");
|
|
}
|
|
|
|
// Destructor
|
|
HiScoreTable::~HiScoreTable() {
|
|
SDL_DestroyTexture(backbuffer_);
|
|
Options::settings.clearLastHiScoreEntries();
|
|
}
|
|
|
|
// Actualiza las variables
|
|
void HiScoreTable::update(float delta_time) {
|
|
elapsed_time_ += delta_time; // Incrementa el tiempo transcurrido
|
|
|
|
static auto* const SCREEN = Screen::get();
|
|
SCREEN->update(delta_time); // Actualiza el objeto screen
|
|
Audio::update(); // Actualiza el objeto audio
|
|
|
|
updateSprites(delta_time); // Actualiza las posiciones de los sprites de texto
|
|
background_->update(delta_time); // Actualiza el fondo
|
|
updateFade(delta_time); // Gestiona el fade
|
|
updateCounter(); // Gestiona el contador y sus eventos
|
|
fillTexture(); // Dibuja los sprites en la textura
|
|
}
|
|
|
|
// Pinta en pantalla
|
|
void HiScoreTable::render() {
|
|
static auto* const SCREEN = Screen::get();
|
|
|
|
SCREEN->start(); // Prepara para empezar a dibujar en la textura de juego
|
|
SCREEN->clean(); // Limpia la pantalla
|
|
|
|
background_->render(); // Pinta el fondo
|
|
float scroll_offset = elapsed_time_ * SCROLL_SPEED_PPS; // Calcula el desplazamiento del scroll usando velocidad en pixels/segundo
|
|
view_area_.y = std::round(std::max(0.0F, (param.game.height + 100.0F) - scroll_offset)); // Establece la ventana del backbuffer (redondeado para evitar deformaciones)
|
|
SDL_RenderTexture(renderer_, backbuffer_, nullptr, &view_area_); // Copia el backbuffer al renderizador
|
|
fade_->render(); // Renderiza el fade
|
|
|
|
SCREEN->render(); // Vuelca el contenido del renderizador en pantalla
|
|
}
|
|
|
|
// Dibuja los sprites en la textura
|
|
void HiScoreTable::fillTexture() {
|
|
// Pinta en el backbuffer el texto y los sprites
|
|
auto* temp = SDL_GetRenderTarget(renderer_);
|
|
SDL_SetRenderTarget(renderer_, backbuffer_);
|
|
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
|
|
SDL_RenderClear(renderer_);
|
|
|
|
// Escribe el texto: Mejores puntuaciones
|
|
header_->render();
|
|
|
|
// Escribe los nombres de la tabla de puntuaciones
|
|
for (auto const& entry : entry_names_) {
|
|
entry->render();
|
|
}
|
|
|
|
// Cambia el destino de renderizado
|
|
SDL_SetRenderTarget(renderer_, temp);
|
|
}
|
|
|
|
// Comprueba los eventos
|
|
void HiScoreTable::checkEvents() {
|
|
SDL_Event event;
|
|
while (SDL_PollEvent(&event)) {
|
|
GlobalEvents::handle(event);
|
|
}
|
|
}
|
|
|
|
// Comprueba las entradas
|
|
void HiScoreTable::checkInput() {
|
|
Input::get()->update();
|
|
GlobalInputs::check();
|
|
}
|
|
|
|
// Calcula el tiempo transcurrido desde el último frame
|
|
auto HiScoreTable::calculateDeltaTime() -> float {
|
|
const Uint64 CURRENT_TIME = SDL_GetTicks();
|
|
const float DELTA_TIME = static_cast<float>(CURRENT_TIME - last_time_) / 1000.0F; // Convertir ms a segundos
|
|
last_time_ = CURRENT_TIME;
|
|
return DELTA_TIME;
|
|
}
|
|
|
|
// Avanza un frame (llamado desde Director::iterate)
|
|
void HiScoreTable::iterate() {
|
|
const float DELTA_TIME = calculateDeltaTime();
|
|
checkInput();
|
|
update(DELTA_TIME);
|
|
render();
|
|
}
|
|
|
|
// Procesa un evento (llamado desde Director::handleEvent)
|
|
void HiScoreTable::handleEvent(const SDL_Event& /*event*/) {
|
|
// Eventos globales ya gestionados por Director::handleEvent
|
|
}
|
|
|
|
// Bucle para la pantalla de puntuaciones (fallback legacy)
|
|
void HiScoreTable::run() {
|
|
last_time_ = SDL_GetTicks();
|
|
Audio::get()->playMusic("title.ogg");
|
|
|
|
while (Section::name == Section::Name::HI_SCORE_TABLE) {
|
|
const float DELTA_TIME = calculateDeltaTime();
|
|
|
|
checkInput();
|
|
update(DELTA_TIME);
|
|
checkEvents(); // Tiene que ir antes del render
|
|
render();
|
|
}
|
|
}
|
|
|
|
// Gestiona el fade
|
|
void HiScoreTable::updateFade(float delta_time) {
|
|
fade_->update(delta_time);
|
|
|
|
if (fade_->hasEnded() && fade_mode_ == Fade::Mode::IN) {
|
|
(*fade_).reset();
|
|
fade_mode_ = Fade::Mode::OUT;
|
|
fade_->setMode(fade_mode_);
|
|
}
|
|
|
|
if (fade_->hasEnded() && fade_mode_ == Fade::Mode::OUT) {
|
|
Section::name = (Section::options == Section::Options::HI_SCORE_AFTER_PLAYING)
|
|
? Section::Name::TITLE
|
|
: Section::Name::INSTRUCTIONS;
|
|
Section::options = Section::Options::NONE;
|
|
}
|
|
}
|
|
|
|
// Convierte un entero a un string con separadores de miles
|
|
auto HiScoreTable::format(int number) -> std::string {
|
|
const std::string SEPARATOR = ".";
|
|
const std::string SCORE = std::to_string(number);
|
|
|
|
auto index = static_cast<int>(SCORE.size()) - 1;
|
|
std::string result;
|
|
auto i = 0;
|
|
while (index >= 0) {
|
|
result = SCORE.at(index) + result;
|
|
index--;
|
|
i++;
|
|
if (i == 3) {
|
|
i = 0;
|
|
result = SEPARATOR + result;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Crea los sprites con los textos
|
|
void HiScoreTable::createSprites() {
|
|
auto header_text = Resource::get()->getText("04b_25_grey");
|
|
auto entry_text = Resource::get()->getText("smb2");
|
|
|
|
// Obtiene el tamaño de la textura
|
|
float backbuffer_width;
|
|
float backbuffer_height;
|
|
SDL_GetTextureSize(backbuffer_, &backbuffer_width, &backbuffer_height);
|
|
|
|
constexpr int ENTRY_LENGTH = 22;
|
|
constexpr int MAX_NAMES = 10;
|
|
const int SPACE_BETWEEN_HEADER = entry_text->getCharacterSize() * 4;
|
|
const int SPACE_BETWEEN_LINES = entry_text->getCharacterSize() * 2;
|
|
const int SIZE = SPACE_BETWEEN_HEADER + (SPACE_BETWEEN_LINES * (MAX_NAMES - 1)) + entry_text->getCharacterSize();
|
|
const int FIRST_LINE = (param.game.height - SIZE) / 2;
|
|
|
|
// Crea el sprite para el texto de cabecera
|
|
header_ = std::make_unique<Sprite>(header_text->writeDXToTexture(Text::COLOR, Lang::getText("[HIGHSCORE_TABLE] CAPTION"), -2, background_fade_color_.INVERSE().LIGHTEN(25)));
|
|
header_->setPosition(param.game.game_area.center_x - (header_->getWidth() / 2), FIRST_LINE);
|
|
|
|
// Crea los sprites para las entradas en la tabla de puntuaciones
|
|
const int ANIMATION = rand() % 4;
|
|
const std::string SAMPLE_LINE(ENTRY_LENGTH + 3, ' ');
|
|
auto sample_entry = std::make_unique<Sprite>(entry_text->writeDXToTexture(Text::SHADOW, SAMPLE_LINE, 1, Colors::NO_COLOR_MOD, 1, Colors::SHADOW_TEXT));
|
|
const auto ENTRY_WIDTH = sample_entry->getWidth();
|
|
for (int i = 0; i < MAX_NAMES; ++i) {
|
|
const auto TABLE_POSITION = format(i + 1) + ". ";
|
|
const auto SCORE = format(Options::settings.hi_score_table.at(i).score);
|
|
const auto NUM_DOTS = ENTRY_LENGTH - Options::settings.hi_score_table.at(i).name.size() - SCORE.size();
|
|
const auto* const ONE_CC = Options::settings.hi_score_table.at(i).one_credit_complete ? " }" : "";
|
|
std::string dots;
|
|
for (int j = 0; std::cmp_less(j, NUM_DOTS); ++j) {
|
|
dots = dots + ".";
|
|
}
|
|
const auto LINE = TABLE_POSITION + Options::settings.hi_score_table.at(i).name + dots + SCORE + ONE_CC;
|
|
|
|
entry_names_.emplace_back(std::make_shared<PathSprite>(entry_text->writeDXToTexture(Text::SHADOW, LINE, 1, Colors::NO_COLOR_MOD, 1, Colors::SHADOW_TEXT)));
|
|
const int DEFAULT_POS_X = (backbuffer_width - ENTRY_WIDTH) / 2;
|
|
const int POS_X = (i < 9) ? DEFAULT_POS_X : DEFAULT_POS_X - entry_text->getCharacterSize();
|
|
const int POS_Y = (i * SPACE_BETWEEN_LINES) + FIRST_LINE + SPACE_BETWEEN_HEADER;
|
|
switch (ANIMATION) {
|
|
case 0: // Ambos lados alternativamente
|
|
{
|
|
if (i % 2 == 0) {
|
|
entry_names_.back()->addPath(-entry_names_.back()->getWidth(), POS_X, PathType::HORIZONTAL, POS_Y, ANIM_DURATION_S, easeOutQuint);
|
|
entry_names_.back()->setPosition(-entry_names_.back()->getWidth(), 0);
|
|
} else {
|
|
entry_names_.back()->addPath(backbuffer_width, POS_X, PathType::HORIZONTAL, POS_Y, ANIM_DURATION_S, easeOutQuint);
|
|
entry_names_.back()->setPosition(backbuffer_width, 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 1: // Entran por la izquierda
|
|
{
|
|
entry_names_.back()->addPath(-entry_names_.back()->getWidth(), POS_X, PathType::HORIZONTAL, POS_Y, ANIM_DURATION_S, easeOutQuint);
|
|
entry_names_.back()->setPosition(-entry_names_.back()->getWidth(), 0);
|
|
break;
|
|
}
|
|
|
|
case 2: // Entran por la derecha
|
|
{
|
|
entry_names_.back()->addPath(backbuffer_width, POS_X, PathType::HORIZONTAL, POS_Y, ANIM_DURATION_S, easeOutQuint);
|
|
entry_names_.back()->setPosition(backbuffer_width, 0);
|
|
break;
|
|
}
|
|
|
|
case 3: // Entran desde la parte inferior
|
|
{
|
|
entry_names_.back()->addPath(backbuffer_height, POS_Y, PathType::VERTICAL, POS_X, ANIM_DURATION_S, easeOutQuint);
|
|
entry_names_.back()->setPosition(0, backbuffer_height);
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Actualiza las posiciones de los sprites de texto
|
|
void HiScoreTable::updateSprites(float delta_time) {
|
|
if (elapsed_time_ >= INIT_DELAY_S) {
|
|
const float ELAPSED_SINCE_INIT = elapsed_time_ - INIT_DELAY_S;
|
|
int index = static_cast<int>(ELAPSED_SINCE_INIT / ENTRY_DELAY_S);
|
|
if (std::cmp_less(index, entry_names_.size()) && index >= 0) {
|
|
// Verificar si este índice debe activarse ahora
|
|
float expected_time = index * ENTRY_DELAY_S;
|
|
if (ELAPSED_SINCE_INIT >= expected_time && ELAPSED_SINCE_INIT < expected_time + delta_time) {
|
|
entry_names_.at(index)->enable();
|
|
}
|
|
}
|
|
}
|
|
for (auto const& entry : entry_names_) {
|
|
entry->update(delta_time);
|
|
}
|
|
|
|
glowEntryNames();
|
|
}
|
|
|
|
// Inicializa el fade
|
|
void HiScoreTable::initFade() {
|
|
fade_->setColor(param.fade.color);
|
|
fade_->setType(Fade::Type::RANDOM_SQUARE2);
|
|
fade_->setPostDuration(param.fade.post_duration_ms);
|
|
fade_->setMode(fade_mode_);
|
|
fade_->activate();
|
|
}
|
|
|
|
// Inicializa el fondo
|
|
void HiScoreTable::initBackground() {
|
|
background_->setManualMode(true);
|
|
background_->setPos(param.game.game_area.rect);
|
|
background_->setCloudsSpeed(CLOUDS_SPEED);
|
|
|
|
const int LUCKY = rand() % 3;
|
|
switch (LUCKY) {
|
|
case 0: // Fondo verde
|
|
{
|
|
background_->setGradientNumber(2);
|
|
background_->setTransition(0.0F);
|
|
background_->setSunProgression(1.0F);
|
|
background_->setMoonProgression(0.0F);
|
|
background_fade_color_ = Colors::GREEN_SKY;
|
|
break;
|
|
}
|
|
|
|
case 1: // Fondo naranja
|
|
{
|
|
background_->setGradientNumber(1);
|
|
background_->setTransition(0.0F);
|
|
background_->setSunProgression(0.65F);
|
|
background_->setMoonProgression(0.0F);
|
|
background_fade_color_ = Colors::PINK_SKY;
|
|
break;
|
|
}
|
|
|
|
case 2: // Fondo azul
|
|
{
|
|
background_->setGradientNumber(0);
|
|
background_->setTransition(0.0F);
|
|
background_->setSunProgression(0.0F);
|
|
background_->setMoonProgression(0.0F);
|
|
background_fade_color_ = Colors::BLUE_SKY;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Obtiene un color del vector de colores de entradas
|
|
auto HiScoreTable::getEntryColor(int counter) -> Color {
|
|
int cycle_length = (entry_colors_.size() * 2) - 2;
|
|
size_t n = counter % cycle_length;
|
|
|
|
size_t index;
|
|
if (n < entry_colors_.size()) {
|
|
index = n; // Avanza: 0,1,2,3
|
|
} else {
|
|
index = (2 * (entry_colors_.size() - 1)) - n; // Retrocede: 2,1
|
|
}
|
|
|
|
return entry_colors_[index];
|
|
}
|
|
|
|
// Inicializa los colores de las entradas
|
|
void HiScoreTable::iniEntryColors() {
|
|
entry_colors_.clear();
|
|
entry_colors_.emplace_back(background_fade_color_.INVERSE().LIGHTEN(75));
|
|
entry_colors_.emplace_back(background_fade_color_.INVERSE().LIGHTEN(50));
|
|
entry_colors_.emplace_back(background_fade_color_.INVERSE().LIGHTEN(25));
|
|
entry_colors_.emplace_back(background_fade_color_.INVERSE());
|
|
}
|
|
|
|
// Hace brillar los nombres de la tabla de records
|
|
void HiScoreTable::glowEntryNames() {
|
|
int color_counter = static_cast<int>(elapsed_time_ * 60.0F / 5.0F); // Convertir tiempo a equivalente frame
|
|
const Color ENTRY_COLOR = getEntryColor(color_counter);
|
|
for (const auto& entry_index : Options::settings.glowing_entries) {
|
|
if (entry_index != -1) {
|
|
entry_names_.at(entry_index)->getTexture()->setColor(ENTRY_COLOR);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Gestiona el contador
|
|
void HiScoreTable::updateCounter() {
|
|
if (elapsed_time_ >= BACKGROUND_CHANGE_S && !hiscore_flags_.background_changed) {
|
|
background_->setColor(background_fade_color_.DARKEN());
|
|
background_->setAlpha(96);
|
|
hiscore_flags_.background_changed = true;
|
|
}
|
|
|
|
if (elapsed_time_ >= COUNTER_END_S && !hiscore_flags_.fade_activated) {
|
|
fade_->activate();
|
|
hiscore_flags_.fade_activated = true;
|
|
}
|
|
} |