#include "notifier.h" #include // Para SDL_BLENDMODE_BLEND #include // Para SDL_PIXELFORMAT_RGBA8888 #include // Para string #include #include #include "jail_audio.h" // Para JA_DeleteSound, JA_LoadSound, JA_Pla... #include "param.h" // Para Param, param, ParamNotification, Par... #include "screen.h" // Para Screen #include "sprite.h" // Para Sprite #include "text.h" // Para Text #include "texture.h" // Para Texture #include "resource.h" // [SINGLETON] Hay que definir las variables estáticas, desde el .h sólo la hemos declarado Notifier *Notifier::notifier_ = nullptr; // [SINGLETON] Crearemos el objeto screen con esta función estática void Notifier::init(const std::string &icon_file, std::shared_ptr text) { Notifier::notifier_ = new Notifier(icon_file, text); } // [SINGLETON] Destruiremos el objeto screen con esta función estática void Notifier::destroy() { delete Notifier::notifier_; } // [SINGLETON] Con este método obtenemos el objeto screen y podemos trabajar con él Notifier *Notifier::get() { return Notifier::notifier_; } // Constructor Notifier::Notifier(std::string icon_file, std::shared_ptr text) : renderer_(Screen::get()->getRenderer()), icon_texture_(!icon_file.empty() ? std::make_unique(renderer_, icon_file) : nullptr), text_(text), bg_color_(param.notification.color), wait_time_(150), stack_(false), has_icons_(!icon_file.empty()) {} // Dibuja las notificaciones por pantalla void Notifier::render() { for (int i = (int)notifications_.size() - 1; i >= 0; --i) { notifications_[i].sprite->render(); } } // Actualiza el estado de las notificaiones void Notifier::update() { for (int i = 0; i < (int)notifications_.size(); ++i) { // Si la notificación anterior está "saliendo", no hagas nada if (i > 0) { if (notifications_[i - 1].state == NotificationStatus::RISING) { break; } } notifications_[i].counter++; // Hace sonar la notificación en el primer frame if (notifications_[i].counter == 1) { if (param.notification.sound) { if (notifications_[i].state == NotificationStatus::RISING) { // Reproduce el sonido de la notificación JA_PlaySound(Resource::get()->getSound("notify.wav")); } } } // Comprueba los estados if (notifications_[i].state == NotificationStatus::RISING) { const float step = ((float)notifications_[i].counter / notifications_[i].travel_dist); const int alpha = 255 * step; if (param.notification.pos_v == NotifyPosition::TOP) { notifications_[i].rect.y++; } else { notifications_[i].rect.y--; } notifications_[i].texture->setAlpha(alpha); if (notifications_[i].rect.y == notifications_[i].y) { notifications_[i].state = NotificationStatus::STAY; notifications_[i].texture->setAlpha(255); notifications_[i].counter = 0; } } else if (notifications_[i].state == NotificationStatus::STAY) { if (notifications_[i].counter == wait_time_) { notifications_[i].state = NotificationStatus::VANISHING; notifications_[i].counter = 0; } } else if (notifications_[i].state == NotificationStatus::VANISHING) { const float step = (notifications_[i].counter / (float)notifications_[i].travel_dist); const int alpha = 255 * (1 - step); if (param.notification.pos_v == NotifyPosition::TOP) { notifications_[i].rect.y--; } else { notifications_[i].rect.y++; } notifications_[i].texture->setAlpha(alpha); if (notifications_[i].rect.y == notifications_[i].y - notifications_[i].travel_dist) { notifications_[i].state = NotificationStatus::FINISHED; } } notifications_[i].sprite->setPosition(notifications_[i].rect); } clearFinishedNotifications(); } // Elimina las notificaciones finalizadas void Notifier::clearFinishedNotifications() { for (int i = (int)notifications_.size() - 1; i >= 0; --i) { if (notifications_[i].state == NotificationStatus::FINISHED) { notifications_.erase(notifications_.begin() + i); } } } void Notifier::showText(std::vector texts, int icon, const std::string &code) { // Si no hay texto, acaba if (texts.empty()) { return; } // Si las notificaciones no se apilan, elimina las anteriores if (!stack_) { clearNotifications(); } // Elimina las cadenas vacías texts.erase(std::remove_if(texts.begin(), texts.end(), [](const std::string &s) { return s.empty(); }), texts.end()); // Encuentra la cadena más larga std::string longest; for (const auto &text : texts) { if (text.length() > longest.length()) longest = text; } // Inicializa variables constexpr int icon_size = 16; constexpr int padding_out = 1; const auto padding_in_h = text_->getCharacterSize(); const auto padding_in_v = text_->getCharacterSize() / 2; const int icon_space = icon >= 0 ? icon_size + padding_in_h : 0; const int width = text_->lenght(longest) + (padding_in_h * 2) + icon_space; const int height = (text_->getCharacterSize() * texts.size()) + (padding_in_v * 2); const auto shape = NotificationShape::SQUARED; // Posición horizontal auto desp_h = 0; if (param.notification.pos_h == NotifyPosition::LEFT) { desp_h = padding_out; } else if (param.notification.pos_h == NotifyPosition::MIDDLE) { desp_h = ((param.game.width / 2) - (width / 2)); } else { desp_h = param.game.width - width - padding_out; } // Posición vertical const int desp_v = (param.notification.pos_v == NotifyPosition::TOP) ? padding_out : (param.game.height - height - padding_out); // Offset const auto travel_dist = height + padding_out; auto offset = 0; if (param.notification.pos_v == NotifyPosition::TOP) { offset = !notifications_.empty() ? notifications_.back().y + travel_dist : desp_v; } else { offset = !notifications_.empty() ? notifications_.back().y - travel_dist : desp_v; } // Crea la notificacion Notification n; // Inicializa variables n.code = code; n.y = offset; n.travel_dist = travel_dist; n.texts = texts; n.shape = shape; auto y_pos = offset + (param.notification.pos_v == NotifyPosition::TOP ? -travel_dist : travel_dist); n.rect = {desp_h, y_pos, width, height}; // Crea la textura n.texture = std::make_shared(renderer_); n.texture->createBlank(width, height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET); n.texture->setBlendMode(SDL_BLENDMODE_BLEND); // Prepara para dibujar en la textura n.texture->setAsRenderTarget(renderer_); // Dibuja el fondo de la notificación SDL_SetRenderDrawColor(renderer_, bg_color_.r, bg_color_.g, bg_color_.b, 255); SDL_Rect rect; if (shape == NotificationShape::ROUNDED) { rect = {4, 0, width - (4 * 2), height}; SDL_RenderFillRect(renderer_, &rect); rect = {4 / 2, 1, width - 4, height - 2}; SDL_RenderFillRect(renderer_, &rect); rect = {1, 4 / 2, width - 2, height - 4}; SDL_RenderFillRect(renderer_, &rect); rect = {0, 4, width, height - (4 * 2)}; SDL_RenderFillRect(renderer_, &rect); } else if (shape == NotificationShape::SQUARED) { SDL_RenderClear(renderer_); } // Dibuja el icono de la notificación if (has_icons_ && icon >= 0 && texts.size() >= 2) { auto sp = std::make_unique(icon_texture_, (SDL_Rect){0, 0, icon_size, icon_size}); sp->setPosition({padding_in_h, padding_in_v, icon_size, icon_size}); sp->setSpriteClip({icon_size * (icon % 10), icon_size * (icon / 10), icon_size, icon_size}); sp->render(); } // Escribe el texto de la notificación const Color color{255, 255, 255}; int iterator = 0; for (const auto &text : texts) { text_->writeColored(padding_in_h + icon_space, padding_in_v + iterator * (text_->getCharacterSize() + 1), text, color); ++iterator; } // Deja de dibujar en la textura SDL_SetRenderTarget(renderer_, nullptr); // Crea el sprite de la notificación n.sprite = std::make_shared(n.texture, n.rect); // Deja la notificación invisible n.texture->setAlpha(0); // Añade la notificación a la lista notifications_.emplace_back(n); } // Indica si hay notificaciones activas bool Notifier::isActive() { return !notifications_.empty(); } // Finaliza y elimnina todas las notificaciones activas void Notifier::clearNotifications() { for (auto ¬ification : notifications_) { notification.state = NotificationStatus::FINISHED; } clearFinishedNotifications(); } // Obtiene los códigos de las notificaciones std::vector Notifier::getCodes() { std::vector codes; for (const auto ¬ification : notifications_) { codes.emplace_back(notification.code); } return codes; }