diff --git a/CMakeLists.txt b/CMakeLists.txt index 29ec911..bebb00f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,7 @@ set(APP_SOURCES source/game/scenes/title.cpp # Game - UI + source/game/ui/console.cpp source/game/ui/notifier.cpp # Utils diff --git a/source/core/input/global_inputs.cpp b/source/core/input/global_inputs.cpp index 77ba73b..a86a92a 100644 --- a/source/core/input/global_inputs.cpp +++ b/source/core/input/global_inputs.cpp @@ -10,6 +10,7 @@ #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 #include "game/ui/notifier.hpp" // Para Notifier, NotificationText #include "utils/utils.hpp" // Para stringInVector @@ -176,6 +177,9 @@ namespace GlobalInputs { if (Input::get()->checkAction(InputAction::SHOW_DEBUG_INFO, Input::DO_NOT_ALLOW_REPEAT)) { return InputAction::SHOW_DEBUG_INFO; } + if (Input::get()->checkAction(InputAction::TOGGLE_CONSOLE, Input::DO_NOT_ALLOW_REPEAT)) { + return InputAction::TOGGLE_CONSOLE; + } return InputAction::NONE; } @@ -185,6 +189,15 @@ namespace GlobalInputs { // Comprueba los inputs que se pueden introducir en cualquier sección del juego void handle() { + // Si la consola está activa, bloquea el resto de inputs globales + if (Console::get() != nullptr && Console::get()->isActive()) { + if (Input::get()->checkAction(InputAction::TOGGLE_CONSOLE, Input::DO_NOT_ALLOW_REPEAT) || + Input::get()->checkAction(InputAction::EXIT, Input::DO_NOT_ALLOW_REPEAT)) { + Console::get()->toggle(); + } + return; + } + // Salida de administrador en modo kiosko (Ctrl+Shift+Alt+Q) if (Options::kiosk.enabled) { SDL_Keymod mod = SDL_GetModState(); @@ -252,6 +265,10 @@ namespace GlobalInputs { handleToggleVSync(); break; + case InputAction::TOGGLE_CONSOLE: + if (Console::get() != nullptr) { Console::get()->toggle(); } + break; + #ifdef _DEBUG case InputAction::TOGGLE_DEBUG: Screen::get()->toggleFPS(); diff --git a/source/core/input/input.cpp b/source/core/input/input.cpp index 7247942..332bf1c 100644 --- a/source/core/input/input.cpp +++ b/source/core/input/input.cpp @@ -51,7 +51,8 @@ Input::Input(std::string game_controller_db_path) {Action::TOGGLE_BORDER, KeyState{.scancode = SDL_SCANCODE_F9}}, {Action::TOGGLE_VSYNC, KeyState{.scancode = SDL_SCANCODE_F10}}, {Action::PAUSE, KeyState{.scancode = SDL_SCANCODE_F11}}, - {Action::TOGGLE_DEBUG, KeyState{.scancode = SDL_SCANCODE_F12}}}; + {Action::TOGGLE_DEBUG, KeyState{.scancode = SDL_SCANCODE_F12}}, + {Action::TOGGLE_CONSOLE, KeyState{.scancode = SDL_SCANCODE_TAB}}}; initSDLGamePad(); // Inicializa el subsistema SDL_INIT_GAMEPAD } diff --git a/source/core/input/input_types.hpp b/source/core/input/input_types.hpp index 8847cb8..2c9139e 100644 --- a/source/core/input/input_types.hpp +++ b/source/core/input/input_types.hpp @@ -33,6 +33,7 @@ enum class InputAction : int { // Acciones de entrada posibles en el juego PREVIOUS_PALETTE, SHOW_DEBUG_INFO, TOGGLE_DEBUG, + TOGGLE_CONSOLE, // Input obligatorio NONE, diff --git a/source/core/rendering/screen.cpp b/source/core/rendering/screen.cpp index 10b64d4..92371ea 100644 --- a/source/core/rendering/screen.cpp +++ b/source/core/rendering/screen.cpp @@ -18,6 +18,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 "game/ui/console.hpp" // Para Console #include "game/ui/notifier.hpp" // Para Notifier // [SINGLETON] @@ -215,6 +216,9 @@ void Screen::renderNotifications() const { if (notifications_enabled_) { Notifier::get()->render(); } + if (Console::get() != nullptr) { + Console::get()->render(); + } } // Cambia el estado del PostFX @@ -247,6 +251,9 @@ void Screen::reloadPostFX() { void Screen::update(float delta_time) { fps_.calculate(SDL_GetTicks()); Notifier::get()->update(delta_time); + if (Console::get() != nullptr) { + Console::get()->update(delta_time); + } Mouse::updateCursorVisibility(); } diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 2b3a42b..490335e 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -30,6 +30,7 @@ #include "game/scenes/loading_screen.hpp" // Para LoadingScreen #include "game/scenes/logo.hpp" // Para Logo #include "game/scenes/title.hpp" // Para Title +#include "game/ui/console.hpp" // Para Console #include "game/ui/notifier.hpp" // Para Notifier #include "utils/defines.hpp" // Para WINDOW_CAPTION @@ -141,6 +142,7 @@ Director::Director(std::vector const& args) { // Initialize resources (works for both release and development) Resource::Cache::init(); Notifier::init("", "8bithud"); + Console::init("8bithud"); Screen::get()->setNotificationsEnabled(true); // Special handling for gamecontrollerdb.txt - SDL needs filesystem path @@ -191,6 +193,7 @@ Director::~Director() { Debug::destroy(); #endif Input::destroy(); + Console::destroy(); 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 new file mode 100644 index 0000000..72f9107 --- /dev/null +++ b/source/game/ui/console.cpp @@ -0,0 +1,128 @@ +#include "game/ui/console.hpp" + +#include + +#include // Para string + +#include "core/rendering/screen.hpp" // Para Screen +#include "core/rendering/sprite/sprite.hpp" // Para Sprite +#include "core/rendering/surface.hpp" // Para Surface +#include "core/rendering/text.hpp" // Para Text +#include "core/resources/resource_cache.hpp" // Para Resource +#include "game/options.hpp" // Para Options + +// [SINGLETON] +Console* Console::console = nullptr; + +// [SINGLETON] +void Console::init(const std::string& font_name) { + Console::console = new Console(font_name); +} + +// [SINGLETON] +void Console::destroy() { + delete Console::console; + Console::console = nullptr; +} + +// [SINGLETON] +auto Console::get() -> Console* { + return Console::console; +} + +// Constructor +Console::Console(const std::string& font_name) + : text_(Resource::Cache::get()->getText(font_name)) { + const int TEXT_SIZE = 6; + const int PADDING_IN_V = TEXT_SIZE / 2; + height_ = static_cast((TEXT_SIZE * 2) + (PADDING_IN_V * 2)); + y_ = -height_; + + buildSurface(); +} + +// Crea la Surface con el aspecto visual de la consola +void Console::buildSurface() { + const float WIDTH = Options::game.width; + + surface_ = std::make_shared(WIDTH, height_); + + auto previous_renderer = Screen::get()->getRendererSurface(); + Screen::get()->setRendererSurface(surface_); + + // Fondo y borde + surface_->clear(BG_COLOR); + SDL_FRect rect = {.x = 0, .y = 0, .w = WIDTH, .h = height_}; + surface_->drawRectBorder(&rect, BORDER_COLOR); + + // Texto de marcador de posición + const int TEXT_SIZE = 6; + const int PADDING_IN_H = TEXT_SIZE; + const int PADDING_IN_V = TEXT_SIZE / 2; + text_->writeColored(PADDING_IN_H, PADDING_IN_V, "> _", BORDER_COLOR); + + Screen::get()->setRendererSurface(previous_renderer); + + // Posición inicial (fuera de pantalla por arriba) + SDL_FRect sprite_rect = {.x = 0, .y = y_, .w = WIDTH, .h = height_}; + sprite_ = std::make_shared(surface_, sprite_rect); +} + +// Actualiza la animación de la consola +void Console::update(float delta_time) { + if (status_ == Status::HIDDEN) { + return; + } + + 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_ <= -height_) { + y_ = -height_; + status_ = Status::HIDDEN; + } + break; + } + default: + break; + } + + SDL_FRect rect = {.x = 0, .y = y_, .w = Options::game.width, .h = height_}; + sprite_->setPosition(rect); +} + +// Renderiza la consola +void Console::render() { + if (status_ == Status::HIDDEN) { + return; + } + sprite_->render(); +} + +// Activa o desactiva la consola +void Console::toggle() { + switch (status_) { + case Status::HIDDEN: + status_ = Status::RISING; + break; + case Status::ACTIVE: + status_ = Status::VANISHING; + break; + default: + // Durante RISING o VANISHING no se hace nada + break; + } +} + +// Indica si la consola está activa (visible o en animación) +auto Console::isActive() -> bool { + return status_ != Status::HIDDEN; +} diff --git a/source/game/ui/console.hpp b/source/game/ui/console.hpp new file mode 100644 index 0000000..392af06 --- /dev/null +++ b/source/game/ui/console.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include + +#include // Para shared_ptr +#include // Para string + +class Surface; +class Sprite; +class Text; + +class Console { + public: + // Singleton + static void init(const std::string& font_name); + static void destroy(); + static auto get() -> Console*; + + // Métodos principales + void update(float delta_time); + void render(); + void toggle(); + + // Consultas + auto isActive() -> bool; // true si RISING, ACTIVE o VANISHING + + private: + enum class Status { + HIDDEN, + RISING, + ACTIVE, + VANISHING, + }; + + // Constantes visuales + static constexpr Uint8 BG_COLOR = 0; // PaletteColor::BLACK + static constexpr Uint8 BORDER_COLOR = 9; // PaletteColor::BRIGHT_GREEN + static constexpr float SLIDE_SPEED = 120.0F; + + // [SINGLETON] + static Console* console; + + // Constructor y destructor privados [SINGLETON] + explicit Console(const std::string& font_name); + ~Console() = default; + + // Métodos privados + void buildSurface(); // Crea la Surface con el aspecto visual + + // Objetos de renderizado + std::shared_ptr text_; + std::shared_ptr surface_; + std::shared_ptr sprite_; + + // Estado de la animación + Status status_{Status::HIDDEN}; + float y_{0.0F}; // Posición Y actual (animada) + float height_{0.0F}; // Altura del panel +};