Files
coffee-crisis-ae/source/sections/hiscore_table.cpp
T
JailDesigner 3964503f1c game fix: la velocitat dels globos dins de la fase actual muntava al primer globo explotat
game fix: al trencar una powerball ja no eixien mes globos
style: renombrades variables i funcions
2025-08-14 20:41:44 +02:00

369 lines
13 KiB
C++

#include "hiscore_table.h"
#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderTarget
#include <algorithm> // Para max
#include <cstdlib> // Para rand, size_t
#include <functional> // Para function
#include <vector> // Para vector
#include "audio.h" // Para Audio
#include "background.h" // Para Background
#include "color.h" // Para Color, easeOutQuint, NO_TEXT_COLOR
#include "fade.h" // Para Fade, FadeMode, FadeType
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input
#include "lang.h" // Para getText
#include "manage_hiscore_table.h" // Para HiScoreEntry
#include "options.h" // Para SettingsOptions, settings
#include "param.h" // Para Param, param, ParamGame, ParamFade
#include "path_sprite.h" // Para PathSprite, Path, PathType
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "section.hpp" // Para Name, name, Options, options
#include "sprite.h" // Para Sprite
#include "text.h" // Para Text, Text::SHADOW, Text::COLOR
#include "texture.h" // Para Texture
#include "utils.h"
// 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>()),
ticks_(0),
view_area_(SDL_FRect{0, 0, param.game.width, param.game.height}),
fade_mode_(FadeMode::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();
}
// Destructor
HiScoreTable::~HiScoreTable() {
SDL_DestroyTexture(backbuffer_);
Options::settings.clearLastHiScoreEntries();
}
// Actualiza las variables
void HiScoreTable::update() {
if (SDL_GetTicks() - ticks_ > param.game.speed) {
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
Screen::get()->update(); // Actualiza el objeto screen
updateSprites(); // Actualiza las posiciones de los sprites de texto
background_->update(); // Actualiza el fondo
updateFade(); // Gestiona el fade
updateCounter(); // Gestiona el contador y sus eventos
fillTexture(); // Dibuja los sprites en la textura
}
Audio::update();
}
// 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
view_area_.y = std::max(0.0F, param.game.height - counter_ + 100); // Establece la ventana del backbuffer
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();
}
// Bucle para la pantalla de instrucciones
void HiScoreTable::run() {
Audio::get()->playMusic("title.ogg");
while (Section::name == Section::Name::HI_SCORE_TABLE) {
checkInput();
update();
checkEvents(); // Tiene que ir antes del render
render();
}
}
// Gestiona el fade
void HiScoreTable::updateFade() {
fade_->update();
if (fade_->hasEnded() && fade_mode_ == FadeMode::IN) {
fade_->reset();
fade_mode_ = FadeMode::OUT;
fade_->setMode(fade_mode_);
}
if (fade_->hasEnded() && fade_mode_ == FadeMode::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 = (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_LENGHT = 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_LENGHT + 3, ' ');
auto sample_entry = std::make_unique<Sprite>(entry_text->writeDXToTexture(Text::SHADOW, SAMPLE_LINE, 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR));
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_LENGHT - 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; j < (int)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, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR)));
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;
constexpr int STEPS = 80;
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, STEPS, easeOutQuint);
entry_names_.back()->setPosition(-entry_names_.back()->getWidth(), 0);
} else {
entry_names_.back()->addPath(backbuffer_width, POS_X, PathType::HORIZONTAL, POS_Y, STEPS, 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, STEPS, 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, STEPS, 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, STEPS, easeOutQuint);
entry_names_.back()->setPosition(0, backbuffer_height);
}
default:
break;
}
}
}
// Actualiza las posiciones de los sprites de texto
void HiScoreTable::updateSprites() {
constexpr int INIT_COUNTER = 190;
const int COUNTER_BETWEEN_ENTRIES = 16;
if (counter_ >= INIT_COUNTER) {
const int COUNTER2 = counter_ - INIT_COUNTER;
if (COUNTER2 % COUNTER_BETWEEN_ENTRIES == 0) {
int index = COUNTER2 / COUNTER_BETWEEN_ENTRIES;
if (index < static_cast<int>(entry_names_.size())) {
entry_names_.at(index)->enable();
}
}
}
for (auto const &entry : entry_names_) {
entry->update();
}
glowEntryNames();
}
// Inicializa el fade
void HiScoreTable::initFade() {
fade_->setColor(param.fade.color);
fade_->setType(FadeType::RANDOM_SQUARE);
fade_->setPostDuration(param.fade.post_duration);
fade_->setMode(fade_mode_);
fade_->activate();
}
// Inicializa el fondo
void HiScoreTable::initBackground() {
background_->setPos(param.game.game_area.rect);
background_->setCloudsSpeed(-0.1F);
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_ = GREEN_SKY_COLOR;
break;
}
case 1: // Fondo naranja
{
background_->setGradientNumber(1);
background_->setTransition(0.0F);
background_->setSunProgression(0.65F);
background_->setMoonProgression(0.0F);
background_fade_color_ = PINK_SKY_COLOR;
break;
}
case 2: // Fondo azul
{
background_->setGradientNumber(0);
background_->setTransition(0.0F);
background_->setSunProgression(0.0F);
background_->setMoonProgression(0.0F);
background_fade_color_ = BLUE_SKY_COLOR;
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() {
const Color ENTRY_COLOR = getEntryColor(counter_ / 5);
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() {
++counter_;
if (counter_ == 150) {
background_->setColor(background_fade_color_.DARKEN());
background_->setAlpha(96);
}
if (counter_ == COUNTER_END) {
fade_->activate();
}
}