#include "core/rendering/render_info.hpp" #include #include // Para transform #include // Para round, floor #include // Para setprecision #include // Para ostringstream #include // Para string #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/text.hpp" // Para Text #include "game/options.hpp" // Para Options #include "game/ui/console.hpp" // Para Console #include "game/ui/notifier.hpp" // Para Notifier #include "utils/utils.hpp" // Para prettyName // [SINGLETON] RenderInfo* RenderInfo::render_info = nullptr; // [SINGLETON] Crearemos el objeto con esta función estática void RenderInfo::init() { RenderInfo::render_info = new RenderInfo(); } // [SINGLETON] Destruiremos el objeto con esta función estática void RenderInfo::destroy() { delete RenderInfo::render_info; RenderInfo::render_info = nullptr; } // [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él auto RenderInfo::get() -> RenderInfo* { return RenderInfo::render_info; } // Constructor: en DEBUG se activa inmediatamente (notifica a Notifier del offset) RenderInfo::RenderInfo() { #ifdef _DEBUG toggle(); #endif } // Actualiza la animación de entrada/salida del overlay void RenderInfo::update(float delta_time) { switch (status_) { case Status::RISING: y_ += SLIDE_SPEED * delta_time; if (y_ >= 0.0F) { y_ = 0.0F; status_ = Status::ACTIVE; } break; case Status::VANISHING: y_ -= SLIDE_SPEED * delta_time; if (y_ <= static_cast(-HEIGHT)) { y_ = static_cast(-HEIGHT); status_ = Status::HIDDEN; } break; default: break; } } // Renderiza el overlay de información por pantalla void RenderInfo::render() const { if (status_ == Status::HIDDEN) { return; } // FPS std::string line = std::to_string(Screen::get()->getLastFPS()) + " fps"; // Driver GPU const auto& driver = Screen::get()->getGPUDriver(); line += " | " + (driver.empty() ? std::string("sdl") : driver); // Zoom calculado (alto físico / alto lógico), con coma decimal y sin ceros innecesarios const float ROUNDED = std::round(Screen::get()->getZoomFactor() * 100.0F) / 100.0F; std::string zoom_str; if (ROUNDED == std::floor(ROUNDED)) { zoom_str = std::to_string(static_cast(ROUNDED)); } else { std::ostringstream oss; oss << std::fixed << std::setprecision(2) << ROUNDED; zoom_str = oss.str(); if (zoom_str.back() == '0') { zoom_str.pop_back(); } std::ranges::replace(zoom_str, '.', ','); } line += " | " + zoom_str + "x"; // PostFX: muestra shader + preset y supersampling, o nada si está desactivado if (Options::video.shader.enabled) { const bool IS_CRTPI = (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI); const std::string SHADER_NAME = IS_CRTPI ? "crtpi" : "postfx"; std::string preset_name = "-"; if (IS_CRTPI) { if (!Options::crtpi_presets.empty()) { preset_name = prettyName(Options::crtpi_presets[static_cast(Options::video.shader.current_crtpi_preset)].name); } } else { if (!Options::postfx_presets.empty()) { preset_name = prettyName(Options::postfx_presets[static_cast(Options::video.shader.current_postfx_preset)].name); } } const bool SHOW_SS = Options::video.supersampling.enabled && !IS_CRTPI; line += " | " + SHADER_NAME + " " + preset_name + (SHOW_SS ? " (ss)" : ""); } // Todo en lowercase std::ranges::transform(line, line.begin(), [](unsigned char c) { return std::tolower(c); }); // Constantes visuales (igual que Console) static constexpr Uint8 BG_COLOR = 0; // PaletteColor::BLACK static constexpr Uint8 MSG_COLOR = 9; // PaletteColor::BRIGHT_GREEN static constexpr int TEXT_SIZE = 6; static constexpr int PADDING_V = (TEXT_SIZE / 2) - 1; // Fuente: preferir la de la consola si está disponible auto text_obj = (Console::get() != nullptr) ? Console::get()->getText() : Screen::get()->getText(); // Posición Y: debajo de la consola + offset animado propio const int CONSOLE_Y = (Console::get() != nullptr) ? Console::get()->getVisibleHeight() : 0; const int Y = CONSOLE_Y + static_cast(y_); // Rectángulo de fondo: ancho completo, alto ajustado al texto const SDL_FRect RECT = { .x = 0.0F, .y = static_cast(Y), .w = Options::game.width, .h = static_cast(TEXT_SIZE + (PADDING_V * 2))}; auto game_surface = Screen::get()->getGameSurface(); game_surface->fillRect(&RECT, BG_COLOR); // game_surface->drawRectBorder(&RECT, BORDER_COLOR); text_obj->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, static_cast(Options::game.width / 2), Y + PADDING_V, line, 1, MSG_COLOR); } // Activa o desactiva el overlay y notifica a Notifier del cambio de offset void RenderInfo::toggle() { switch (status_) { case Status::HIDDEN: status_ = Status::RISING; Screen::get()->updateZoomFactor(); if (Notifier::get() != nullptr) { Notifier::get()->addYOffset(HEIGHT); } break; case Status::ACTIVE: status_ = Status::VANISHING; if (Notifier::get() != nullptr) { Notifier::get()->removeYOffset(HEIGHT); } break; default: break; } }