reestructuració
This commit is contained in:
@@ -0,0 +1,399 @@
|
||||
#include "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 "audio.hpp" // Para Audio
|
||||
#include "background.hpp" // Para Background
|
||||
#include "color.hpp" // Para Color, easeOutQuint, Colors::NO_COLOR_MOD
|
||||
#include "fade.hpp" // Para Fade, FadeMode, FadeType
|
||||
#include "global_events.hpp" // Para check
|
||||
#include "global_inputs.hpp" // Para check
|
||||
#include "input.hpp" // Para Input
|
||||
#include "lang.hpp" // Para getText
|
||||
#include "manage_hiscore_table.hpp" // Para HiScoreEntry
|
||||
#include "options.hpp" // Para SettingsOptions, settings
|
||||
#include "param.hpp" // Para Param, param, ParamGame, ParamFade
|
||||
#include "path_sprite.hpp" // Para PathSprite, Path, PathType
|
||||
#include "resource.hpp" // Para Resource
|
||||
#include "screen.hpp" // Para Screen
|
||||
#include "section.hpp" // Para Name, name, Options, options
|
||||
#include "sprite.hpp" // Para Sprite
|
||||
#include "text.hpp" // Para Text, Text::SHADOW, Text::COLOR
|
||||
#include "texture.hpp" // Para Texture
|
||||
#include "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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user