#include "screen.h" #include // for SDL_DISABLE, SDL_ENABLE #include // for SDL_ShowCursor #include // for SDL_PIXELFORMAT_RGBA8888 #include // for SDL_GetTicks #include // for max, min #include // for basic_ifstream, ifstream #include // for istreambuf_iterator, operator!= #include // for basic_string, operator+, to_string, cha... #include // for vector #include "asset.h" // for Asset #include "dbgtxt.h" // for dbg_print #include "global_inputs.h" // for servicePressedCounter #include "input.h" // for Input, inputs_e, INPUT_DO_NOT_ALLOW_REPEAT #include "notify.h" // for Notify #include "on_screen_help.h" // for OnScreenHelp #include "options.h" // for options #include "param.h" // for param #ifndef NO_SHADERS #include "jail_shader.h" // for init, render #endif // [SINGLETON] Hay que definir las variables estáticas, desde el .h sólo la hemos declarado Screen *Screen::screen = nullptr; // [SINGLETON] Crearemos el objeto screen con esta función estática void Screen::init(SDL_Window *window, SDL_Renderer *renderer) { Screen::screen = new Screen(window, renderer); } // [SINGLETON] Destruiremos el objeto screen con esta función estática void Screen::destroy() { delete Screen::screen; } // [SINGLETON] Con este método obtenemos el objeto screen y podemos trabajar con él Screen *Screen::get() { return Screen::screen; } // Constructor Screen::Screen(SDL_Window *window, SDL_Renderer *renderer) : window(window), renderer(renderer) { // Copia punteros input = Input::get(); asset = Asset::get(); // Inicializa variables srcrect = {0, 0, param.game.width, param.game.height}; dstrect = {0, 0, param.game.width, param.game.height}; borderColor = {0, 0, 0}; flashEffect.enabled = false; flashEffect.counter = 0; flashEffect.lenght = 0; flashEffect.color = {0xFF, 0xFF, 0xFF}; shakeEffect.enabled = false; shakeEffect.desp = 2; shakeEffect.delay = 3; shakeEffect.counter = 0; shakeEffect.lenght = 8; shakeEffect.remaining = 0; shakeEffect.originalPos = 0; shakeEffect.originalWidth = param.game.width; attenuateEffect = false; fpsTicks = 0; fpsCounter = 0; fps = 0; #ifdef DEBUG showInfo = true; #else showInfo = false; #endif SDL_DisplayMode DM; SDL_GetCurrentDisplayMode(0, &DM); infoResolution = std::to_string(DM.w) + " X " + std::to_string(DM.h) + " AT " + std::to_string(DM.refresh_rate) + " HZ"; // Crea los objetos notify = std::make_unique(renderer, "", asset->get("8bithud.png"), asset->get("8bithud.txt"), asset->get("notify.wav")); // Define el color del borde para el modo de pantalla completa borderColor = {0x00, 0x00, 0x00}; // Crea las textura donde se dibujan los graficos del juego gameCanvas = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height); shaderCanvas = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height); // Establece el modo de video setVideoMode(options.video.mode); // Muestra la ventana SDL_ShowWindow(window); } // Destructor Screen::~Screen() { SDL_DestroyTexture(gameCanvas); SDL_DestroyTexture(shaderCanvas); } // Limpia la pantalla void Screen::clean(color_t color) { SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, 0xFF); SDL_RenderClear(renderer); } // Prepara para empezar a dibujar en la textura de juego void Screen::start() { SDL_SetRenderTarget(renderer, gameCanvas); } // Vuelca el contenido del renderizador en pantalla void Screen::blit() { // Actualiza el contador de FPS fpsCounter++; // Actualiza y dibuja el efecto de flash en la pantalla doFlash(); // Atenua la pantalla doAttenuate(); // Muestra la ayuda por pantalla OnScreenHelp::get()->render(); // Muestra información por pantalla displayInfo(); // Muestra las notificaciones notify->render(); #ifdef NO_SHADERS // Vuelve a dejar el renderizador en modo normal SDL_SetRenderTarget(renderer, nullptr); // Borra el contenido previo SDL_SetRenderDrawColor(renderer, borderColor.r, borderColor.g, borderColor.b, 0xFF); SDL_RenderClear(renderer); // Copia la textura de juego en el renderizador en la posición adecuada if (shakeEffect.enabled) SDL_RenderCopy(renderer, gameCanvas, nullptr, nullptr); SDL_RenderCopy(renderer, gameCanvas, &srcrect, &dstrect); // Muestra por pantalla el renderizador SDL_RenderPresent(renderer); #else if (options.video.shaders) { SDL_SetRenderTarget(renderer, shaderCanvas); SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF); SDL_RenderClear(renderer); if (shakeEffect.enabled) SDL_RenderCopy(renderer, gameCanvas, nullptr, nullptr); SDL_RenderCopy(renderer, gameCanvas, &srcrect, &dstrect); SDL_SetRenderTarget(renderer, nullptr); shader::render(); } else { // Vuelve a dejar el renderizador en modo normal SDL_SetRenderTarget(renderer, nullptr); // Borra el render SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF); SDL_RenderClear(renderer); // Copia la textura de juego en el renderizador en la posición adecuada if (shakeEffect.enabled) SDL_RenderCopy(renderer, gameCanvas, nullptr, nullptr); SDL_RenderCopy(renderer, gameCanvas, &srcrect, &dstrect); // Muestra por pantalla el renderizador SDL_RenderPresent(renderer); } #endif } // Establece el modo de video void Screen::setVideoMode(screenVideoMode videoMode) { options.video.mode = videoMode; #ifdef ARCADE options.video.mode = screenVideoMode::WINDOW; #endif switch (options.video.mode) { case screenVideoMode::WINDOW: { // Cambia a modo de ventana SDL_SetWindowFullscreen(window, 0); #ifdef ARCADE // Oculta el puntero SDL_ShowCursor(SDL_DISABLE); #else // Muestra el puntero SDL_ShowCursor(SDL_ENABLE); #endif // Modifica el tamaño de la ventana SDL_SetWindowSize(window, param.game.width * options.video.window.size, param.game.height * options.video.window.size); SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); break; } // Si está activo el modo de pantalla completa añade el borde case screenVideoMode::FULLSCREEN: { // Aplica el modo de video SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); // Oculta el puntero SDL_ShowCursor(SDL_DISABLE); break; } default: break; } // Reinicia los shaders if (options.video.shaders) { std::ifstream f(asset->get("crtpi.glsl").c_str()); std::string source((std::istreambuf_iterator(f)), std::istreambuf_iterator()); shader::init(window, shaderCanvas, source.c_str()); } // Actualiza variables // shakeEffect.originalPos = srcrect.x; // shakeEffect.originalWidth = srcrect.w; } // Camibia entre pantalla completa y ventana void Screen::switchVideoMode() { options.video.mode = options.video.mode == screenVideoMode::WINDOW ? screenVideoMode::FULLSCREEN : screenVideoMode::WINDOW; setVideoMode(options.video.mode); } // Cambia el tamaño de la ventana void Screen::setWindowSize(int size) { options.video.window.size = size; setVideoMode(screenVideoMode::WINDOW); } // Reduce el tamaño de la ventana void Screen::decWindowSize() { --options.video.window.size; options.video.window.size = std::max(options.video.window.size, 1); setVideoMode(screenVideoMode::WINDOW); } // Aumenta el tamaño de la ventana void Screen::incWindowSize() { ++options.video.window.size; options.video.window.size = std::min(options.video.window.size, 4); setVideoMode(screenVideoMode::WINDOW); } // Cambia el color del borde void Screen::setBorderColor(color_t color) { borderColor = color; } // Cambia el tipo de mezcla void Screen::setBlendMode(SDL_BlendMode blendMode) { SDL_SetRenderDrawBlendMode(renderer, blendMode); } // Actualiza la lógica de la clase void Screen::update() { updateShake(); notify->update(); updateFPS(); OnScreenHelp::get()->update(); } // Comprueba las entradas void Screen::checkInput() { #ifndef ARCADE // Comprueba el teclado para cambiar entre pantalla completa y ventana if (input->checkInput(input_window_fullscreen, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_KEYBOARD)) { switchVideoMode(); const std::string mode = options.video.mode == screenVideoMode::WINDOW ? "Window" : "Fullscreen"; showNotification(mode + " mode"); return; } // Comprueba el teclado para decrementar el tamaño de la ventana if (input->checkInput(input_window_dec_size, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_KEYBOARD)) { decWindowSize(); const std::string size = std::to_string(options.video.window.size); showNotification("Window size x" + size); return; } // Comprueba el teclado para incrementar el tamaño de la ventana if (input->checkInput(input_window_inc_size, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_KEYBOARD)) { incWindowSize(); const std::string size = std::to_string(options.video.window.size); showNotification("Window size x" + size); return; } #endif // Comprueba el teclado para activar o desactivar los shaders if (input->checkInput(input_video_shaders, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_KEYBOARD)) { switchShaders(); return; } #ifdef DEBUG // Comprueba el teclado para mostrar la información de debug if (input->checkInput(input_showinfo, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_KEYBOARD)) { showInfo = !showInfo; return; } #endif for (int i = 0; i < input->getNumControllers(); ++i) { // Comprueba los mandos para activar o desactivar los shaders if (input->checkModInput(input_service, input_video_shaders, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_GAMECONTROLLER, i)) { switchShaders(); return; } // Comprueba los mandos para mostrar la información de debug if (input->checkModInput(input_service, input_showinfo, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_GAMECONTROLLER, i)) { showInfo = !showInfo; return; } } } // Agita la pantalla void Screen::shake() { // Si no hay un shake effect activo, se guarda una copia de los valores actuales antes de modificarlos if (!shakeEffect.enabled) { shakeEffect.enabled = true; shakeEffect.originalPos = srcrect.x; shakeEffect.originalWidth = srcrect.w; srcrect.w -= shakeEffect.desp; dstrect.w = srcrect.w; } // Si ya hay un shake effect en marcha no se pilla el origen, solo se renuevan los contadores shakeEffect.remaining = shakeEffect.lenght; shakeEffect.counter = shakeEffect.delay; } // Actualiza la logica para agitar la pantalla void Screen::updateShake() { if (shakeEffect.enabled) { if (shakeEffect.counter > 0) { shakeEffect.counter--; } else { shakeEffect.counter = shakeEffect.delay; const auto srcdesp = shakeEffect.remaining % 2 == 0 ? 0 : shakeEffect.desp; const auto dstdesp = shakeEffect.remaining % 2 == 1 ? 0 : shakeEffect.desp; srcrect.x = shakeEffect.originalPos + srcdesp; dstrect.x = shakeEffect.originalPos + dstdesp; shakeEffect.remaining--; shakeEffect.enabled = shakeEffect.remaining == -1 ? false : true; if (!shakeEffect.enabled) { srcrect.x = shakeEffect.originalPos; srcrect.w = shakeEffect.originalWidth; dstrect = srcrect; } } } } // Pone la pantalla de color void Screen::flash(color_t color, int lenght) { flashEffect.enabled = true; flashEffect.counter = 0; flashEffect.lenght = lenght; flashEffect.color = color; } // Actualiza y dibuja el efecto de flash en la pantalla void Screen::doFlash() { if (flashEffect.enabled) { // Dibuja el color del flash en la textura SDL_Texture *temp = SDL_GetRenderTarget(renderer); SDL_SetRenderTarget(renderer, gameCanvas); SDL_SetRenderDrawColor(renderer, flashEffect.color.r, flashEffect.color.g, flashEffect.color.b, 0xFF); SDL_RenderClear(renderer); SDL_SetRenderTarget(renderer, temp); // Actualiza la lógica del efecto flashEffect.counter < flashEffect.lenght ? flashEffect.counter++ : flashEffect.enabled = false; } } // Atenua la pantalla void Screen::doAttenuate() { if (attenuateEffect) { SDL_Texture *temp = SDL_GetRenderTarget(renderer); SDL_SetRenderTarget(renderer, gameCanvas); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 64); SDL_RenderFillRect(renderer, nullptr); SDL_SetRenderTarget(renderer, temp); } } // Activa/desactiva los shaders void Screen::switchShaders() { options.video.shaders = !options.video.shaders; setVideoMode(options.video.mode); const std::string value = options.video.shaders ? "on" : "off"; showNotification("Shaders " + value); } // Atenua la pantalla void Screen::attenuate(bool value) { attenuateEffect = value; } // Muestra una notificación de texto por pantalla; void Screen::showNotification(std::string text1, std::string text2, int icon) { notify->showText(text1, text2, icon); } // Obtiene el puntero al renderizador SDL_Renderer *Screen::getRenderer() { return renderer; } // Calcula los frames por segundo void Screen::updateFPS() { if (SDL_GetTicks() - fpsTicks > 1000) { fpsTicks = SDL_GetTicks(); fps = fpsCounter; fpsCounter = 0; } } // Muestra información por pantalla void Screen::displayInfo() { if (showInfo) { // FPS const std::string fpstext = std::to_string(fps) + " FPS"; dbg_print(param.game.width - fpstext.length() * 8, 0, fpstext.c_str(), 255, 255, 0); // Resolution dbg_print(0, 0, infoResolution.c_str(), 255, 255, 0); dbg_print(0, 8, std::to_string(globalInputs::servicePressedCounter[0]).c_str(), 255, 255, 0); } } // Indica si hay alguna notificación activa en pantalla bool Screen::notificationsAreActive() { return notify->active(); }