#include "game/scenes/ending2.hpp" #include #include // Para max, replace #include "core/audio/audio.hpp" // Para Audio #include "core/input/global_inputs.hpp" // Para check #include "core/input/input.hpp" // Para Input #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/surface_animated_sprite.hpp" // Para SAnimatedSprite #include "core/rendering/surface_moving_sprite.hpp" // Para SMovingSprite #include "core/rendering/text.hpp" // Para Text #include "core/resources/resource_cache.hpp" // Para Resource #include "core/system/global_events.hpp" // Para check #include "game/options.hpp" // Para Options, options, OptionsGame, Sectio... #include "game/scene_manager.hpp" // Para SceneManager #include "utils/defines.hpp" // Para GAMECANVAS_CENTER_X, GAMECANVAS_CENTER_Y #include "utils/delta_timer.hpp" // Para DeltaTimer #include "utils/utils.hpp" // Para PaletteColor, stringToColor // Constructor Ending2::Ending2() : delta_timer_(std::make_unique()), state_{EndingState::PRE_CREDITS, STATE_PRE_CREDITS_DURATION} { // Establece la escena SceneManager::current = SceneManager::Scene::ENDING2; SceneManager::options = SceneManager::Options::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)); } Screen::get()->setBorderColor(static_cast(PaletteColor::BLACK)); // Cambia el color del borde iniSpriteList(); // Inicializa la lista de sprites loadSprites(); // Carga todos los sprites desde una lista placeSprites(); // Coloca los sprites en su sito createSpriteTexts(); // Crea los sprites con las texturas con los textos createTexts(); // Crea los sprites con las texturas con los textos del final } // Actualiza el objeto void Ending2::update() { const float DELTA_TIME = delta_timer_->tick(); handleEvents(); // Comprueba los eventos handleInput(); // Comprueba las entradas updateState(DELTA_TIME); // Actualiza el estado switch (state_.state) { case EndingState::CREDITS: // Actualiza los sprites, los textos y los textos del final updateSprites(DELTA_TIME); updateTextSprites(DELTA_TIME); updateTexts(DELTA_TIME); break; case EndingState::FADING: // Actualiza el fade final updateFinalFade(); break; default: // No hacer nada si el estado no corresponde a un caso manejado break; } Audio::update(); // Actualiza el objeto Audio Screen::get()->update(DELTA_TIME); // Actualiza el objeto Screen } // 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 auto 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::handleEvents() { SDL_Event event; while (SDL_PollEvent(&event)) { GlobalEvents::handle(event); } } // Comprueba las entradas void Ending2::handleInput() { Input::get()->update(); GlobalInputs::handle(); } // Bucle principal void Ending2::run() { Audio::get()->playMusic("ending2.ogg"); while (SceneManager::current == SceneManager::Scene::ENDING2) { update(); render(); } Audio::get()->stopMusic(); } // Actualiza el estado void Ending2::updateState(float delta_time) { state_time_ += delta_time; switch (state_.state) { case EndingState::PRE_CREDITS: if (state_time_ >= STATE_PRE_CREDITS_DURATION) { transitionToState(EndingState::CREDITS); } break; case EndingState::CREDITS: if (texts_.back()->getPosY() <= GAMECANVAS_CENTER_Y) { transitionToState(EndingState::POST_CREDITS); } break; case EndingState::POST_CREDITS: if (state_time_ >= STATE_POST_CREDITS_DURATION) { transitionToState(EndingState::FADING); } break; case EndingState::FADING: if (state_time_ >= STATE_FADE_DURATION) { SceneManager::current = SceneManager::Scene::LOGO; SceneManager::options = SceneManager::Options::LOGO_TO_LOADING_SCREEN; } break; default: break; } } // Transición entre estados void Ending2::transitionToState(EndingState new_state) { state_.state = new_state; state_time_ = 0.0F; // Actualizar duración según el nuevo estado switch (new_state) { case EndingState::PRE_CREDITS: state_.duration = STATE_PRE_CREDITS_DURATION; break; case EndingState::POST_CREDITS: state_.duration = STATE_POST_CREDITS_DURATION; break; case EndingState::FADING: state_.duration = STATE_FADE_DURATION; // Al entrar en FADING, iniciar fade de audio Audio::get()->fadeOutMusic(MUSIC_FADE_DURATION); break; case EndingState::CREDITS: state_.duration = 0.0F; // CREDITS no tiene duración fija, termina cuando el último texto llega al centro break; default: break; } } // Inicializa la lista de sprites void Ending2::iniSpriteList() { // Reinicia el vector sprite_list_.clear(); // Añade los valores sprite_list_.emplace_back("bin"); sprite_list_.emplace_back("floppy"); sprite_list_.emplace_back("bird"); sprite_list_.emplace_back("chip"); sprite_list_.emplace_back("jeannine"); sprite_list_.emplace_back("spark"); sprite_list_.emplace_back("code"); sprite_list_.emplace_back("paco"); sprite_list_.emplace_back("elsa"); sprite_list_.emplace_back("z80"); sprite_list_.emplace_back("bell"); sprite_list_.emplace_back("dong"); sprite_list_.emplace_back("amstrad_cs"); sprite_list_.emplace_back("breakout"); sprite_list_.emplace_back("flying_arounder"); sprite_list_.emplace_back("stopped_arounder"); sprite_list_.emplace_back("walking_arounder"); sprite_list_.emplace_back("arounders_door"); sprite_list_.emplace_back("arounders_machine"); sprite_list_.emplace_back("abad"); sprite_list_.emplace_back("abad_bell"); sprite_list_.emplace_back("lord_abad"); sprite_list_.emplace_back("bat"); sprite_list_.emplace_back("batman_bell"); sprite_list_.emplace_back("batman_fire"); sprite_list_.emplace_back("batman"); sprite_list_.emplace_back("demon"); sprite_list_.emplace_back("heavy"); sprite_list_.emplace_back("dimallas"); sprite_list_.emplace_back("guitar"); sprite_list_.emplace_back("jailbattle_alien"); sprite_list_.emplace_back("jailbattle_human"); sprite_list_.emplace_back("jailer_#1"); sprite_list_.emplace_back("jailer_#2"); sprite_list_.emplace_back("jailer_#3"); sprite_list_.emplace_back("bry"); sprite_list_.emplace_back("upv_student"); sprite_list_.emplace_back("lamp"); sprite_list_.emplace_back("robot"); sprite_list_.emplace_back("congo"); sprite_list_.emplace_back("crosshair"); sprite_list_.emplace_back("tree_thing"); sprite_list_.emplace_back("matatunos"); sprite_list_.emplace_back("tuno"); sprite_list_.emplace_back("mummy"); sprite_list_.emplace_back("sam"); sprite_list_.emplace_back("qvoid"); sprite_list_.emplace_back("sigmasua"); sprite_list_.emplace_back("tv_panel"); sprite_list_.emplace_back("tv"); sprite_list_.emplace_back("spider"); sprite_list_.emplace_back("shock"); sprite_list_.emplace_back("wave"); sprite_list_.emplace_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::Cache::get()->getAnimations(file + ".yaml"))); 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(float delta) { for (const auto& sprite : sprites_) { sprite->update(delta); } } // Actualiza los sprites de texto void Ending2::updateTextSprites(float delta) { for (const auto& sprite : sprite_texts_) { sprite->update(delta); } } // Actualiza los sprites de texto del final void Ending2::updateTexts(float delta) { for (const auto& sprite : texts_) { sprite->update(delta); } } // Dibuja los sprites void Ending2::renderSprites() { const auto COLOR_A = static_cast(PaletteColor::RED); for (const 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, COLOR_A); } } // Pinta el ultimo elemento de otro color const auto COLOR_B = static_cast(PaletteColor::WHITE); sprites_.back()->render(1, COLOR_B); } // Dibuja los sprites con el texto void Ending2::renderSpriteTexts() { const auto COLOR = static_cast(PaletteColor::WHITE); for (const 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 (const 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::Cache::get()->getText("smb2")->getCharacterSize() + DIST_SPRITE_SPRITE)) + Options::game.height + INITIAL_Y_OFFSET; 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::Cache::get()->getText("smb2"); // Procesa y ajusta el texto del sprite actual std::string txt = sprite_list_[i]; std::ranges::replace(txt, '_', ' '); // Reemplaza '_' por ' ' if (txt == "player") { txt = "JAILDOCTOR"; // Reemplaza "player" por "JAILDOCTOR" } // Calcula las dimensiones del texto const float W = text->length(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.emplace_back("STARRING"); auto text = Resource::Cache::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->length(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 * TEXT_SPACING_MULTIPLIER)); // 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.emplace_back("THANK YOU"); list.emplace_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->length(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 * TEXT_SPACING_MULTIPLIER)); // 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 (const auto& sprite : texts_) { sprite->getSurface()->fadeSubPalette(0); } }