#include "title.h" #include // for SDL_BLENDMODE_BLEND #include // for SDL_GetError #include // for SDL_PollEvent, SDL_Event, SDL_KEYDOWN #include // for SDL_PIXELFORMAT_RGBA8888 #include // for SDL_SCANCODE_1, SDL_SCANCODE_2 #include // for SDL_GetTicks #include // for basic_ostream, operator<<, cout, endl #include "asset.h" // for Asset #include "cheevos.h" // for Achievement, Cheevos #include "defines.h" // for PLAY_AREA_CENTER_X, GAMECANVAS_WIDTH #include "global_events.h" // for check #include "global_inputs.h" // for check #include "input.h" // for Input, inputs_e, REPEAT_FALSE, REPEA... #include "options.h" // for Options, options, OptionsVideo, Sect... #include "resource.h" // for Resource #include "screen.h" // for Screen #include "s_sprite.h" // for SSprite #include "text.h" // for Text, TEXT_CENTER, TEXT_COLOR #include "surface.h" // for Surface #include "utils.h" // for Color, stringToColor, Palette #include "paleta.h" // Constructor Title::Title() : surface_(Resource::get()->getSurface("title_logo.gif")), sprite_(std::make_shared(surface_, 0, 0, surface_->getWidth(), surface_->getHeight())), bg_surface_(std::make_shared(Screen::get()->getRenderSurfaceData(), options.game.width, options.game.height)) { // Carga la surface con los gráficos de la pantalla de carga pInit(Screen::get()->getRenderer(), 256, 128); loading_screen_ = pLoadSurface(Asset::get()->get("loading_screen_color.gif").c_str()); pLoadPal(Asset::get()->get("loading_screen_color.gif").c_str()); pSetSource(loading_screen_); // Inicializa variables state_ = options.section.subsection == Subsection::TITLE_WITH_LOADING_SCREEN ? TitleState::SHOW_LOADING_SCREEN : TitleState::SHOW_MENU; options.section.section = Section::TITLE; options.section.subsection = Subsection::NONE; initMarquee(); // Crea y rellena la textura para mostrar los logros createCheevosTexture(); // Cambia el color del borde Screen::get()->setBorderColor(stringToColor(options.video.palette, "black")); // Rellena la textura de fondo con todos los gráficos fillSurface(); // Inicia la musica playMusic("title.ogg"); } // Destructor Title::~Title() { pDeleteSurface(loading_screen_); } // Inicializa la marquesina void Title::initMarquee() { letters_.clear(); long_text_ = "HEY JAILERS!! IT'S 2022 AND WE'RE STILL ROCKING LIKE IT'S 1998!!! HAVE YOU HEARD IT? JAILGAMES ARE BACK!! YEEESSS BACK!! MORE THAN 10 TITLES ON JAILDOC'S KITCHEN!! THATS A LOOOOOOT OF JAILGAMES, BUT WHICH ONE WILL STRIKE FIRST? THERE IS ALSO A NEW DEVICE TO COME THAT WILL BLOW YOUR MIND WITH JAILGAMES ON THE GO: P.A.C.O. BUT WAIT! WHAT'S THAT BEAUTY I'M SEEING RIGHT OVER THERE?? OOOH THAT TINY MINIASCII IS PURE LOVE!! I WANT TO LICK EVERY BYTE OF IT!! OH SHIT! AND DON'T FORGET TO BRING BACK THOSE OLD AND FAT MS-DOS JAILGAMES TO GITHUB TO KEEP THEM ALIVE!! WHAT WILL BE THE NEXT JAILDOC RELEASE? WHAT WILL BE THE NEXT PROJECT TO COME ALIVE?? OH BABY WE DON'T KNOW BUT HERE YOU CAN FIND THE ANSWER, YOU JUST HAVE TO COMPLETE JAILDOCTOR'S DILEMMA ... COULD YOU?"; for (int i = 0; i < (int)long_text_.length(); ++i) { TitleLetter l; l.letter = long_text_.substr(i, 1); l.x = 256; l.enabled = false; letters_.push_back(l); } letters_[0].enabled = true; } // Comprueba el manejador de eventos void Title::checkEvents() { SDL_Event event; while (SDL_PollEvent(&event)) { globalEvents::check(event); // Solo se comprueban estas teclas si no está activo el menu de logros if (event.type == SDL_KEYDOWN) { if (!show_cheevos_) { switch (event.key.keysym.scancode) { case SDL_SCANCODE_1: options.section.section = Section::GAME; options.section.subsection = Subsection::NONE; break; case SDL_SCANCODE_2: show_cheevos_ = true; break; default: break; } } } } } // Comprueba las entradas void Title::checkInput() { if (show_cheevos_) { if (Input::get()->checkInput(InputAction::DOWN, REPEAT_TRUE)) { moveCheevosList(1); } else if (Input::get()->checkInput(InputAction::UP, REPEAT_TRUE)) { moveCheevosList(0); } else if (Input::get()->checkInput(InputAction::ACCEPT, REPEAT_FALSE)) { hideCheevosList(); counter_ = 0; } } if (Input::get()->checkInput(InputAction::ACCEPT, REPEAT_FALSE)) { if (state_ == TitleState::SHOW_LOADING_SCREEN) { state_ = TitleState::FADE_LOADING_SCREEN; } } globalInputs::check(); } // Actualiza la marquesina void Title::updateMarquee() { const auto TEXT = Resource::get()->getText("smb2"); for (int i = 0; i < (int)letters_.size(); ++i) { if (letters_[i].enabled) { letters_[i].x -= marquee_speed_; if (letters_[i].x < -10) { letters_[i].enabled = false; } } else { if (i > 0 && letters_[i - 1].x < 256 && letters_[i - 1].enabled) { letters_[i].enabled = true; letters_[i].x = letters_[i - 1].x + TEXT->lenght(letters_[i - 1].letter) + 1; } } } // Comprueba si ha terminado la marquesina y la reinicia if (letters_[letters_.size() - 1].x < -10) { // Inicializa la marquesina initMarquee(); } } // Dibuja la marquesina void Title::renderMarquee() { const auto TEXT = Resource::get()->getText("smb2"); for (const auto &l : letters_) { if (l.enabled) { TEXT->writeColored(l.x, 184, l.letter, stringToColor(options.video.palette, "white")); } } } // Actualiza las variables void Title::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(); Screen::get()->update(); // Incrementa el contador counter_++; switch (state_) { case TitleState::SHOW_LOADING_SCREEN: if (counter_ == 500) { counter_ = 0; state_ = TitleState::FADE_LOADING_SCREEN; } break; case TitleState::FADE_LOADING_SCREEN: if (counter_ % 4 == 0) { if (pFadePal()) { counter_ = 0; state_ = TitleState::SHOW_MENU; } } break; case TitleState::SHOW_MENU: // Actualiza la marquesina updateMarquee(); // Si el contador alcanza cierto valor, termina la seccion if (counter_ == 2200) { if (!show_cheevos_) { options.section.section = Section::CREDITS; options.section.subsection = Subsection::NONE; } } break; default: break; } } } // Dibuja en pantalla void Title::render() { // Prepara para empezar a dibujar en la textura de juego Screen::get()->start(); Screen::get()->clean(stringToColor("black")); if (state_ == TitleState::SHOW_MENU) { // Dibuja la textura de fondo bg_surface_->render(0, 0); // Dibuja la marquesina renderMarquee(); // Dibuja la información de logros if (show_cheevos_) { cheevos_sprite_->render(); } } else { // Dibuja la pantalla de carga pCls(4); pBlit(0, 0, 0, 0, 256, 128); pFlip(Screen::get()->getRenderer()); // Dibuja el logo del título sprite_->render(); } // Vuelca el contenido del renderizador en pantalla Screen::get()->render(); } // Bucle para el logo del juego void Title::run() { while (options.section.section == Section::TITLE) { update(); checkEvents(); render(); } } // Desplaza la lista de logros void Title::moveCheevosList(int direction) { const int speed = 2; cheevos_surface_view_.y = direction == 0 ? cheevos_surface_view_.y - speed : cheevos_surface_view_.y + speed; const int bottom = cheevos_surface_->getHeight() - cheevos_surface_view_.h; if (cheevos_surface_view_.y < 0) { cheevos_surface_view_.y = 0; } else if (cheevos_surface_view_.y > bottom) { cheevos_surface_view_.y = bottom; } cheevos_sprite_->setClip(cheevos_surface_view_); } // Rellena la textura de fondo con todos los gráficos void Title::fillSurface() { // Coloca el puntero del renderizador sobre la textura Screen::get()->setRenderSurfaceData(bg_surface_); // Rellena la textura de color bg_surface_->setColor(255, 0xFF000000); bg_surface_->clear(255); // Pinta el gráfico del titulo a partir del sprite sprite_->render(); // Escribe el texto en la textura auto text = Resource::get()->getText("smb2"); const Color COLOR = stringToColor(options.video.palette, "green"); const int TEXT_SIZE = text->getCharacterSize(); text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, 11 * TEXT_SIZE, "1.PLAY", 1, COLOR); text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, 13 * TEXT_SIZE, "2.ACHIEVEMENTS", 1, COLOR); text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, 15 * TEXT_SIZE, "3.REDEFINE KEYS", 1, COLOR); text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, 20 * TEXT_SIZE, "ESC.EXIT GAME", 1, COLOR); // Devuelve el puntero del renderizador a su sitio Screen::get()->setRenderSurfaceData(nullptr); } // Crea y rellena la textura para mostrar los logros void Title::createCheevosTexture() { // Crea la textura con el listado de logros const auto CHEEVOS_LIST = Cheevos::get()->list(); const auto TEXT = Resource::get()->getText("subatomic"); constexpr int CHEEVOS_TEXTURE_WIDTH = 200; constexpr int CHEEVOS_TEXTURE_VIEW_HEIGHT = 110; constexpr int CHEEVOS_TEXTURE_POS_Y = 73; constexpr int CHEEVOS_PADDING = 10; const int CHEEVO_HEIGHT = CHEEVOS_PADDING + (TEXT->getCharacterSize() * 2) + 1; const int CHEEVOS_TEXTURE_HEIGHT = (CHEEVO_HEIGHT * CHEEVOS_LIST.size()) + 2 + TEXT->getCharacterSize() + 8; cheevos_surface_ = std::make_shared(Screen::get()->getRenderSurfaceData(), CHEEVOS_TEXTURE_WIDTH, CHEEVOS_TEXTURE_HEIGHT); // Prepara para dibujar sobre la textura Screen::get()->setRenderSurfaceData(cheevos_surface_); // Rellena la textura con color sólido const Color CHEEVOS_BG_COLOR = stringToColor(options.video.palette, "black"); SDL_SetRenderDrawColor(Screen::get()->getRenderer(), CHEEVOS_BG_COLOR.r, CHEEVOS_BG_COLOR.g, CHEEVOS_BG_COLOR.b, 0xFF); SDL_RenderClear(Screen::get()->getRenderer()); // Escribe la lista de logros en la textura const std::string CHEEVOS_OWNER = "ACHIEVEMENTS"; const std::string CHEEVOS_LIST_CAPTION = CHEEVOS_OWNER + " (" + std::to_string(Cheevos::get()->getTotalUnlockedAchievements()) + " / " + std::to_string(Cheevos::get()->size()) + ")"; int pos = 2; TEXT->writeDX(TEXT_CENTER | TEXT_COLOR, cheevos_surface_->getWidth() / 2, pos, CHEEVOS_LIST_CAPTION, 1, stringToColor(options.video.palette, "bright_green")); pos += TEXT->getCharacterSize(); const Color CHEEVO_LOCKED_COLOR = stringToColor(options.video.palette, "white"); const Color CHEEVO_UNLOCKED_COLOR = stringToColor(options.video.palette, "bright_green"); Color cheevoColor; SDL_SetRenderDrawColor(Screen::get()->getRenderer(), CHEEVO_LOCKED_COLOR.r, CHEEVO_LOCKED_COLOR.g, CHEEVO_LOCKED_COLOR.b, 0xFF); constexpr int LINE_X1 = (CHEEVOS_TEXTURE_WIDTH / 7) * 3; constexpr int LINE_X2 = LINE_X1 + ((CHEEVOS_TEXTURE_WIDTH / 7) * 1); for (const auto &cheevo : CHEEVOS_LIST) { cheevoColor = cheevo.completed ? CHEEVO_UNLOCKED_COLOR : CHEEVO_LOCKED_COLOR; pos += CHEEVOS_PADDING; constexpr int HALF = CHEEVOS_PADDING / 2; SDL_RenderDrawLine(Screen::get()->getRenderer(), LINE_X1, pos - HALF - 1, LINE_X2, pos - HALF - 1); TEXT->writeDX(TEXT_CENTER | TEXT_COLOR, CHEEVOS_TEXTURE_WIDTH / 2, pos, cheevo.caption, 1, cheevoColor); pos += TEXT->getCharacterSize() + 1; TEXT->writeDX(TEXT_CENTER | TEXT_COLOR, CHEEVOS_TEXTURE_WIDTH / 2, pos, cheevo.description, 1, cheevoColor); pos += TEXT->getCharacterSize(); } // Restablece el RenderSurface Screen::get()->setRenderSurfaceData(nullptr); // Crea el sprite para el listado de logros cheevos_sprite_ = std::make_shared(cheevos_surface_, (GAMECANVAS_WIDTH - cheevos_surface_->getWidth()) / 2, CHEEVOS_TEXTURE_POS_Y, cheevos_surface_->getWidth(), cheevos_surface_->getHeight()); cheevos_surface_view_ = {0, 0, cheevos_surface_->getWidth(), CHEEVOS_TEXTURE_VIEW_HEIGHT}; cheevos_sprite_->setClip(cheevos_surface_view_); } // Oculta la lista de logros void Title::hideCheevosList() { show_cheevos_ = false; cheevos_surface_view_.y = 0; cheevos_sprite_->setClip(cheevos_surface_view_); }