#include "intro.h" #include // Para SDL_GetTicks, SDL_SetRenderDrawColor, SDL_FRect, SDL_RenderFillRect, SDL_GetRenderTarget, SDL_RenderClear, SDL_RenderRect, SDL_SetRenderTarget, SDL_BLENDMODE_BLEND, SDL_PixelFormat, SDL_PollEvent, SDL_RenderTexture, SDL_TextureAccess, SDLK_A, SDLK_C, SDLK_D, SDLK_F, SDLK_S, SDLK_V, SDLK_X, SDLK_Z, SDL_Event, SDL_EventType, Uint32 #include // Para max #include // Para array #include // Para function #include // Para basic_ostream, basic_ostream::operator<<, operator<<, cout, endl, hex #include // Para char_traits, basic_string, string #include // Para move #include "audio.h" // Para Audio #include "color.h" // Para Color, Zone, easeInOutExpo, easeInElastic, easeOutBounce, easeOutElastic, easeOutQuad, easeOutQuint #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, ParamIntro, ParamTitle #include "path_sprite.h" // Para PathSprite, PathType #include "resource.h" // Para Resource #include "screen.h" // Para Screen #include "section.hpp" // Para Name, name, Options, options #include "text.h" // Para Text #include "texture.h" // Para Texture #include "tiled_bg.h" // Para TiledBG, TiledBGMode #include "utils.h" #include "writer.h" // Para Writer #ifdef DEBUG #include // Para operator<<, setfill, setw #endif // Constructor Intro::Intro() : tiled_bg_(std::make_unique(param.game.game_area.rect, TiledBGMode::DIAGONAL)) { // Inicializa variables Section::name = Section::Name::INTRO; Section::options = Section::Options::NONE; // Inicializa las imagens initSprites(); // Inicializa los textos initTexts(); // Configura el fondo tiled_bg_->setSpeed(0.3F); tiled_bg_->setColor(bg_color_); } // Comprueba los eventos void Intro::checkEvents() { SDL_Event event; while (SDL_PollEvent(&event)) { #ifdef DEBUG if (event.type == SDL_EVENT_KEY_DOWN && static_cast(event.key.repeat) == 1) { static Color color_ = param.intro.bg_color; handleDebugColorKeys(event.key.key, color_); tiled_bg_->setColor(color_); printColorDebugInfo(color_); } #endif GlobalEvents::check(event); } } // Comprueba las entradas void Intro::checkInput() { Input::get()->update(); GlobalInputs::check(); } // Actualiza las escenas de la intro void Intro::updateScenes() { switch (scene_) { case 0: updateScene0(); break; case 1: updateScene1(); break; case 2: updateScene2(); break; case 3: updateScene3(); break; case 4: updateScene4(); break; case 5: updateScene5(); break; default: break; } } void Intro::updateScene0() { // Primera imagen - UPV enableCardAndShadow(0); // Primer texto de la primera imagen if (card_sprites_.at(0)->hasFinished() && !texts_.at(0)->hasFinished()) { texts_.at(0)->setEnabled(true); } // Segundo texto de la primera imagen if (texts_.at(0)->hasFinished() && !texts_.at(1)->hasFinished()) { switchText(0, 1); } // Tercer texto de la primera imagen if (texts_.at(1)->hasFinished() && !texts_.at(2)->hasFinished()) { switchText(1, 2); } // Fin de la primera escena if (texts_.at(2)->hasFinished()) { texts_.at(2)->setEnabled(false); scene_++; } } void Intro::updateScene1() { // Segunda imagen - Máquina enableCardAndShadow(1); // Primer texto de la segunda imagen if (card_sprites_.at(1)->hasFinished() && !texts_.at(3)->hasFinished()) { texts_.at(3)->setEnabled(true); } // Fin de la segunda escena if (texts_.at(3)->hasFinished()) { texts_.at(3)->setEnabled(false); scene_++; } } void Intro::updateScene2() { // Tercera imagen junto con primer texto - GRITO if (!texts_.at(4)->hasFinished()) { enableCardAndShadow(2); texts_.at(4)->setEnabled(true); } // Fin de la tercera escena if (card_sprites_.at(2)->hasFinished() && texts_.at(4)->hasFinished()) { texts_.at(4)->setEnabled(false); scene_++; } } void Intro::updateScene3() { // Cuarta imagen junto con primer texto - Reflexión enableCardAndShadow(3); if (!texts_.at(5)->hasFinished()) { texts_.at(5)->setEnabled(true); } // Segundo texto de la cuarta imagen if (texts_.at(5)->hasFinished() && !texts_.at(6)->hasFinished()) { switchText(5, 6); } // Fin de la cuarta escena if (card_sprites_.at(3)->hasFinished() && texts_.at(6)->hasFinished()) { texts_.at(6)->setEnabled(false); scene_++; } } void Intro::updateScene4() { // Quinta imagen - Patada enableCardAndShadow(4); // Primer texto de la quinta imagen if (!texts_.at(7)->hasFinished()) { texts_.at(7)->setEnabled(true); } // Fin de la quinta escena if (card_sprites_.at(4)->hasFinished() && texts_.at(7)->hasFinished()) { texts_.at(7)->setEnabled(false); scene_++; } } void Intro::updateScene5() { // Sexta imagen junto con texto - Globos de café enableCardAndShadow(5); if (!texts_.at(8)->hasFinished()) { texts_.at(8)->setEnabled(true); } // Acaba el último texto if (texts_.at(8)->hasFinished()) { texts_.at(8)->setEnabled(false); } // Acaba la ultima imagen if (card_sprites_.at(5)->hasFinished() && texts_.at(8)->hasFinished()) { state_ = IntroState::POST; state_start_time_ = SDL_GetTicks(); } } // Helper methods to reduce code duplication void Intro::enableCardAndShadow(int index) { card_sprites_.at(index)->enable(); shadow_sprites_.at(index)->enable(); } void Intro::switchText(int fromIndex, int toIndex) { texts_.at(fromIndex)->setEnabled(false); texts_.at(toIndex)->setEnabled(true); } // Actualiza las variables del objeto void Intro::update() { if (SDL_GetTicks() - ticks_ > param.game.speed) { // Actualiza el contador de ticks ticks_ = SDL_GetTicks(); // Actualiza el fondo tiled_bg_->update(); switch (state_) { case IntroState::SCENES: updateSprites(); updateTexts(); updateScenes(); break; case IntroState::POST: updatePostState(); break; } // Actualiza el objeto screen Screen::get()->update(); } } // Dibuja el objeto en pantalla void Intro::render() { // Prepara para empezar a dibujar en la textura de juego Screen::get()->start(); // Limpia la pantalla Screen::get()->clean(); // Dibuja el fondo tiled_bg_->render(); switch (state_) { case IntroState::SCENES: { renderTextRect(); renderSprites(); renderTexts(); break; } case IntroState::POST: break; } // Vuelca el contenido del renderizador en pantalla Screen::get()->render(); } // Bucle principal void Intro::run() { Audio::get()->playMusic("intro.ogg", 0); while (Section::name == Section::Name::INTRO) { checkInput(); update(); checkEvents(); // Tiene que ir antes del render render(); } } // Inicializa las imagens void Intro::initSprites() { // Listado de imagenes a usar const std::array TEXTURE_LIST = { "intro1.png", "intro2.png", "intro3.png", "intro4.png", "intro5.png", "intro6.png"}; // Constantes constexpr int TOTAL_SPRITES = TEXTURE_LIST.size(); const float BORDER = 2.0F; auto texture = Resource::get()->getTexture(TEXTURE_LIST.front()); const float CARD_WIDTH = texture->getWidth() + (BORDER * 2); const float CARD_HEIGHT = texture->getHeight() + (BORDER * 2); // Crea las texturas para las tarjetas std::vector> card_textures; for (int i = 0; i < TOTAL_SPRITES; ++i) { // Crea la textura auto card_texture = std::make_unique(Screen::get()->getRenderer()); card_texture->createBlank(CARD_WIDTH, CARD_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET); card_texture->setBlendMode(SDL_BLENDMODE_BLEND); // Apuntamos el renderizador a la textura auto *temp = SDL_GetRenderTarget(Screen::get()->getRenderer()); card_texture->setAsRenderTarget(Screen::get()->getRenderer()); // Limpia la textura SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 0); SDL_RenderClear(Screen::get()->getRenderer()); // Pone color en el marco de la textura auto color = param.intro.card_color; SDL_SetRenderDrawColor(Screen::get()->getRenderer(), color.r, color.g, color.b, color.a); SDL_FRect rect1 = {1, 0, CARD_WIDTH - 2, CARD_HEIGHT}; SDL_FRect rect2 = {0, 1, CARD_WIDTH, CARD_HEIGHT - 2}; SDL_RenderRect(Screen::get()->getRenderer(), &rect1); SDL_RenderRect(Screen::get()->getRenderer(), &rect2); // Copia la textura con la imagen dentro del marco SDL_FRect dest = {BORDER, BORDER, CARD_WIDTH - (BORDER * 2), CARD_HEIGHT - (BORDER * 2)}; SDL_RenderTexture(Screen::get()->getRenderer(), Resource::get()->getTexture(TEXTURE_LIST.at(i))->getSDLTexture(), nullptr, &dest); // Deja el renderizador como estaba y añade la textura a la lista SDL_SetRenderTarget(Screen::get()->getRenderer(), temp); card_textures.push_back(std::move(card_texture)); } // Inicializa los sprites para las tarjetas for (int i = 0; i < TOTAL_SPRITES; ++i) { auto sprite = std::make_unique(card_textures.at(i)); sprite->setWidth(CARD_WIDTH); sprite->setHeight(CARD_HEIGHT); sprite->setSpriteClip(0, 0, CARD_WIDTH, CARD_HEIGHT); card_sprites_.push_back(std::move(sprite)); } const float X_DEST = param.game.game_area.center_x - CARD_WIDTH / 2; const float Y_DEST = param.game.game_area.first_quarter_y - (CARD_HEIGHT / 4); card_sprites_.at(0)->addPath(-CARD_WIDTH - 10, X_DEST, PathType::HORIZONTAL, Y_DEST, 100, easeInOutExpo, 0); card_sprites_.at(1)->addPath(param.game.width, X_DEST, PathType::HORIZONTAL, Y_DEST, 100, easeOutBounce, 0); card_sprites_.at(2)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 40, easeOutQuint, 0); card_sprites_.at(3)->addPath(param.game.height, Y_DEST, PathType::VERTICAL, X_DEST, 300, easeInOutExpo, 0); card_sprites_.at(4)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 70, easeOutElastic, 0); card_sprites_.at(5)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 250, easeOutQuad, 450); card_sprites_.at(5)->addPath(X_DEST, -CARD_WIDTH, PathType::HORIZONTAL, Y_DEST, 80, easeInElastic, 0); // Constantes const float DESP = 8; const float SHADOW_SPRITE_WIDTH = CARD_WIDTH; const float SHADOW_SPRITE_HEIGHT = CARD_HEIGHT; // Crea la textura para las sombras de las tarjetas auto shadow_texture = std::make_shared(Screen::get()->getRenderer()); shadow_texture->createBlank(SHADOW_SPRITE_WIDTH, SHADOW_SPRITE_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET); shadow_texture->setBlendMode(SDL_BLENDMODE_BLEND); // Apuntamos el renderizador a la textura auto *temp = SDL_GetRenderTarget(Screen::get()->getRenderer()); shadow_texture->setAsRenderTarget(Screen::get()->getRenderer()); // Limpia la textura SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 0); SDL_RenderClear(Screen::get()->getRenderer()); // Dibuja la sombra sobre la textura auto shadow_color = param.intro.shadow_color; SDL_SetRenderDrawColor(Screen::get()->getRenderer(), shadow_color.r, shadow_color.g, shadow_color.b, Color::MAX_ALPHA_VALUE); SDL_FRect rect1 = {1, 0, SHADOW_SPRITE_WIDTH - 2, SHADOW_SPRITE_HEIGHT}; SDL_FRect rect2 = {0, 1, SHADOW_SPRITE_WIDTH, SHADOW_SPRITE_HEIGHT - 2}; SDL_RenderFillRect(Screen::get()->getRenderer(), &rect1); SDL_RenderFillRect(Screen::get()->getRenderer(), &rect2); // Deja el renderizador como estaba y añade la textura a la lista SDL_SetRenderTarget(Screen::get()->getRenderer(), temp); // Inicializa los sprites para la sombras usando la texturas con la sombra for (int i = 0; i < TOTAL_SPRITES; ++i) { auto shadow_color = param.intro.shadow_color; auto sprite = std::make_unique(shadow_texture); sprite->setWidth(SHADOW_SPRITE_WIDTH); sprite->setHeight(SHADOW_SPRITE_HEIGHT); sprite->setSpriteClip(0, 0, SHADOW_SPRITE_WIDTH, SHADOW_SPRITE_HEIGHT); sprite->getTexture()->setAlpha(shadow_color.a); shadow_sprites_.push_back(std::move(sprite)); } const float S_X_DEST = X_DEST + DESP; const float S_Y_DEST = Y_DEST + DESP; shadow_sprites_.at(0)->addPath(param.game.height + 10, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 100, easeInOutExpo, 0); shadow_sprites_.at(1)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 100, easeOutBounce, 0); shadow_sprites_.at(2)->addPath(-SHADOW_SPRITE_WIDTH, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, 40, easeOutQuint, 0); shadow_sprites_.at(3)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 300, easeInOutExpo, 0); shadow_sprites_.at(4)->addPath(param.game.height, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 70, easeOutElastic, 0); shadow_sprites_.at(5)->addPath(param.game.width, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, 250, easeOutQuad, 450); shadow_sprites_.at(5)->addPath(S_X_DEST, param.game.width, PathType::HORIZONTAL, S_Y_DEST, 80, easeInElastic, 0); } // Inicializa los textos void Intro::initTexts() { constexpr int TOTAL_TEXTS = 9; for (int i = 0; i < TOTAL_TEXTS; ++i) { auto writer = std::make_unique(Resource::get()->getText("04b_25_metal")); writer->setPosX(0); writer->setPosY(param.game.height - param.intro.text_distance_from_bottom); writer->setKerning(-2); writer->setEnabled(false); writer->setFinishedCounter(180); texts_.push_back(std::move(writer)); } // Un dia qualsevol de l'any 2000 texts_.at(0)->setCaption(Lang::getText("[INTRO] 1")); texts_.at(0)->setSpeed(8); // Tot esta tranquil a la UPV texts_.at(1)->setCaption(Lang::getText("[INTRO] 2")); texts_.at(1)->setSpeed(8); // Fins que un desaprensiu... texts_.at(2)->setCaption(Lang::getText("[INTRO] 3")); texts_.at(2)->setSpeed(12); // HEY! ME ANE A FERME UN CORTAET... texts_.at(3)->setCaption(Lang::getText("[INTRO] 4")); texts_.at(3)->setSpeed(8); // UAAAAAAAAAAAAA!!! texts_.at(4)->setCaption(Lang::getText("[INTRO] 5")); texts_.at(4)->setSpeed(1); // Espera un moment... texts_.at(5)->setCaption(Lang::getText("[INTRO] 6")); texts_.at(5)->setSpeed(16); // Si resulta que no tinc solt! texts_.at(6)->setCaption(Lang::getText("[INTRO] 7")); texts_.at(6)->setSpeed(2); // MERDA DE MAQUINA! texts_.at(7)->setCaption(Lang::getText("[INTRO] 8")); texts_.at(7)->setSpeed(3); // Blop... blop... blop... texts_.at(8)->setCaption(Lang::getText("[INTRO] 9")); texts_.at(8)->setSpeed(20); for (auto &text : texts_) { text->center(param.game.game_area.center_x); } } // Actualiza los sprites void Intro::updateSprites() { for (auto &sprite : card_sprites_) { sprite->update(); } for (auto &sprite : shadow_sprites_) { sprite->update(); } } // Actualiza los textos void Intro::updateTexts() { for (auto &text : texts_) { text->update(); } } // Dibuja los sprites void Intro::renderSprites() { shadow_sprites_.at(scene_)->render(); card_sprites_.at(scene_)->render(); } // Dibuja los textos void Intro::renderTexts() { for (const auto &text : texts_) { text->render(); } } // Actualiza el estado POST void Intro::updatePostState() { const Uint32 ELAPSED_TIME = SDL_GetTicks() - state_start_time_; switch (post_state_) { case IntroPostState::STOP_BG: // EVENTO: Detiene el fondo después de 1 segundo if (ELAPSED_TIME >= 1000) { tiled_bg_->stopGracefully(); if (!bg_color_.IS_EQUAL_TO(param.title.bg_color)) { bg_color_ = bg_color_.APPROACH_TO(param.title.bg_color, 1); } tiled_bg_->setColor(bg_color_); } // Cambia de estado si el fondo se ha detenido y recuperado el color if (tiled_bg_->isStopped() && bg_color_.IS_EQUAL_TO(param.title.bg_color)) { post_state_ = IntroPostState::END; state_start_time_ = SDL_GetTicks(); } break; case IntroPostState::END: // Finaliza la intro después de 1 segundo if (ELAPSED_TIME >= 1000) { Audio::get()->stopMusic(); Section::name = Section::Name::TITLE; Section::options = Section::Options::TITLE_1; } break; default: break; } } void Intro::renderTextRect() { static const float HEIGHT = Resource::get()->getText("04b_25_metal")->getCharacterSize(); static SDL_FRect rect_ = {0.0F, param.game.height - param.intro.text_distance_from_bottom - HEIGHT, param.game.width, HEIGHT * 3}; SDL_SetRenderDrawColor(Screen::get()->getRenderer(), param.intro.shadow_color.r, param.intro.shadow_color.g, param.intro.shadow_color.b, param.intro.shadow_color.a); SDL_RenderFillRect(Screen::get()->getRenderer(), &rect_); } #ifdef DEBUG // Helper functions for color adjustment void Intro::adjustColorComponent(uint8_t &component, bool increase) { if (increase && component < 255) { ++component; } else if (!increase && component > 0) { --component; } } void Intro::adjustAllColorComponents(Color &color, bool increase) { adjustColorComponent(color.r, increase); adjustColorComponent(color.g, increase); adjustColorComponent(color.b, increase); } void Intro::handleDebugColorKeys(SDL_Keycode key, Color &color) { switch (key) { case SDLK_A: adjustColorComponent(color.r, true); break; case SDLK_Z: adjustColorComponent(color.r, false); break; case SDLK_S: adjustColorComponent(color.g, true); break; case SDLK_X: adjustColorComponent(color.g, false); break; case SDLK_D: adjustColorComponent(color.b, true); break; case SDLK_C: adjustColorComponent(color.b, false); break; case SDLK_F: adjustAllColorComponents(color, true); break; case SDLK_V: adjustAllColorComponents(color, false); break; default: break; } } void Intro::printColorDebugInfo(const Color &color) { std::cout << "#" << std::hex << std::setw(2) << std::setfill('0') << (int)color.r << std::setw(2) << std::setfill('0') << (int)color.g << std::setw(2) << std::setfill('0') << (int)color.b << std::endl; } #endif