#include "game/scenes/instructions.h" #include #include // for max #include // for char_traits, basic_ostream, operator<< #include // for basic_string #include "core/audio/audio.hpp" // for Audio::update #include "core/input/global_inputs.hpp" // for GlobalInputs::handle #include "core/input/input.h" // for Input, REPEAT_FALSE, InputAction #include "core/locale/lang.h" // for Lang #include "core/rendering/screen.h" // for Screen #include "core/rendering/sprite.h" // for Sprite #include "core/rendering/text.h" // for Text, TXT_CENTER, TXT_COLOR, TXT_SHADOW #include "core/rendering/texture.h" // for Texture #include "core/resources/resource.h" #include "game/defaults.hpp" // for shdwTxtColor, GAMECANVAS_CENTER_X, GAME... #include "utils/utils.h" // for Color, Section // Constructor Instructions::Instructions(SDL_Renderer *renderer, Section *section) { // Copia los punteros this->renderer_ = renderer; this->section_ = section; // Texturas (handles compartidos de Resource) Resource *resource = Resource::get(); item_textures_.push_back(resource->getTexture("item_points1_disk.png")); item_textures_.push_back(resource->getTexture("item_points2_gavina.png")); item_textures_.push_back(resource->getTexture("item_points3_pacmar.png")); item_textures_.push_back(resource->getTexture("item_clock.png")); item_textures_.push_back(resource->getTexture("item_coffee.png")); item_textures_.push_back(resource->getTexture("item_coffee_machine.png")); event_handler_ = new SDL_Event(); sprite_ = new Sprite(0, 0, 16, 16, item_textures_[0], renderer); text_ = resource->getText("smb2"); // Crea un backbuffer para el renderizador backbuffer_ = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT); if (backbuffer_ != nullptr) { SDL_SetTextureScaleMode(backbuffer_, Texture::currentScaleMode); } else { std::cout << "Error: textTexture could not be created!\nSDL Error: " << SDL_GetError() << '\n'; } // Inicializa variables ticks_ = 0; ticks_speed_ = 15; manual_quit_ = false; counter_ = 0; counter_end_ = 600; finished_ = false; quit_requested_ = false; } // Destructor Instructions::~Instructions() { // itemTextures y text son propiedad de Resource — no liberar. item_textures_.clear(); delete sprite_; delete event_handler_; SDL_DestroyTexture(backbuffer_); } // Actualiza las variables void Instructions::update() { // Bombea el stream de música: si no se llama, el buffer se vacía y la // música se corta hasta que volvamos a una escena que sí lo haga. Audio::update(); // Comprueba las entradas checkInput(); // Actualiza las variables if (SDL_GetTicks() - ticks_ > ticks_speed_) { // Actualiza el contador de ticks ticks_ = SDL_GetTicks(); if (mode_ == Mode::AUTO) { // Modo automático counter_++; if (counter_ == counter_end_) { finished_ = true; } } else { // Modo manual ++counter_ %= 60000; if (manual_quit_) { finished_ = true; } } } } // Pinta en pantalla void Instructions::render() { // Pinta en pantalla SDL_Rect window = {0, 0, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT}; SDL_Rect src_rect = {0, 0, 16, 16}; const Color ORANGE_COLOR = {0xFF, 0x7A, 0x00}; const SDL_Rect DEST_RECT1 = {60, 88 + (16 * 0), 16, 16}; // Disquito const SDL_Rect DEST_RECT2 = {60, 88 + (16 * 1), 16, 16}; // Gavineixon const SDL_Rect DEST_RECT3 = {60, 88 + (16 * 2), 16, 16}; // Pacmar const SDL_Rect DEST_RECT4 = {60, 88 + (16 * 3), 16, 16}; // Time Stopper const SDL_Rect DEST_RECT5 = {60, 88 + (16 * 4), 16, 16}; // Coffee // Pinta en el backbuffer el texto y los sprites SDL_SetRenderTarget(renderer_, backbuffer_); SDL_SetRenderDrawColor(renderer_, bgColor.r, bgColor.g, bgColor.b, 255); SDL_RenderClear(renderer_); // Escribe el texto text_->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 8, Lang::get()->getText(11), 1, ORANGE_COLOR, 1, shdwTxtColor); text_->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 24, Lang::get()->getText(12), 1, noColor, 1, shdwTxtColor); text_->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 34, Lang::get()->getText(13), 1, noColor, 1, shdwTxtColor); text_->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 48, Lang::get()->getText(14), 1, noColor, 1, shdwTxtColor); text_->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 58, Lang::get()->getText(15), 1, noColor, 1, shdwTxtColor); text_->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 75, Lang::get()->getText(16), 1, ORANGE_COLOR, 1, shdwTxtColor); text_->writeShadowed(84, 92, Lang::get()->getText(17), shdwTxtColor); text_->writeShadowed(84, 108, Lang::get()->getText(18), shdwTxtColor); text_->writeShadowed(84, 124, Lang::get()->getText(19), shdwTxtColor); text_->writeShadowed(84, 140, Lang::get()->getText(20), shdwTxtColor); text_->writeShadowed(84, 156, Lang::get()->getText(21), shdwTxtColor); if ((mode_ == Mode::MANUAL) && (counter_ % 50 > 14)) { text_->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, GAMECANVAS_HEIGHT - 12, Lang::get()->getText(22), 1, ORANGE_COLOR, 1, shdwTxtColor); } // Disquito sprite_->setTexture(item_textures_[0]); sprite_->setPos(DEST_RECT1); src_rect.y = 16 * (((counter_ + 12) / 36) % 2); sprite_->setSpriteClip(src_rect); sprite_->render(); // Gavineixon sprite_->setTexture(item_textures_[1]); sprite_->setPos(DEST_RECT2); src_rect.y = 16 * (((counter_ + 9) / 36) % 2); sprite_->setSpriteClip(src_rect); sprite_->render(); // Pacmar sprite_->setTexture(item_textures_[2]); sprite_->setPos(DEST_RECT3); src_rect.y = 16 * (((counter_ + 6) / 36) % 2); sprite_->setSpriteClip(src_rect); sprite_->render(); // Time Stopper sprite_->setTexture(item_textures_[3]); sprite_->setPos(DEST_RECT4); src_rect.y = 16 * (((counter_ + 3) / 36) % 2); sprite_->setSpriteClip(src_rect); sprite_->render(); // Coffee sprite_->setTexture(item_textures_[4]); sprite_->setPos(DEST_RECT5); src_rect.y = 16 * (((counter_ + 0) / 36) % 2); sprite_->setSpriteClip(src_rect); sprite_->render(); // Cambia el destino de renderizado SDL_SetRenderTarget(renderer_, nullptr); // Prepara para empezar a dibujar en la textura de juego Screen::get()->start(); // Limpia la pantalla Screen::get()->clean(bgColor); // Establece la ventana del backbuffer if (mode_ == Mode::AUTO) { window.y = std::max(8, GAMECANVAS_HEIGHT - counter_ + 100); } else { window.y = 0; } // Copia el backbuffer al renderizador SDL_FRect f_window = {(float)window.x, (float)window.y, (float)window.w, (float)window.h}; SDL_RenderTexture(renderer_, backbuffer_, nullptr, &f_window); // Vuelca el contenido del renderizador en pantalla Screen::get()->blit(); } // Comprueba los eventos void Instructions::checkEvents() { #ifndef __EMSCRIPTEN__ // Comprueba los eventos que hay en la cola while (static_cast(SDL_PollEvent(event_handler_)) != 0) { // Evento de salida de la aplicación if (event_handler_->type == SDL_EVENT_QUIT) { quit_requested_ = true; finished_ = true; break; } } #endif } // Comprueba las entradas void Instructions::checkInput() { #ifndef __EMSCRIPTEN__ if (Input::get()->checkInput(input_exit, REPEAT_FALSE)) { quit_requested_ = true; finished_ = true; return; } #endif if (GlobalInputs::handle()) { return; } if (Input::get()->checkInput(input_pause, REPEAT_FALSE) || Input::get()->checkInput(input_accept, REPEAT_FALSE) || Input::get()->checkInput(input_fire_left, REPEAT_FALSE) || Input::get()->checkInput(input_fire_center, REPEAT_FALSE) || Input::get()->checkInput(input_fire_right, REPEAT_FALSE)) { if (mode_ == Mode::AUTO) { finished_ = true; } else { if (counter_ > 30) { manual_quit_ = true; } } } } // Bucle para la pantalla de instrucciones (compatibilidad) void Instructions::run(Mode mode) { start(mode); while (!finished_) { update(); checkEvents(); render(); } // Aplica los cambios de sección según el resultado if (quit_requested_) { section_->name = SECTION_PROG_QUIT; } else { section_->name = SECTION_PROG_TITLE; section_->subsection = (mode == Mode::AUTO) ? SUBSECTION_TITLE_1 : SUBSECTION_TITLE_3; } } // Inicia las instrucciones (sin bucle) void Instructions::start(Mode mode) { mode_ = mode; finished_ = false; quit_requested_ = false; manual_quit_ = false; counter_ = 0; ticks_ = 0; } // Indica si las instrucciones han terminado auto Instructions::hasFinished() const -> bool { return finished_; } // Indica si se ha solicitado salir de la aplicación auto Instructions::isQuitRequested() const -> bool { return quit_requested_; }