diff --git a/source/core/input/global_inputs.cpp b/source/core/input/global_inputs.cpp index 1a33116..f2b0343 100644 --- a/source/core/input/global_inputs.cpp +++ b/source/core/input/global_inputs.cpp @@ -35,7 +35,7 @@ void handleQuit() { } } else { // Si la notificación de salir no está activa, muestra la notificación - Notifier::get()->show({CODE}, Notifier::TextAlign::CENTER, 2000, -1, true, CODE); + Notifier::get()->show({CODE}, Notifier::Style::DEFAULT, -1, true, CODE); } } @@ -59,50 +59,50 @@ void handleSkipSection() { void handleToggleBorder() { Screen::get()->toggleBorder(); - Notifier::get()->show({"BORDER " + std::string(Options::video.border.enabled ? "ENABLED" : "DISABLED")}, Notifier::TextAlign::CENTER); + Notifier::get()->show({"BORDER " + std::string(Options::video.border.enabled ? "ENABLED" : "DISABLED")}); } void handleToggleVideoMode() { Screen::get()->toggleVideoMode(); - Notifier::get()->show({"FULLSCREEN " + std::string(static_cast(Options::video.fullscreen) == 0 ? "DISABLED" : "ENABLED")}, Notifier::TextAlign::CENTER); + Notifier::get()->show({"FULLSCREEN " + std::string(static_cast(Options::video.fullscreen) == 0 ? "DISABLED" : "ENABLED")}); } void handleDecWindowZoom() { if (Screen::get()->decWindowZoom()) { - Notifier::get()->show({"WINDOW ZOOM x" + std::to_string(Options::window.zoom)}, Notifier::TextAlign::CENTER); + Notifier::get()->show({"WINDOW ZOOM x" + std::to_string(Options::window.zoom)}); } } void handleIncWindowZoom() { if (Screen::get()->incWindowZoom()) { - Notifier::get()->show({"WINDOW ZOOM x" + std::to_string(Options::window.zoom)}, Notifier::TextAlign::CENTER); + Notifier::get()->show({"WINDOW ZOOM x" + std::to_string(Options::window.zoom)}); } } void handleToggleShaders() { Screen::get()->toggleShaders(); - Notifier::get()->show({"SHADERS " + std::string(Options::video.shaders ? "ENABLED" : "DISABLED")}, Notifier::TextAlign::CENTER); + Notifier::get()->show({"SHADERS " + std::string(Options::video.shaders ? "ENABLED" : "DISABLED")}); } void handleNextPalette() { Screen::get()->nextPalette(); - Notifier::get()->show({"PALETTE " + Options::video.palette}, Notifier::TextAlign::CENTER); + Notifier::get()->show({"PALETTE " + Options::video.palette}); } void handlePreviousPalette() { Screen::get()->previousPalette(); - Notifier::get()->show({"PALETTE " + Options::video.palette}, Notifier::TextAlign::CENTER); + Notifier::get()->show({"PALETTE " + Options::video.palette}); } void handleToggleIntegerScale() { Screen::get()->toggleIntegerScale(); Screen::get()->setVideoMode(Options::video.fullscreen); - Notifier::get()->show({"INTEGER SCALE " + std::string(Options::video.integer_scale ? "ENABLED" : "DISABLED")}, Notifier::TextAlign::CENTER); + Notifier::get()->show({"INTEGER SCALE " + std::string(Options::video.integer_scale ? "ENABLED" : "DISABLED")}); } void handleToggleVSync() { Screen::get()->toggleVSync(); - Notifier::get()->show({"V-SYNC " + std::string(Options::video.vertical_sync ? "ENABLED" : "DISABLED")}, Notifier::TextAlign::CENTER); + Notifier::get()->show({"V-SYNC " + std::string(Options::video.vertical_sync ? "ENABLED" : "DISABLED")}); } #ifdef _DEBUG diff --git a/source/game/gameplay/cheevos.cpp b/source/game/gameplay/cheevos.cpp index 803f95a..a62241b 100644 --- a/source/game/gameplay/cheevos.cpp +++ b/source/game/gameplay/cheevos.cpp @@ -82,7 +82,7 @@ void Cheevos::unlock(int id) { cheevos_list_.at(INDEX).completed = true; // Mostrar notificación en la pantalla - Notifier::get()->show({"ACHIEVEMENT UNLOCKED!", cheevos_list_.at(INDEX).caption}, Notifier::TextAlign::CENTER, Notifier::DURATION_CHEEVO /*, cheevos_list_.at(INDEX).icon*/); + Notifier::get()->show({"ACHIEVEMENT UNLOCKED!", cheevos_list_.at(INDEX).caption}, Notifier::Style::CHEEVO /*, cheevos_list_.at(INDEX).icon*/); // Guardar el estado de los logros saveToFile(); diff --git a/source/game/scenes/game.cpp b/source/game/scenes/game.cpp index 85a6394..1f85b22 100644 --- a/source/game/scenes/game.cpp +++ b/source/game/scenes/game.cpp @@ -85,12 +85,12 @@ void Game::handleInput() { if (Input::get()->checkAction(InputAction::TOGGLE_MUSIC, Input::DO_NOT_ALLOW_REPEAT)) { board_->music = !board_->music; board_->music ? Audio::get()->resumeMusic() : Audio::get()->pauseMusic(); - Notifier::get()->show({"MUSIC " + std::string(board_->music ? "ENABLED" : "DISABLED")}, Notifier::TextAlign::CENTER); + Notifier::get()->show({"MUSIC " + std::string(board_->music ? "ENABLED" : "DISABLED")}); } else if (Input::get()->checkAction(InputAction::PAUSE, Input::DO_NOT_ALLOW_REPEAT)) { togglePause(); - Notifier::get()->show({std::string(paused_ ? "GAME PAUSED" : "GAME RUNNING")}, Notifier::TextAlign::CENTER); + Notifier::get()->show({std::string(paused_ ? "GAME PAUSED" : "GAME RUNNING")}); } GlobalInputs::handle(); @@ -223,7 +223,7 @@ void Game::handleDebugEvents(const SDL_Event& event) { switch (event.key.key) { case SDLK_F12: Debug::get()->toggleEnabled(); - Notifier::get()->show({"DEBUG " + std::string(Debug::get()->isEnabled() ? "ENABLED" : "DISABLED")}, Notifier::TextAlign::CENTER); + Notifier::get()->show({"DEBUG " + std::string(Debug::get()->isEnabled() ? "ENABLED" : "DISABLED")}); room_->redrawMap(); // Redibuja el tilemap para mostrar/ocultar líneas de colisión Options::cheats.invincible = static_cast(Debug::get()->isEnabled()); player_->setColor(); @@ -252,7 +252,7 @@ void Game::handleDebugEvents(const SDL_Event& event) { break; case SDLK_7: - Notifier::get()->show({"ACHIEVEMENT UNLOCKED!", "I LIKE MY MULTICOLOURED FRIENDS"}, Notifier::TextAlign::CENTER, Notifier::DURATION_CHEEVO, -1, false, "F7"); + Notifier::get()->show({"ACHIEVEMENT UNLOCKED!", "I LIKE MY MULTICOLOURED FRIENDS"}, Notifier::Style::CHEEVO, -1, false, "F7"); break; default: diff --git a/source/game/ui/notifier.cpp b/source/game/ui/notifier.cpp index a7396f0..34f3ea9 100644 --- a/source/game/ui/notifier.cpp +++ b/source/game/ui/notifier.cpp @@ -21,6 +21,27 @@ // [SINGLETON] Notifier* Notifier::notifier = nullptr; +// Definición de estilos predefinidos +const Notifier::Style Notifier::Style::DEFAULT = { + .bg_color = static_cast(PaletteColor::BLUE), + .border_color = static_cast(PaletteColor::CYAN), + .text_color = static_cast(PaletteColor::CYAN), + .shape = Notifier::Shape::SQUARED, + .text_align = Notifier::TextAlign::CENTER, + .duration = 2.0F, + .sound_file = "notify.wav", + .play_sound = false}; + +const Notifier::Style Notifier::Style::CHEEVO = { + .bg_color = static_cast(PaletteColor::MAGENTA), + .border_color = static_cast(PaletteColor::BRIGHT_MAGENTA), + .text_color = static_cast(PaletteColor::WHITE), + .shape = Notifier::Shape::SQUARED, + .text_align = Notifier::TextAlign::CENTER, + .duration = 4.0F, + .sound_file = "notify.wav", + .play_sound = true}; + // [SINGLETON] Crearemos el objeto con esta función estática void Notifier::init(const std::string& icon_file, const std::string& text) { Notifier::notifier = new Notifier(icon_file, text); @@ -41,7 +62,6 @@ Notifier::Notifier(const std::string& icon_file, const std::string& text) : icon_surface_(!icon_file.empty() ? Resource::Cache::get()->getSurface(icon_file) : nullptr), text_(Resource::Cache::get()->getText(text)), delta_timer_(std::make_unique()), - bg_color_(Options::notifications.color), has_icons_(!icon_file.empty()) {} // Dibuja las notificaciones por pantalla @@ -70,12 +90,12 @@ void Notifier::update(float delta_time) { if (notification.rect.y >= notification.y) { notification.rect.y = notification.y; notification.state = Status::STAY; - notification.start_time = SDL_GetTicks(); + notification.elapsed_time = 0.0f; } break; } case Status::STAY: { - notification.elapsed_time = SDL_GetTicks() - notification.start_time; + notification.elapsed_time += delta_time; if (notification.elapsed_time >= notification.display_duration) { notification.state = Status::VANISHING; } @@ -115,7 +135,7 @@ void Notifier::clearFinishedNotifications() { notifications_.erase(result.begin(), result.end()); } -void Notifier::show(std::vector texts, TextAlign text_is, Uint32 display_duration, int icon, bool can_be_removed, const std::string& code) { +void Notifier::show(std::vector texts, const Style& style, int icon, bool can_be_removed, const std::string& code) { // Si no hay texto, acaba if (texts.empty()) { return; @@ -143,10 +163,10 @@ void Notifier::show(std::vector texts, TextAlign text_is, Uint32 di const auto PADDING_IN_H = TEXT_SIZE; const auto PADDING_IN_V = TEXT_SIZE / 2; const int ICON_SPACE = icon >= 0 ? ICON_SIZE + PADDING_IN_H : 0; - text_is = ICON_SPACE > 0 ? TextAlign::LEFT : text_is; + const TextAlign text_is = ICON_SPACE > 0 ? TextAlign::LEFT : style.text_align; const float WIDTH = Options::game.width - (PADDING_OUT * 2); const float HEIGHT = (TEXT_SIZE * texts.size()) + (PADDING_IN_V * 2); - const auto SHAPE = Shape::SQUARED; + const auto SHAPE = style.shape; // Posición horizontal float desp_h = ((Options::game.width / 2) - (WIDTH / 2)); @@ -170,7 +190,7 @@ void Notifier::show(std::vector texts, TextAlign text_is, Uint32 di n.travel_dist = TRAVEL_DIST; n.texts = texts; n.shape = SHAPE; - n.display_duration = display_duration; + n.display_duration = style.duration; const float Y_POS = OFFSET + -TRAVEL_DIST; n.rect = {.x = desp_h, .y = Y_POS, .w = WIDTH, .h = HEIGHT}; @@ -185,22 +205,22 @@ void Notifier::show(std::vector texts, TextAlign text_is, Uint32 di SDL_FRect rect; if (SHAPE == Shape::ROUNDED) { rect = {.x = 4, .y = 0, .w = WIDTH - (4 * 2), .h = HEIGHT}; - n.surface->fillRect(&rect, bg_color_); + n.surface->fillRect(&rect, style.bg_color); rect = {.x = 4 / 2, .y = 1, .w = WIDTH - 4, .h = HEIGHT - 2}; - n.surface->fillRect(&rect, bg_color_); + n.surface->fillRect(&rect, style.bg_color); rect = {.x = 1, .y = 4 / 2, .w = WIDTH - 2, .h = HEIGHT - 4}; - n.surface->fillRect(&rect, bg_color_); + n.surface->fillRect(&rect, style.bg_color); rect = {.x = 0, .y = 4, .w = WIDTH, .h = HEIGHT - (4 * 2)}; - n.surface->fillRect(&rect, bg_color_); + n.surface->fillRect(&rect, style.bg_color); } else if (SHAPE == Shape::SQUARED) { - n.surface->clear(bg_color_); + n.surface->clear(style.bg_color); SDL_FRect squared_rect = {0, 0, n.surface->getWidth(), n.surface->getHeight()}; - n.surface->drawRectBorder(&squared_rect, static_cast(PaletteColor::CYAN)); + n.surface->drawRectBorder(&squared_rect, style.border_color); } // Dibuja el icono de la notificación @@ -212,7 +232,7 @@ void Notifier::show(std::vector texts, TextAlign text_is, Uint32 di } // Escribe el texto de la notificación - const auto COLOR = static_cast(PaletteColor::WHITE); + const auto COLOR = style.text_color; int iterator = 0; for (const auto& text : texts) { switch (text_is) { @@ -239,7 +259,9 @@ void Notifier::show(std::vector texts, TextAlign text_is, Uint32 di notifications_.emplace_back(n); // Reproduce el sonido de la notificación - Audio::get()->playSound("notify.wav", Audio::Group::INTERFACE); + if (style.play_sound && !style.sound_file.empty()) { + Audio::get()->playSound(style.sound_file, Audio::Group::INTERFACE); + } } // Indica si hay notificaciones activas diff --git a/source/game/ui/notifier.hpp b/source/game/ui/notifier.hpp index c4ec815..0e0f4a3 100644 --- a/source/game/ui/notifier.hpp +++ b/source/game/ui/notifier.hpp @@ -12,16 +12,34 @@ class DeltaTimer; // lines 11-11 class Notifier { public: - // Constantes - static constexpr Uint32 DURATION_DEFAULT = 2000; - static constexpr Uint32 DURATION_CHEEVO = 4000; - // Justificado para las notificaciones enum class TextAlign { LEFT, CENTER, }; + // Forma de las notificaciones + enum class Shape { + ROUNDED, + SQUARED, + }; + + // Estilo de notificación + struct Style { + Uint8 bg_color; // Color de fondo + Uint8 border_color; // Color del borde + Uint8 text_color; // Color del texto + Shape shape; // Forma (ROUNDED/SQUARED) + TextAlign text_align; // Alineación del texto + float duration; // Duración en segundos + std::string sound_file; // Archivo de sonido (vacío = sin sonido) + bool play_sound; // Si reproduce sonido + + // Estilos predefinidos + static const Style DEFAULT; + static const Style CHEEVO; + }; + // Gestión singleton static void init(const std::string& icon_file, const std::string& text); // Inicialización static void destroy(); // Destrucción @@ -32,8 +50,7 @@ class Notifier { void update(float delta_time); // Actualización lógica void show( std::vector texts, - TextAlign text_is = TextAlign::LEFT, - Uint32 display_duration = DURATION_DEFAULT, + const Style& style = Style::DEFAULT, int icon = -1, bool can_be_removed = true, const std::string& code = std::string()); // Mostrar notificación @@ -51,11 +68,6 @@ class Notifier { FINISHED, }; - enum class Shape { - ROUNDED, - SQUARED, - }; - struct Notification { std::shared_ptr surface{nullptr}; std::shared_ptr sprite{nullptr}; @@ -68,9 +80,8 @@ class Notifier { std::string code{}; bool can_be_removed{true}; int height{0}; - Uint32 start_time{0}; - Uint32 elapsed_time{0}; - Uint32 display_duration{0}; + float elapsed_time{0.0f}; + float display_duration{0.0f}; }; // Constantes @@ -93,7 +104,6 @@ class Notifier { std::shared_ptr icon_surface_; // Textura para los iconos std::shared_ptr text_; // Objeto para dibujar texto std::unique_ptr delta_timer_; // Timer for frame-independent animations - Uint8 bg_color_{0}; // Color de fondo de las notificaciones 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