#include "game/gameplay/scoreboard.hpp" #include #include #include "core/locale/locale.hpp" // Para Locale #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/sprite/animated_sprite.hpp" // Para SAnimatedSprite #include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/text.hpp" // Para Text #include "core/resources/resource_cache.hpp" // Para Resource #include "game/entities/player.hpp" // Para Player::skinToAnimationPath #include "game/options.hpp" // Para Options, options, Cheat, OptionsGame #include "utils/defines.hpp" // Para BLOCK #include "utils/utils.hpp" // Constructor Scoreboard::Scoreboard(std::shared_ptr data) : item_surface_(Resource::Cache::get()->getSurface("items.gif")), data_(std::move(std::move(data))) { const float SURFACE_WIDTH = Options::game.width; constexpr float SURFACE_HEIGHT = 6.0F * Tile::SIZE; // Reserva memoria para los objetos const std::string PLAYER_ANIM_PATH = Player::skinToAnimationPath(Options::game.player_skin); const auto& player_animation_data = Resource::Cache::get()->getAnimationData(PLAYER_ANIM_PATH); player_sprite_ = std::make_shared(player_animation_data); player_sprite_->setCurrentAnimation("default"); surface_ = std::make_shared(SURFACE_WIDTH, SURFACE_HEIGHT); surface_dest_ = {.x = 0, .y = Options::game.height - SURFACE_HEIGHT, .w = SURFACE_WIDTH, .h = SURFACE_HEIGHT}; // Inicializa el color de items items_color_ = 14; // Inicializa el vector de colores color_ = {2, 6, 8, 10, 12, 14, 3, 7, 9, 11, 13, 15}; } // Pinta el objeto en pantalla void Scoreboard::render() { return; surface_->render(nullptr, &surface_dest_); } // Actualiza las variables del objeto void Scoreboard::update(float delta_time) { // Acumular tiempo para animaciones time_accumulator_ += delta_time; // Actualiza el color de la cantidad de items recogidos updateItemsColor(delta_time); // Dibuja la textura fillTexture(); if (!is_paused_) { // Si está en pausa no se actualiza el reloj clock_ = getTime(); } } // Obtiene el tiempo transcurrido de partida auto Scoreboard::getTime() -> Scoreboard::ClockData { // NOLINT(readability-convert-member-functions-to-static) const Uint32 TIME_ELAPSED = SDL_GetTicks() - data_->ini_clock - paused_time_elapsed_; ClockData time; time.hours = TIME_ELAPSED / 3600000; time.minutes = TIME_ELAPSED / 60000; time.seconds = TIME_ELAPSED / 1000; time.separator = (TIME_ELAPSED % 1000 <= 500) ? ":" : " "; return time; } // Actualiza el sprite del jugador con la skin actual void Scoreboard::refreshPlayerSkin() { const std::string PLAYER_ANIM_PATH = Player::skinToAnimationPath(Options::game.player_skin); const auto& player_animation_data = Resource::Cache::get()->getAnimationData(PLAYER_ANIM_PATH); player_sprite_ = std::make_shared(player_animation_data); player_sprite_->setCurrentAnimation("default"); } // Pone el marcador en modo pausa void Scoreboard::setPaused(bool value) { if (is_paused_ == value) { // Evita ejecutar lógica si el estado no cambia return; } is_paused_ = value; if (is_paused_) { // Guarda el tiempo actual al pausar paused_time_ = SDL_GetTicks(); } else { // Calcula el tiempo pausado acumulado al reanudar paused_time_elapsed_ += SDL_GetTicks() - paused_time_; } } // Actualiza el color de la cantidad de items recogidos void Scoreboard::updateItemsColor(float delta_time) { if (!data_->jail_is_open) { return; } items_color_timer_ += delta_time; // Resetear timer cada 2 ciclos (0.666s total) if (items_color_timer_ >= ITEMS_COLOR_BLINK_DURATION * 2.0F) { items_color_timer_ = 0.0F; } // Alternar color cada ITEMS_COLOR_BLINK_DURATION if (items_color_timer_ < ITEMS_COLOR_BLINK_DURATION) { items_color_ = 14; } else { items_color_ = 6; } } // Devuelve la cantidad de minutos de juego transcurridos auto Scoreboard::getMinutes() -> int { return getTime().minutes; } // Dibuja los elementos del marcador en la textura void Scoreboard::fillTexture() { // Empieza a dibujar en la textura auto previuos_renderer = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(surface_); // Limpia la textura surface_->clear(0); // Dibuja las vidas const int WALK_FRAMES = player_sprite_->getCurrentAnimationSize(); const int DESP = static_cast(time_accumulator_ / SPRITE_WALK_CYCLE_DURATION) % (WALK_FRAMES * 2); const int FRAME = DESP % WALK_FRAMES; player_sprite_->setCurrentAnimationFrame(FRAME); player_sprite_->setPosY(LINE2_Y); for (int i = 0; i < data_->lives; ++i) { player_sprite_->setPosX(LIVES_START_X + (LIVES_SPACING * i) + DESP); const int INDEX = i % color_.size(); player_sprite_->render(1, color_.at(INDEX)); } // Muestra si suena la música if (data_->music) { const Uint8 C = data_->color; SDL_FRect clip = {.x = 0, .y = 8, .w = 8, .h = 8}; item_surface_->renderWithColorReplace(MUSIC_ICON_X, LINE2_Y, 1, C, &clip); } // Escribe los textos auto text = Resource::Cache::get()->getText("smb2"); const std::string TIME_TEXT = std::to_string((clock_.minutes % 100) / 10) + std::to_string(clock_.minutes % 10) + clock_.separator + std::to_string((clock_.seconds % 60) / 10) + std::to_string(clock_.seconds % 10); const std::string ITEMS_TEXT = std::to_string(data_->items / 100) + std::to_string((data_->items % 100) / 10) + std::to_string(data_->items % 10); text->writeColored(ITEMS_LABEL_X, LINE1_Y, Locale::get()->get("scoreboard.items"), data_->color); // NOLINT(readability-static-accessed-through-instance) text->writeColored(ITEMS_VALUE_X, LINE1_Y, ITEMS_TEXT, items_color_); text->writeColored(TIME_LABEL_X, LINE1_Y, Locale::get()->get("scoreboard.time"), data_->color); // NOLINT(readability-static-accessed-through-instance) text->writeColored(TIME_VALUE_X, LINE1_Y, TIME_TEXT, 14); const std::string ROOMS_TEXT = std::to_string(data_->rooms / 100) + std::to_string((data_->rooms % 100) / 10) + std::to_string(data_->rooms % 10); text->writeColored(ROOMS_LABEL_X, LINE2_Y, Locale::get()->get("scoreboard.rooms"), 14); // NOLINT(readability-static-accessed-through-instance) text->writeColored(ROOMS_VALUE_X, LINE2_Y, ROOMS_TEXT, 14); // Indicadores de trucos activos (fuente 8bithud) auto cheat_text = Resource::Cache::get()->getText("8bithud"); if (Options::cheats.infinite_lives == Options::Cheat::State::ENABLED) { cheat_text->writeColored(CHEAT_INF_LIVES_X, CHEAT_INF_LIVES_Y, Locale::get()->get("scoreboard.cheat_infinite_lives"), data_->color); // NOLINT(readability-static-accessed-through-instance) } if (Options::cheats.invincible == Options::Cheat::State::ENABLED) { cheat_text->writeColored(CHEAT_INVINCIBLE_X, CHEAT_INVINCIBLE_Y, Locale::get()->get("scoreboard.cheat_invincibility"), data_->color); // NOLINT(readability-static-accessed-through-instance) } // Deja el renderizador como estaba Screen::get()->setRendererSurface(previuos_renderer); }