#include "instructions.h" #include // Para SDL_BLENDMODE_BLEND #include // Para SDL_PollEvent, SDL_Event #include // Para SDL_PIXELFORMAT_RGBA8888 #include // Para SDL_GetTicks #include // Para max #include // Para array #include // Para move #include // Para vector #include "fade.h" // Para Fade, FadeMode, FadeType #include "global_events.h" // Para check #include "global_inputs.h" // Para check, update #include "audio.h" // Para JA_GetMusicState, JA_Music_state #include "lang.h" // Para getText #include "param.h" // Para Param, param, ParamGame, ParamFade #include "resource.h" // Para Resource #include "screen.h" // Para Screen #include "section.h" // Para Name, name, Options, options #include "sprite.h" // Para Sprite #include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR, TEXT_... #include "tiled_bg.h" // Para TiledBG, TiledBGMode #include "utils.h" // Para Color, shdw_txt_color, Zone, no_color // Constructor Instructions::Instructions() : renderer_(Screen::get()->getRenderer()), texture_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), text_(Resource::get()->getText("smb2")), tiled_bg_(std::make_unique(param.game.game_area.rect, TiledBGMode::STATIC)), fade_(std::make_unique()) { // Configura las texturas SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND); // Inicializa variables Section::name = Section::Name::INSTRUCTIONS; view_ = param.game.game_area.rect; // Inicializa objetos fade_->setColor(param.fade.color); fade_->setType(FadeType::FULLSCREEN); fade_->setPostDuration(param.fade.post_duration); fade_->setMode(FadeMode::IN); fade_->activate(); // Inicializa las líneas con un retraso progresivo de 50 ms lines_ = initializeLines(256); // Rellena la textura de texto fillTexture(); // Inicializa los sprites de los items iniSprites(); } // Destructor Instructions::~Instructions() { item_textures_.clear(); sprites_.clear(); SDL_DestroyTexture(backbuffer_); SDL_DestroyTexture(texture_); } // Inicializa los sprites de los items void Instructions::iniSprites() { // Inicializa las texturas item_textures_.emplace_back(Resource::get()->getTexture("item_points1_disk.png")); item_textures_.emplace_back(Resource::get()->getTexture("item_points2_gavina.png")); item_textures_.emplace_back(Resource::get()->getTexture("item_points3_pacmar.png")); item_textures_.emplace_back(Resource::get()->getTexture("item_clock.png")); item_textures_.emplace_back(Resource::get()->getTexture("item_coffee.png")); // Inicializa los sprites for (int i = 0; i < (int)item_textures_.size(); ++i) { auto sprite = std::make_unique(item_textures_[i], 0, 0, param.game.item_size, param.game.item_size); sprite->setPosition((SDL_FPoint){sprite_pos_.x, sprite_pos_.y + ((param.game.item_size + item_space_) * i)}); sprites_.push_back(std::move(sprite)); } } // Actualiza los sprites void Instructions::updateSprites() { SDL_FRect src_rect = {0, 0, param.game.item_size, param.game.item_size}; // Disquito src_rect.y = param.game.item_size * (((counter_ + 12) / 36) % 2); sprites_[0]->setSpriteClip(src_rect); // Gavina src_rect.y = param.game.item_size * (((counter_ + 9) / 36) % 2); sprites_[1]->setSpriteClip(src_rect); // Pacmar src_rect.y = param.game.item_size * (((counter_ + 6) / 36) % 2); sprites_[2]->setSpriteClip(src_rect); // Time Stopper src_rect.y = param.game.item_size * (((counter_ + 3) / 36) % 2); sprites_[3]->setSpriteClip(src_rect); // Coffee src_rect.y = param.game.item_size * (((counter_ + 0) / 36) % 2); sprites_[4]->setSpriteClip(src_rect); } // Rellena la textura de texto void Instructions::fillTexture() { const int desp_x = param.game.item_size + 8; // Modifica el renderizador para pintar en la textura auto temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, texture_); // Limpia la textura SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); SDL_RenderClear(renderer_); // Constantes constexpr int num_lines = 4; constexpr int num_item_lines = 4; constexpr int num_post_headers = 2; constexpr int num_pre_headers = 1; constexpr int space_post_header = 20; constexpr int space_pre_header = 28; const int space_between_lines = text_->getCharacterSize() * 1.5f; const int space_between_item_lines = param.game.item_size + item_space_; const int space_new_paragraph = space_between_lines * 0.5f; const int size = (num_lines * space_between_lines) + (num_item_lines * space_between_item_lines) + (num_post_headers * space_post_header) + (num_pre_headers * space_pre_header) + (space_new_paragraph); const int first_line = (param.game.height - size) / 2; // Calcula cual es el texto más largo de las descripciones de los items int lenght = 0; const std::array ITEM_DESCRIPTIONS = { Lang::getText("[INSTRUCTIONS] 07"), Lang::getText("[INSTRUCTIONS] 08"), Lang::getText("[INSTRUCTIONS] 09"), Lang::getText("[INSTRUCTIONS] 10"), Lang::getText("[INSTRUCTIONS] 11")}; for (const auto &desc : ITEM_DESCRIPTIONS) { const int l = text_->lenght(desc); lenght = l > lenght ? l : lenght; } const int ANCHOR_ITEM = (param.game.width - (lenght + desp_x)) / 2; // Escribe el texto de las instrucciones text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, first_line, Lang::getText("[INSTRUCTIONS] 01"), 1, ORANGE_COLOR, 1, SHADOW_TEXT_COLOR); const int anchor1 = first_line + space_post_header; text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_between_lines * 0, Lang::getText("[INSTRUCTIONS] 02"), 1, NO_COLOR, 1, SHADOW_TEXT_COLOR); text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_between_lines * 1, Lang::getText("[INSTRUCTIONS] 03"), 1, NO_COLOR, 1, SHADOW_TEXT_COLOR); text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_new_paragraph + space_between_lines * 2, Lang::getText("[INSTRUCTIONS] 04"), 1, NO_COLOR, 1, SHADOW_TEXT_COLOR); text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_new_paragraph + space_between_lines * 3, Lang::getText("[INSTRUCTIONS] 05"), 1, NO_COLOR, 1, SHADOW_TEXT_COLOR); // Escribe el texto de los objetos y sus puntos const int anchor2 = anchor1 + space_pre_header + space_new_paragraph + space_between_lines * 3; text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor2, Lang::getText("[INSTRUCTIONS] 06"), 1, ORANGE_COLOR, 1, SHADOW_TEXT_COLOR); const int anchor3 = anchor2 + space_post_header; text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 0, Lang::getText("[INSTRUCTIONS] 07"), SHADOW_TEXT_COLOR); text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 1, Lang::getText("[INSTRUCTIONS] 08"), SHADOW_TEXT_COLOR); text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 2, Lang::getText("[INSTRUCTIONS] 09"), SHADOW_TEXT_COLOR); text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 3, Lang::getText("[INSTRUCTIONS] 10"), SHADOW_TEXT_COLOR); text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 4, Lang::getText("[INSTRUCTIONS] 11"), SHADOW_TEXT_COLOR); // Deja el renderizador como estaba SDL_SetRenderTarget(renderer_, temp); // Da valor a la variable sprite_pos_.x = ANCHOR_ITEM; sprite_pos_.y = anchor3 - ((param.game.item_size - text_->getCharacterSize()) / 2); } // Rellena el backbuffer void Instructions::fillBackbuffer() { // Modifica el renderizador para pintar en la textura auto temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, backbuffer_); // Limpia la textura SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); SDL_RenderClear(renderer_); // Coloca el texto de fondo SDL_RenderTexture(renderer_, texture_, nullptr, nullptr); // Dibuja los sprites for (auto &sprite : sprites_) { sprite->render(); } // Deja el renderizador como estaba SDL_SetRenderTarget(renderer_, temp); } // Actualiza las variables void Instructions::update() { if (SDL_GetTicks() - ticks_ > param.game.speed) { // Actualiza el contador de ticks ticks_ = SDL_GetTicks(); // Actualiza el objeto screen Screen::get()->update(); // Incrementa el contador counter_++; // Actualiza los sprites updateSprites(); // Gestiona la textura con los graficos updateBackbuffer(); // Actualiza el mosaico de fondo tiled_bg_->update(); // Actualiza el objeto "fade" fade_->update(); // Rellena el backbuffer fillBackbuffer(); } } // Pinta en pantalla void Instructions::render() { // Prepara para empezar a dibujar en la textura de juego Screen::get()->start(); // Limpia la pantalla Screen::get()->clean(BG_COLOR); // Dibuja el mosacico de fondo tiled_bg_->render(); // Copia la textura y el backbuffer al renderizador if (view_.y == 0) renderLines(renderer_, backbuffer_, lines_); else SDL_RenderTexture(renderer_, backbuffer_, nullptr, &view_); fade_->render(); // Vuelca el contenido del renderizador en pantalla Screen::get()->render(); } // Comprueba los eventos void Instructions::checkEvents() { SDL_Event event; while (SDL_PollEvent(&event)) { GlobalEvents::check(event); } } // Comprueba las entradas void Instructions::checkInput() { GlobalInputs::check(); } // Bucle para la pantalla de instrucciones void Instructions::run() { Audio::get()->playMusic("title.ogg"); while (Section::name == Section::Name::INSTRUCTIONS) { checkInput(); update(); checkEvents(); // Tiene que ir antes del render render(); } } // Método para inicializar las líneas std::vector Instructions::initializeLines(int height) { std::vector lines; for (int y = 0; y < height; y++) { int direction = (y % 2 == 0) ? -1 : 1; // Pares a la izquierda, impares a la derecha lines.emplace_back(y, 0.0f, direction); } return lines; } // Método para mover las líneas con suavizado bool Instructions::moveLines(std::vector &lines, int width, float duration, Uint32 startDelay) { Uint32 current_time = SDL_GetTicks(); bool all_lines_off_screen = true; for (auto &line : lines) { // Establecer startTime en el primer cuadro de animación if (line.startTime == 0) { line.startTime = current_time + line.y * startDelay; } float elapsed_time = (current_time - line.startTime) / 1000.0f; // Convertir a segundos if (elapsed_time < 0) { all_lines_off_screen = false; // Si aún no se debe mover esta línea, no están todas fuera de pantalla continue; } if (elapsed_time >= duration) { continue; // Si la línea ha salido de los límites, no la muevas más } float t = elapsed_time / duration; float smooth_factor = easeInOutQuint(t); line.x = line.direction * smooth_factor * width; all_lines_off_screen = false; // Si alguna línea aún se está moviendo, no están todas fuera de pantalla } return all_lines_off_screen; } // Método para renderizar las líneas void Instructions::renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector &lines) { for (const auto &LINE : lines) { SDL_FRect srcRect = {0, static_cast(LINE.y), 320, 1}; SDL_FRect dstRect = {static_cast(LINE.x), static_cast(LINE.y), 320, 1}; SDL_RenderTexture(renderer, texture, &srcRect, &dstRect); } } // Gestiona la textura con los graficos void Instructions::updateBackbuffer() { // Establece la ventana del backbuffer view_.y = std::max(0.0f, param.game.height - counter_ + 100); // Verifica si view_.y == 0 y gestiona el temporizador if (view_.y == 0) { if (!start_delay_triggered_) { // Activa el temporizador si no ha sido activado start_delay_triggered_ = true; start_delay_time_ = SDL_GetTicks(); } else if (SDL_GetTicks() - start_delay_time_ >= 4000) { // Han pasado tres segundos, mover líneas all_lines_off_screen_ = moveLines(lines_, 320, 1.0f, 5); } } // Comprueba si el contador ha llegado al final if (all_lines_off_screen_) { Section::name = Section::Name::TITLE; Section::options = Section::Options::TITLE_1; } }