#include "instructions.h" #include // Para SDL_GetTicks, SDL_SetRenderTarget, SDL_Re... #include // Para max #include // Para array #include // Para basic_string, string #include // Para move #include // Para vector #include "audio.h" // Para Audio #include "color.h" // Para Color, SHADOW_TEXT_COLOR, Zone, NO_TEXT_C... #include "fade.h" // Para Fade, FadeMode, FadeType #include "global_events.h" // Para check #include "global_inputs.h" // Para check #include "input.h" // Para Input #include "lang.h" // Para getText #include "param.h" // Para Param, param, ParamGame, ParamFade, Param... #include "resource.h" // Para Resource #include "screen.h" // Para Screen #include "section.hpp" // Para Name, name, Options, options #include "sprite.h" // Para Sprite #include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR, TEXT_SHADOW #include "tiled_bg.h" // Para TiledBG, TiledBGMode #include "utils.h" // 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 tiled_bg_->setColor(param.title.bg_color); 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; auto caption_style = TextStyle(ORANGE_TEXT_COLOR, SHADOW_TEXT_COLOR); auto text_style = TextStyle(NO_TEXT_COLOR, SHADOW_TEXT_COLOR); // 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"), caption_style); 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"), text_style); text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, ANCHOR1 + SPACE_BETWEEN_LINES * 1, Lang::getText("[INSTRUCTIONS] 03"), text_style); 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"), text_style); 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"), text_style); // 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"), caption_style); 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(); // 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() { Input::get()->update(); 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 auto Instructions::initializeLines(int height) -> std::vector { 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 auto Instructions::moveLines(std::vector &lines, int width, float duration, Uint32 start_delay) -> bool { Uint32 current_time = SDL_GetTicks(); bool all_lines_off_screen = true; for (auto &line : lines) { // Establecer start_time en el primer cuadro de animación if (line.start_time == 0) { line.start_time = current_time + line.y * start_delay; } float elapsed_time = (current_time - line.start_time) / 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 src_rect = {0, static_cast(line.y), 320, 1}; SDL_FRect dst_rect = {static_cast(line.x), static_cast(line.y), 320, 1}; SDL_RenderTexture(renderer, texture, &src_rect, &dst_rect); } } // 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; } }