#pragma once #include // Para SDL_BlendMode #include // Para SDL_Rect, SDL_Point #include // Para SDL_Renderer, SDL_Texture #include // Para Uint32 #include // Para SDL_Window #include // Para string #include "param.h" // Para Param, ParamGame, param #include "utils.h" // Para Color #include "options.h" enum class ScreenFilter : int { NEAREST = 0, LINEAL = 1, }; enum class ScreenVideoMode : bool { WINDOW = false, FULLSCREEN = true, }; class Screen { private: // Constantes static constexpr int WINDOWS_DECORATIONS_ = 35; // Estructuras struct FPS { Uint32 ticks; // Tiempo en milisegundos desde que se comenzó a contar. int frameCount; // Número acumulado de frames en el intervalo. int lastValue; // Número de frames calculado en el último segundo. // Constructor para inicializar la estructura. FPS() : ticks(0), frameCount(0), lastValue(0) {} // Incrementador que se llama en cada frame. void increment() { frameCount++; } // Método para calcular y devolver el valor de FPS. int calculate(Uint32 currentTicks) { if (currentTicks - ticks >= 1000) // Si ha pasado un segundo o más. { lastValue = frameCount; // Actualizamos el valor del último FPS. frameCount = 0; // Reiniciamos el contador de frames. ticks = currentTicks; // Actualizamos el tiempo base. } return lastValue; } }; struct FlashEffect { bool enabled; // Indica si el efecto está activo int lenght; // Duración del efecto int delay; // Frames iniciales en los que no se aplica int counter; // Contador para el efecto Color color; // Color del efecto // Constructor explicit FlashEffect(bool enabled = false, int lenght = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF)) : enabled(enabled), lenght(lenght), delay(delay), counter(lenght), color(color) {} // Actualiza void update() { (enabled && counter > 0) ? counter-- : enabled = false; } // Indica si se pude dibujar bool isRendarable() { return enabled && counter < lenght - delay; } }; struct ShakeEffect { int desp; // Pixels de desplazamiento para agitar la pantalla en el eje x int delay; // Retraso entre cada desplazamiento de la pantalla al agitarse int counter; // Contador para el retraso int lenght; // Cantidad de desplazamientos a realizar int remaining; // Cantidad de desplazamientos pendientes a realizar int original_pos; // Posición inicial de la pantalla para dejarla igual tras el desplazamiento int original_width; // Anchura inicial de la pantalla para dejarla igual tras el desplazamiento bool enabled; // Indica si el efecto está activo // Constructor explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int origPos = 0, int origWidth = 800) : desp(dp), delay(dl), counter(cnt), lenght(len), remaining(rem), original_pos(origPos), original_width(origWidth), enabled(en) {} // Método para habilitar el efecto void enable(SDL_FRect &src_rect, SDL_FRect &dst_rect) { if (!enabled) { // Configurar el estado inicial si el efecto no está activo enabled = true; original_pos = src_rect.x; original_width = src_rect.w; // Acortar los anchos iniciales durante el efecto src_rect.w -= desp; dst_rect.w = src_rect.w; } // Renovar contadores y duración del efecto remaining = lenght; counter = delay; } // Método para actualizar el efecto de movimiento/agitación void update(SDL_FRect &src_rect, SDL_FRect &dst_rect) { if (enabled) { if (counter > 0) { counter--; } else { counter = delay; // Calcular desplazamientos según el estado de la agitación const auto SRC_DESP = (remaining % 2 == 0) ? 0 : desp; const auto DST_DESP = (remaining % 2 == 1) ? 0 : desp; src_rect.x = original_pos + SRC_DESP; dst_rect.x = original_pos + DST_DESP; remaining--; if (remaining == -1) { // Restaurar posición y dimensiones originales enabled = false; src_rect.x = original_pos; src_rect.w = original_width; dst_rect.x = original_pos; dst_rect.w = original_width; } } } } // Método para comprobar si el efecto está activo bool isEnabled() const { return enabled; } }; // [SINGLETON] Objeto privado static Screen *screen_; // Objetos y punteros SDL_Window *window_; // Ventana de la aplicación SDL_Renderer *renderer_; // El renderizador de la ventana SDL_Texture *game_canvas_; // Textura donde se dibuja todo antes de volcarse al renderizador // Variables SDL_FRect src_rect_; // Coordenadas de donde va a pillar la textura del juego para dibujarla SDL_FRect dst_rect_; // Coordenadas donde se va a dibujar la textura del juego sobre la pantalla o ventana FPS fps_; // Variable para gestionar los frames por segundo std::string info_resolution_; // Texto con la informacion de la pantalla std::string shader_source_; // Almacena el contenido del archivo GLSL FlashEffect flash_effect_; // Variable para gestionar el efecto de flash ShakeEffect shake_effect_; // Variable para gestionar el efecto de agitar la pantalla bool attenuate_effect_ = false; // Indica si la pantalla ha de estar atenuada #ifdef DEBUG bool show_debug_info_ = false; // Indica si ha de mostrar/ocultar la información de la pantalla #else bool show_debug_info_ = false; // Indica si ha de mostrar/ocultar la información de la pantalla #endif // Dibuja el efecto de flash en la pantalla void renderFlash(); // Aplica el efecto de agitar la pantalla void renderShake(); // Muestra información por pantalla void renderInfo(); // Selecciona y ejecuta el método de renderizado adecuado basado en la configuración de shaders void renderScreen(); // Carga el contenido del archivo GLSL void loadShaders(); // Inicializa los shaders void initShaders(); // Calcula el tamaño de la ventana void adjustWindowSize(); // Ajusta el tamaño lógico del renderizador void adjustRenderLogicalSize() { SDL_SetRenderLogicalPresentation(renderer_, param.game.width, param.game.height, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE); } // Obtiene el tamaño máximo de zoom posible para la ventana int getMaxZoom(); // Renderiza todos los overlays y efectos void renderOverlays(); // Atenua la pantalla void renderAttenuate(); // Constructor Screen(SDL_Window *window, SDL_Renderer *renderer); // Destructor ~Screen(); public: // [SINGLETON] Crearemos el objeto con esta función estática static void init(SDL_Window *window, SDL_Renderer *renderer); // [SINGLETON] Destruiremos el objeto con esta función estática static void destroy(); // [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él static Screen *get(); // Actualiza la lógica de la clase void update(); // Limpia la pantalla void clean(Color color = Color(0x00, 0x00, 0x00)); // Prepara para empezar a dibujar en la textura de juego void start(); // Vuelca el contenido del renderizador en pantalla void render(); // Establece el modo de video void setVideoMode(ScreenVideoMode mode = options.video.mode); // Cambia entre pantalla completa y ventana void toggleVideoMode(); // Cambia el tamaño de la ventana void setWindowZoom(int size); // Reduce el tamaño de la ventana bool decWindowZoom(); // Aumenta el tamaño de la ventana bool incWindowZoom(); // Agita la pantalla void shake() { shake_effect_.enable(src_rect_, dst_rect_); } // Pone la pantalla de color void flash(Color color, int lenght = 10, int delay = 0) { flash_effect_ = FlashEffect(true, lenght, delay, color); } // Activa / desactiva los shaders void toggleShaders() { options.video.shaders = !options.video.shaders; } // Activa / desactiva la información de debug void toggleDebugInfo() { show_debug_info_ = !show_debug_info_; } // Getters SDL_Renderer *getRenderer() { return renderer_; } // Muestra la ventana void show() { SDL_ShowWindow(window_); } // Oculta la ventana void hide() { SDL_HideWindow(window_); } // Atenua la pantalla void attenuate(bool value) { attenuate_effect_ = value; } };