#include "game/scenes/ending2.hpp" #include #include // Para max, replace #include "utils/defines.hpp" // Para GAMECANVAS_CENTER_X, GAMECANVAS_CENTER_Y #include "external/jail_audio.h" // Para JA_SetVolume, JA_PlayMusic, JA_StopMusic #include "utils/global_events.hpp" // Para check #include "core/input/global_inputs.hpp" // Para check #include "game/gameplay/options.hpp" // Para Options, options, OptionsGame, Sectio... #include "core/resources/resource.hpp" // Para Resource #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface_animated_sprite.hpp" // Para SAnimatedSprite #include "core/rendering/surface_moving_sprite.hpp" // Para SMovingSprite #include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/text.hpp" // Para Text #include "utils/utils.hpp" // Para PaletteColor, stringToColor // Constructor Ending2::Ending2() : state_(EndingState::PRE_CREDITS, SDL_GetTicks(), STATE_PRE_CREDITS_DURATION_) { options.section.section = Section::ENDING2; options.section.subsection = Subsection::NONE; // Inicializa el vector de colores const std::vector COLORS = {"white", "yellow", "cyan", "green", "magenta", "red", "blue", "black"}; for (const auto& color : COLORS) { colors_.push_back(stringToColor(color)); } // Cambia el color del borde Screen::get()->setBorderColor(static_cast(PaletteColor::BLACK)); // Inicializa la lista de sprites iniSpriteList(); // Carga todos los sprites desde una lista loadSprites(); // Coloca los sprites en su sito placeSprites(); // Crea los sprites con las texturas con los textos createSpriteTexts(); // Crea los sprites con las texturas con los textos del final createTexts(); } // Actualiza el objeto void Ending2::update() { // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego if (SDL_GetTicks() - ticks_ > GAME_SPEED) { // Actualiza el contador de ticks ticks_ = SDL_GetTicks(); // Comprueba las entradas checkInput(); // Actualiza el estado updateState(); switch (state_.state) { case EndingState::CREDITS: // Actualiza los sprites, los textos y los textos del final for (int i = 0; i < 25; ++i) { updateSprites(); updateTextSprites(); updateTexts(); } break; case EndingState::FADING: // Actualiza el fade final y el volumen de la música updateFinalFade(); updateMusicVolume(); break; default: // No hacer nada si el estado no corresponde a un caso manejado break; } // Actualiza el objeto Screen::get()->update(); } } // Dibuja el final en pantalla void Ending2::render() { // Prepara para empezar a dibujar en la surface de juego Screen::get()->start(); // Limpia la pantalla Screen::get()->clearSurface(static_cast(PaletteColor::BLACK)); // Dibuja los sprites renderSprites(); // Dibuja los sprites con el texto renderSpriteTexts(); // Dibuja los sprites con el texto del final renderTexts(); // Dibuja una trama arriba y abajo Uint8 color = static_cast(PaletteColor::BLACK); auto surface = Screen::get()->getRendererSurface(); for (int i = 0; i < 256; i += 2) { surface->putPixel(i + 0, 0, color); surface->putPixel(i + 1, 1, color); surface->putPixel(i + 0, 2, color); surface->putPixel(i + 1, 3, color); surface->putPixel(i, 4, color); surface->putPixel(i, 6, color); surface->putPixel(i + 0, 191, color); surface->putPixel(i + 1, 190, color); surface->putPixel(i + 0, 189, color); surface->putPixel(i + 1, 188, color); surface->putPixel(i, 187, color); surface->putPixel(i, 185, color); } // Vuelca el contenido del renderizador en pantalla Screen::get()->render(); } // Comprueba el manejador de eventos void Ending2::checkEvents() { SDL_Event event; while (SDL_PollEvent(&event)) { globalEvents::check(event); } } // Comprueba las entradas void Ending2::checkInput() { globalInputs::check(); } // Bucle principal void Ending2::run() { JA_PlayMusic(Resource::get()->getMusic("ending2.ogg")); while (options.section.section == Section::ENDING2) { update(); checkEvents(); render(); } JA_StopMusic(); JA_SetVolume(128); } // Actualiza el estado void Ending2::updateState() { switch (state_.state) { case EndingState::PRE_CREDITS: if (state_.hasEnded(EndingState::PRE_CREDITS)) { state_.set(EndingState::CREDITS, 0); } break; case EndingState::CREDITS: if (texts_.back()->getPosY() <= GAMECANVAS_CENTER_Y) { state_.set(EndingState::POST_CREDITS, STATE_POST_CREDITS_DURATION_); } break; case EndingState::POST_CREDITS: if (state_.hasEnded(EndingState::POST_CREDITS)) { state_.set(EndingState::FADING, STATE_FADE_DURATION_); } break; case EndingState::FADING: if (state_.hasEnded(EndingState::FADING)) { options.section.section = Section::LOGO; options.section.subsection = Subsection::LOGO_TO_INTRO; } break; default: break; } } // Inicializa la lista de sprites void Ending2::iniSpriteList() { // Reinicia el vector sprite_list_.clear(); // Añade los valores sprite_list_.push_back("bin"); sprite_list_.push_back("floppy"); sprite_list_.push_back("bird"); sprite_list_.push_back("chip"); sprite_list_.push_back("jeannine"); sprite_list_.push_back("spark"); sprite_list_.push_back("code"); sprite_list_.push_back("paco"); sprite_list_.push_back("elsa"); sprite_list_.push_back("z80"); sprite_list_.push_back("bell"); sprite_list_.push_back("dong"); sprite_list_.push_back("amstrad_cs"); sprite_list_.push_back("breakout"); sprite_list_.push_back("flying_arounder"); sprite_list_.push_back("stopped_arounder"); sprite_list_.push_back("walking_arounder"); sprite_list_.push_back("arounders_door"); sprite_list_.push_back("arounders_machine"); sprite_list_.push_back("abad"); sprite_list_.push_back("abad_bell"); sprite_list_.push_back("lord_abad"); sprite_list_.push_back("bat"); sprite_list_.push_back("batman_bell"); sprite_list_.push_back("batman_fire"); sprite_list_.push_back("batman"); sprite_list_.push_back("demon"); sprite_list_.push_back("heavy"); sprite_list_.push_back("dimallas"); sprite_list_.push_back("guitar"); sprite_list_.push_back("jailbattle_alien"); sprite_list_.push_back("jailbattle_human"); sprite_list_.push_back("jailer_#1"); sprite_list_.push_back("jailer_#2"); sprite_list_.push_back("jailer_#3"); sprite_list_.push_back("bry"); sprite_list_.push_back("upv_student"); sprite_list_.push_back("lamp"); sprite_list_.push_back("robot"); sprite_list_.push_back("congo"); sprite_list_.push_back("crosshair"); sprite_list_.push_back("tree_thing"); sprite_list_.push_back("matatunos"); sprite_list_.push_back("tuno"); sprite_list_.push_back("mummy"); sprite_list_.push_back("sam"); sprite_list_.push_back("qvoid"); sprite_list_.push_back("sigmasua"); sprite_list_.push_back("tv_panel"); sprite_list_.push_back("tv"); sprite_list_.push_back("spider"); sprite_list_.push_back("shock"); sprite_list_.push_back("wave"); sprite_list_.push_back("player"); } // Carga todos los sprites desde una lista void Ending2::loadSprites() { // Inicializa variables sprite_max_width_ = 0; sprite_max_height_ = 0; // Carga los sprites for (const auto& file : sprite_list_) { sprites_.emplace_back(std::make_shared(Resource::get()->getSurface(file + ".gif"), Resource::get()->getAnimations(file + ".ani"))); sprite_max_width_ = std::max(sprites_.back()->getWidth(), sprite_max_width_); sprite_max_height_ = std::max(sprites_.back()->getHeight(), sprite_max_height_); } } // Actualiza los sprites void Ending2::updateSprites() { for (auto sprite : sprites_) { sprite->update(); } } // Actualiza los sprites de texto void Ending2::updateTextSprites() { for (auto sprite : sprite_texts_) { sprite->update(); } } // Actualiza los sprites de texto del final void Ending2::updateTexts() { for (auto sprite : texts_) { sprite->update(); } } // Dibuja los sprites void Ending2::renderSprites() { const Uint8 colorA = static_cast(PaletteColor::RED); for (auto sprite : sprites_) { const bool A = sprite->getRect().y + sprite->getRect().h > 0; const bool B = sprite->getRect().y < options.game.height; if (A && B) { sprite->render(1, colorA); } } // Pinta el ultimo elemento de otro color const Uint8 colorB = static_cast(PaletteColor::WHITE); sprites_.back()->render(1, colorB); } // Dibuja los sprites con el texto void Ending2::renderSpriteTexts() { const Uint8 color = static_cast(PaletteColor::WHITE); for (auto sprite : sprite_texts_) { const bool A = sprite->getRect().y + sprite->getRect().h > 0; const bool B = sprite->getRect().y < options.game.height; if (A && B) { sprite->render(1, color); } } } // Dibuja los sprites con el texto del final void Ending2::renderTexts() { for (auto sprite : texts_) { const bool A = sprite->getRect().y + sprite->getRect().h > 0; const bool B = sprite->getRect().y < options.game.height; if (A && B) { sprite->render(); } } } // Coloca los sprites en su sito void Ending2::placeSprites() { for (int i = 0; i < static_cast(sprites_.size()); ++i) { const float X = i % 2 == 0 ? FIRST_COL_ : SECOND_COL_; const float Y = (i / 1) * (sprite_max_height_ + DIST_SPRITE_TEXT_ + Resource::get()->getText("smb2")->getCharacterSize() + DIST_SPRITE_SPRITE_) + options.game.height + 40; const float W = sprites_.at(i)->getWidth(); const float H = sprites_.at(i)->getHeight(); const float DX = -(W / 2); const float DY = sprite_max_height_ - H; sprites_.at(i)->setPos({X + DX, Y + DY, W, H}); sprites_.at(i)->setVelY(SPRITE_DESP_SPEED_); } // Recoloca el sprite del jugador, que es el último de la lista const float X = (options.game.width - sprites_.back()->getWidth()) / 2; const float Y = sprites_.back()->getPosY() + sprite_max_height_ * 2; sprites_.back()->setPos(X, Y); sprites_.back()->setCurrentAnimation("walk"); } // Crea los sprites con las texturas con los textos void Ending2::createSpriteTexts() { // Crea los sprites de texto a partir de la lista for (size_t i = 0; i < sprite_list_.size(); ++i) { auto text = Resource::get()->getText("smb2"); // Procesa y ajusta el texto del sprite actual std::string txt = sprite_list_[i]; std::replace(txt.begin(), txt.end(), '_', ' '); // Reemplaza '_' por ' ' if (txt == "player") { txt = "JAILDOCTOR"; // Reemplaza "player" por "JAILDOCTOR" } // Calcula las dimensiones del texto const float W = text->lenght(txt, 1); const float H = text->getCharacterSize(); // Determina la columna y la posición X del texto const float X = (i == sprite_list_.size() - 1) ? (GAMECANVAS_CENTER_X - (W / 2)) : ((i % 2 == 0 ? FIRST_COL_ : SECOND_COL_) - (W / 2)); // Calcula la posición Y del texto en base a la posición y altura del sprite const float Y = sprites_.at(i)->getPosY() + sprites_.at(i)->getHeight() + DIST_SPRITE_TEXT_; // Crea la surface auto surface = std::make_shared(W, H); auto previuos_renderer = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(surface); text->write(0, 0, txt); // Crea el sprite SDL_FRect pos = {X, Y, W, H}; sprite_texts_.emplace_back(std::make_shared(surface, pos)); sprite_texts_.back()->setVelY(SPRITE_DESP_SPEED_); Screen::get()->setRendererSurface(previuos_renderer); } } // Crea los sprites con las texturas con los textos del final void Ending2::createTexts() { // Crea los primeros textos std::vector list; list.push_back("STARRING"); auto text = Resource::get()->getText("smb2"); // Crea los sprites de texto a partir de la lista for (size_t i = 0; i < list.size(); ++i) { // Calcula constantes const float W = text->lenght(list[i], 1); const float H = text->getCharacterSize(); const float X = GAMECANVAS_CENTER_X; const float DX = -(W / 2); const float Y = options.game.height + (text->getCharacterSize() * (i * 2)); // Crea la surface auto surface = std::make_shared(W, H); auto previuos_renderer = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(surface); text->write(0, 0, list[i]); // Crea el sprite SDL_FRect pos = {X + DX, Y, W, H}; texts_.emplace_back(std::make_shared(surface, pos)); texts_.back()->setVelY(SPRITE_DESP_SPEED_); Screen::get()->setRendererSurface(previuos_renderer); } // Crea los últimos textos // El primer texto va a continuación del ultimo spriteText const int START = sprite_texts_.back()->getPosY() + text->getCharacterSize() * 15; list.clear(); list.push_back("THANK YOU"); list.push_back("FOR PLAYING!"); // Crea los sprites de texto a partir de la lista for (size_t i = 0; i < list.size(); ++i) { // Calcula constantes const float W = text->lenght(list[i], 1); const float H = text->getCharacterSize(); const float X = GAMECANVAS_CENTER_X; const float DX = -(W / 2); const float Y = START + (text->getCharacterSize() * (i * 2)); // Crea la surface auto surface = std::make_shared(W, H); auto previuos_renderer = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(surface); text->write(0, 0, list[i]); // Crea el sprite SDL_FRect pos = {X + DX, Y, W, H}; texts_.emplace_back(std::make_shared(surface, pos)); texts_.back()->setVelY(SPRITE_DESP_SPEED_); Screen::get()->setRendererSurface(previuos_renderer); } } // Actualiza el fade final void Ending2::updateFinalFade() { for (auto sprite : texts_) { sprite->getSurface()->fadeSubPalette(0); } } // Actualiza el volumen de la musica void Ending2::updateMusicVolume() { // Constante para la duración en milisegundos constexpr Uint32 VOLUME_FADE_DURATION = 3000; // Tiempo actual const Uint32 CURRENT_TICKS = SDL_GetTicks(); // Calcular el tiempo transcurrido desde init_ticks Uint32 elapsed_ticks = CURRENT_TICKS - state_.init_ticks; // Limitar el tiempo máximo a la duración definida elapsed_ticks = std::min(elapsed_ticks, VOLUME_FADE_DURATION); // Calcular el step basado en la duración const float STEP = (static_cast(VOLUME_FADE_DURATION) - elapsed_ticks) / VOLUME_FADE_DURATION; // Calcular el volumen en función del step const int VOLUME = static_cast(128 * STEP); // Actualizar el volumen JA_SetVolume(VOLUME); }