diff --git a/CMakeLists.txt b/CMakeLists.txt index bebb00f..12ab126 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ set(APP_SOURCES # Core - Rendering source/core/rendering/gif.cpp source/core/rendering/pixel_reveal.cpp + source/core/rendering/render_info.cpp source/core/rendering/screen.cpp source/core/rendering/surface.cpp source/core/rendering/sprite/animated_sprite.cpp diff --git a/source/core/input/global_inputs.cpp b/source/core/input/global_inputs.cpp index a86a92a..242fccd 100644 --- a/source/core/input/global_inputs.cpp +++ b/source/core/input/global_inputs.cpp @@ -7,7 +7,8 @@ #include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT #include "core/locale/locale.hpp" // Para Locale -#include "core/rendering/screen.hpp" // Para Screen +#include "core/rendering/render_info.hpp" // Para RenderInfo +#include "core/rendering/screen.hpp" // Para Screen #include "game/options.hpp" // Para Options, options, OptionsVideo, Section #include "game/scene_manager.hpp" // Para SceneManager #include "game/ui/console.hpp" // Para Console @@ -271,7 +272,7 @@ namespace GlobalInputs { #ifdef _DEBUG case InputAction::TOGGLE_DEBUG: - Screen::get()->toggleFPS(); + if (RenderInfo::get() != nullptr) { RenderInfo::get()->toggle(); } break; case InputAction::SHOW_DEBUG_INFO: diff --git a/source/core/rendering/render_info.cpp b/source/core/rendering/render_info.cpp new file mode 100644 index 0000000..baefa38 --- /dev/null +++ b/source/core/rendering/render_info.cpp @@ -0,0 +1,119 @@ +#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 + +// [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 +} + +// Renderiza el overlay de información por pantalla +void RenderInfo::render() const { + if (!active_) { 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::replace(zoom_str.begin(), zoom_str.end(), '.', ','); + } + line += " | " + zoom_str + "x"; + + // PostFX: muestra preset y supersampling, o nada si está desactivado + if (Options::video.postfx) { + std::string preset_name = "-"; + if (!Options::postfx_presets.empty()) { + preset_name = Options::postfx_presets[static_cast(Options::current_postfx_preset)].name; + } + line += " | " + preset_name + (Options::video.supersampling ? " (ss)" : ""); + } + + // Todo en lowercase + std::transform(line.begin(), line.end(), 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 BORDER_COLOR = 9; // PaletteColor::BRIGHT_GREEN + static constexpr Uint8 MSG_COLOR = 8; // PaletteColor::GREEN + static constexpr int TEXT_SIZE = 6; + static constexpr int PADDING_V = TEXT_SIZE / 2; + + // 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 si está visible) + const int Y = (Console::get() != nullptr) ? Console::get()->getVisibleHeight() : 0; + + // 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() { + active_ = !active_; + if (active_) { + Screen::get()->updateZoomFactor(); + if (Notifier::get() != nullptr) { Notifier::get()->addYOffset(HEIGHT); } + } else { + if (Notifier::get() != nullptr) { Notifier::get()->removeYOffset(HEIGHT); } + } +} diff --git a/source/core/rendering/render_info.hpp b/source/core/rendering/render_info.hpp new file mode 100644 index 0000000..a08ca58 --- /dev/null +++ b/source/core/rendering/render_info.hpp @@ -0,0 +1,29 @@ +#pragma once + +class RenderInfo { + public: + // Singleton + static void init(); + static void destroy(); + static auto get() -> RenderInfo*; + + // Métodos principales + void render() const; + void toggle(); + + // Consultas + [[nodiscard]] auto isActive() const -> bool { return active_; } + + // Altura fija del overlay (TEXT_SIZE(6) + PADDING_V(3) * 2) + static constexpr int HEIGHT = 12; + + private: + // Singleton + static RenderInfo* render_info; + + // Constructor y destructor privados [SINGLETON] + RenderInfo(); + ~RenderInfo() = default; + + bool active_{false}; // Estado del overlay +}; diff --git a/source/core/rendering/screen.cpp b/source/core/rendering/screen.cpp index 6aff8a3..b0128cc 100644 --- a/source/core/rendering/screen.cpp +++ b/source/core/rendering/screen.cpp @@ -9,8 +9,6 @@ #include // Para basic_ostream, operator<<, endl, basic_... #include // Para cerr #include // Para istreambuf_iterator, operator== -#include // Para setprecision -#include // Para ostringstream #include // Para char_traits, string, operator+, operator== #include "core/input/mouse.hpp" // Para updateCursorVisibility @@ -21,6 +19,7 @@ #include "core/resources/resource_helper.hpp" // Para ResourceHelper #include "core/resources/resource_list.hpp" // Para Asset, AssetType #include "game/options.hpp" // Para Options, options, OptionsVideo, Border +#include "core/rendering/render_info.hpp" // Para RenderInfo #include "game/ui/console.hpp" // Para Console #include "game/ui/notifier.hpp" // Para Notifier @@ -445,9 +444,7 @@ void Screen::textureToRenderer() { // Renderiza todos los overlays void Screen::renderOverlays() { renderNotifications(); -#ifdef _DEBUG - renderInfo(); -#endif + if (RenderInfo::get() != nullptr) { RenderInfo::get()->render(); } } // Localiza la paleta dentro del vector de paletas @@ -462,51 +459,6 @@ auto Screen::findPalette(const std::string& name) -> size_t { // NOLINT(readabi return static_cast(0); } -// Muestra información por pantalla -void Screen::renderInfo() const { - if (!show_fps_ || text_ == nullptr) { - return; - } - - const int LINE_HEIGHT = text_->getCharacterSize() - 3; - const int X = 0; - int y = (Console::get() != nullptr) ? Console::get()->getVisibleHeight() : 0; - - // FPS - const std::string FPS_TEXT = std::to_string(fps_.last_value) + " fps"; - text_->write(X, y, FPS_TEXT); - y += LINE_HEIGHT; - - // Driver GPU - text_->write(X, y, gpu_driver_.empty() ? "sdl" : gpu_driver_); - y += LINE_HEIGHT; - - // Zoom calculado (alto físico / alto lógico), con coma decimal y sin ceros innecesarios - const float ROUNDED = std::round(zoom_factor_ * 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::replace(zoom_str.begin(), zoom_str.end(), '.', ','); - } - text_->write(X, y, zoom_str + "x"); - y += LINE_HEIGHT; - - // PostFX: muestra preset y supersampling en una sola línea, o nada si está desactivado - if (Options::video.postfx) { - std::string preset_name = "-"; - if (!Options::postfx_presets.empty()) { - preset_name = Options::postfx_presets[static_cast(Options::current_postfx_preset)].name; - } - const std::string POSTFX_LINE = preset_name + (Options::video.supersampling ? " (SS)" : ""); - text_->write(X, y, POSTFX_LINE); - } -} - // Limpia la game_surface_ void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); } @@ -528,12 +480,6 @@ void Screen::hide() { SDL_HideWindow(window_); } // Establece la visibilidad de las notificaciones void Screen::setNotificationsEnabled(bool value) { notifications_enabled_ = value; } -// Activa / desactiva el contador de FPS -void Screen::toggleFPS() { - show_fps_ = !show_fps_; - if (show_fps_) { updateZoomFactor(); } -} - // Alterna entre activar y desactivar el escalado entero void Screen::toggleIntegerScale() { Options::video.integer_scale = !Options::video.integer_scale; @@ -556,6 +502,7 @@ void Screen::toggleVSync() { // Getters auto Screen::getRenderer() -> SDL_Renderer* { return renderer_; } auto Screen::getRendererSurface() -> std::shared_ptr { return (*renderer_surface_); } +auto Screen::getGameSurface() -> std::shared_ptr { return game_surface_; } auto Screen::getBorderSurface() -> std::shared_ptr { border_is_solid_ = false; // Modificación externa → modo borde dinámico return border_surface_; diff --git a/source/core/rendering/screen.hpp b/source/core/rendering/screen.hpp index b281e6f..544851a 100644 --- a/source/core/rendering/screen.hpp +++ b/source/core/rendering/screen.hpp @@ -65,16 +65,18 @@ class Screen { // Surfaces y notificaciones void setRendererSurface(const std::shared_ptr& surface = nullptr); // Establece el renderizador para las surfaces void setNotificationsEnabled(bool value); // Establece la visibilidad de las notificaciones - void toggleFPS(); // Activa o desactiva el contador de FPS - [[nodiscard]] auto isFPSVisible() const -> bool { return show_fps_; } // Estado actual del overlay de debug + void updateZoomFactor(); // Recalcula y almacena el factor de zoom real // Getters auto getRenderer() -> SDL_Renderer*; auto getRendererSurface() -> std::shared_ptr; auto getBorderSurface() -> std::shared_ptr; + auto getGameSurface() -> std::shared_ptr; [[nodiscard]] auto getText() const -> std::shared_ptr { return text_; } [[nodiscard]] auto getGameSurfaceDstRect() const -> SDL_FRect { return game_surface_dstrect_; } [[nodiscard]] auto getGPUDriver() const -> const std::string& { return gpu_driver_; } + [[nodiscard]] auto getLastFPS() const -> int { return fps_.last_value; } + [[nodiscard]] auto getZoomFactor() const -> float { return zoom_factor_; } private: // Estructuras @@ -114,7 +116,6 @@ class Screen { void renderNotifications() const; // Dibuja las notificaciones void adjustWindowSize(); // Calcula el tamaño de la ventana void adjustRenderLogicalSize(); // Ajusta el tamaño lógico del renderizador - void updateZoomFactor(); // Recalcula y almacena el factor de zoom real void processPaletteList(); // Extrae los nombres de las paletas void surfaceToTexture(); // Copia la surface a la textura void textureToRenderer(); // Copia la textura al renderizador @@ -122,7 +123,6 @@ class Screen { auto findPalette(const std::string& name) -> size_t; // Localiza la paleta dentro del vector de paletas void initShaders(); // Inicializa los shaders void applyCurrentPostFXPreset(); // Aplica los parámetros del preset actual al backend - void renderInfo() const; // Muestra información por pantalla void getDisplayInfo(); // Obtiene información sobre la pantalla auto initSDLVideo() -> bool; // Arranca SDL VIDEO y crea la ventana void createText(); // Crea el objeto de texto @@ -171,10 +171,4 @@ class Screen { // Shaders std::string info_resolution_; // Texto con la información de la pantalla std::string gpu_driver_; // Nombre del driver GPU (SDL3GPU), capturado en initShaders() - -#ifdef _DEBUG - bool show_fps_{true}; // Indica si ha de mostrar el contador de FPS -#else - bool show_fps_{false}; // Indica si ha de mostrar el contador de FPS -#endif }; \ No newline at end of file diff --git a/source/core/resources/resource_cache.cpp b/source/core/resources/resource_cache.cpp index 32f2dbc..68779b3 100644 --- a/source/core/resources/resource_cache.cpp +++ b/source/core/resources/resource_cache.cpp @@ -55,11 +55,8 @@ namespace Resource { // Carga todos los recursos void Cache::load() { - // Oculta el overlay de debug durante la carga inicial; al restaurarlo - // SDL ya estará estabilizado y toggleFPS recalculará zoom_factor_ correctamente - const bool WAS_FPS_VISIBLE = Screen::get()->isFPSVisible(); - if (WAS_FPS_VISIBLE) { Screen::get()->toggleFPS(); } - + // Nota: el overlay de debug (RenderInfo) se inicializa después de esta carga, + // por lo que updateZoomFactor() se llamará correctamente en RenderInfo::init(). calculateTotal(); Screen::get()->setBorderColor(static_cast(PaletteColor::BLACK)); std::cout << "\n** LOADING RESOURCES" << '\n'; @@ -72,8 +69,6 @@ namespace Resource { loadRooms(); createText(); std::cout << "\n** RESOURCES LOADED" << '\n'; - - if (WAS_FPS_VISIBLE) { Screen::get()->toggleFPS(); } } // Recarga todos los recursos diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index cc7cb2f..e206e70 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -14,6 +14,7 @@ #include "core/audio/audio.hpp" // Para Audio #include "core/input/input.hpp" // Para Input, InputAction #include "core/locale/locale.hpp" // Para Locale +#include "core/rendering/render_info.hpp" // Para RenderInfo #include "core/rendering/screen.hpp" // Para Screen #include "core/resources/resource_cache.hpp" // Para Resource #include "core/resources/resource_helper.hpp" // Para ResourceHelper @@ -142,6 +143,9 @@ Director::Director(std::vector const& args) { // Initialize resources (works for both release and development) Resource::Cache::init(); Notifier::init("", "8bithud"); +#ifdef _DEBUG + RenderInfo::init(); // En DEBUG, se activa y notifica a Notifier del offset inicial +#endif Console::init("8bithud"); Screen::get()->setNotificationsEnabled(true); @@ -194,6 +198,9 @@ Director::~Director() { #endif Input::destroy(); Console::destroy(); +#ifdef _DEBUG + RenderInfo::destroy(); +#endif Notifier::destroy(); Resource::Cache::destroy(); Resource::Helper::shutdownResourceSystem(); // Shutdown resource pack system diff --git a/source/game/ui/console.cpp b/source/game/ui/console.cpp index 55a9a14..c1e3975 100644 --- a/source/game/ui/console.cpp +++ b/source/game/ui/console.cpp @@ -14,7 +14,9 @@ #include "core/rendering/text.hpp" // Para Text #include "core/resources/resource_cache.hpp" // Para Resource #include "game/options.hpp" // Para Options +#include "core/rendering/render_info.hpp" // Para RenderInfo #include "game/scene_manager.hpp" // Para SceneManager +#include "game/ui/notifier.hpp" // Para Notifier // ── Sistema de comandos ──────────────────────────────────────────────────────── @@ -293,19 +295,19 @@ static const std::vector COMMANDS = { #ifdef _DEBUG // DEBUG [ON|OFF] — Overlay de debug (F12, solo en builds debug) {.keyword = "DEBUG", .execute = BOOL_TOGGLE_CMD("Debug overlay", - Screen::get()->isFPSVisible(), - Screen::get()->toggleFPS())}, + RenderInfo::get()->isActive(), + RenderInfo::get()->toggle())}, // SHOW FPS / HIDE FPS — Aliases de DEBUG ON / DEBUG OFF {.keyword = "SHOW", .execute = [](const std::vector& args) -> std::string { if (args.empty() || args[0] != "FPS") { return "Usage: SHOW FPS"; } - if (Screen::get()->isFPSVisible()) { return "Debug overlay already ON"; } - Screen::get()->toggleFPS(); return "Debug overlay ON"; + if (RenderInfo::get()->isActive()) { return "Debug overlay already ON"; } + RenderInfo::get()->toggle(); return "Debug overlay ON"; }}, {.keyword = "HIDE", .execute = [](const std::vector& args) -> std::string { if (args.empty() || args[0] != "FPS") { return "Usage: HIDE FPS"; } - if (!Screen::get()->isFPSVisible()) { return "Debug overlay already OFF"; } - Screen::get()->toggleFPS(); return "Debug overlay OFF"; + if (!RenderInfo::get()->isActive()) { return "Debug overlay already OFF"; } + RenderInfo::get()->toggle(); return "Debug overlay OFF"; }}, #endif @@ -629,6 +631,7 @@ void Console::toggle() { cursor_timer_ = 0.0F; cursor_visible_ = true; SDL_StartTextInput(SDL_GetKeyboardFocus()); + if (Notifier::get() != nullptr) { Notifier::get()->addYOffset(static_cast(height_)); } break; case Status::ACTIVE: status_ = Status::VANISHING; @@ -636,6 +639,7 @@ void Console::toggle() { history_index_ = -1; saved_input_.clear(); SDL_StopTextInput(SDL_GetKeyboardFocus()); + if (Notifier::get() != nullptr) { Notifier::get()->removeYOffset(static_cast(height_)); } break; default: // Durante RISING o VANISHING no se hace nada diff --git a/source/game/ui/console.hpp b/source/game/ui/console.hpp index 60cd942..354daed 100644 --- a/source/game/ui/console.hpp +++ b/source/game/ui/console.hpp @@ -26,6 +26,7 @@ class Console { // Consultas auto isActive() -> bool; // true si RISING, ACTIVE o VANISHING auto getVisibleHeight() -> int; // Píxeles visibles actuales (0 = oculta, height_ = totalmente visible) + [[nodiscard]] auto getText() const -> std::shared_ptr { return text_; } private: enum class Status { diff --git a/source/game/ui/notifier.cpp b/source/game/ui/notifier.cpp index b6e357a..85a0aab 100644 --- a/source/game/ui/notifier.cpp +++ b/source/game/ui/notifier.cpp @@ -173,7 +173,7 @@ void Notifier::show(std::vector texts, const Style& style, int icon ; // Posición vertical - const int DESP_V = PADDING_OUT; + const int DESP_V = y_offset_; // Offset const auto TRAVEL_DIST = HEIGHT + PADDING_OUT; @@ -278,6 +278,10 @@ void Notifier::clearNotifications() { clearFinishedNotifications(); } +// Ajusta el offset vertical base +void Notifier::addYOffset(int px) { y_offset_ += px; } +void Notifier::removeYOffset(int px) { y_offset_ -= px; } + // Obtiene los códigos de las notificaciones auto Notifier::getCodes() -> std::vector { std::vector codes; diff --git a/source/game/ui/notifier.hpp b/source/game/ui/notifier.hpp index 99dbca8..967007a 100644 --- a/source/game/ui/notifier.hpp +++ b/source/game/ui/notifier.hpp @@ -59,6 +59,10 @@ class Notifier { auto isActive() -> bool; // Indica si hay notificaciones activas auto getCodes() -> std::vector; // Obtiene códigos de notificaciones + // Offset vertical (para evitar solapamiento con Console y renderInfo) + void addYOffset(int px); // Suma píxeles al offset base + void removeYOffset(int px); // Resta píxeles al offset base + private: // Tipos anidados enum class Status { @@ -107,4 +111,5 @@ class Notifier { std::vector notifications_; // Lista de notificaciones activas bool stack_{false}; // Indica si las notificaciones se apilan bool has_icons_{false}; // Indica si el notificador tiene textura para iconos + int y_offset_{0}; // Offset vertical base (ajustado por Console y renderInfo) };