#include "ending.h" // Constructor Ending::Ending(SDL_Renderer *renderer, Screen *screen, Resource *resource, Asset *asset, options_t *options) { // Copia los punteros this->renderer = renderer; this->screen = screen; this->resource = resource; this->asset = asset; this->options = options; // Reserva memoria para los punteros a objetos eventHandler = new SDL_Event(); text = new Text(resource->getOffset("smb2.txt"), resource->getTexture("smb2.png"), renderer); music = JA_LoadMusic(asset->get("ending1.ogg").c_str()); // Inicializa variables counter = -1; preCounter = 0; coverCounter = 0; section.name = SECTION_PROG_ENDING; section.subsection = 0; ticks = 0; ticksSpeed = 15; scene = 0; // Inicializa los textos iniTexts(); // Inicializa las imagenes iniPics(); // Inicializa las escenas iniScenes(); // Cambia el color del borde screen->setBorderColor(stringToColor(options->palette, "black")); // Crea la textura para cubrir el rexto coverTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT + 8); if (coverTexture == nullptr) { if (options->console) { std::cout << "Error: canvasTexture could not be created!\nSDL Error: " << SDL_GetError() << std::endl; } } SDL_SetTextureBlendMode(coverTexture, SDL_BLENDMODE_BLEND); // Rellena la textura para la cortinilla fillCoverTexture(); } // Destructor Ending::~Ending() { // Libera la memoria de los objetos delete eventHandler; delete text; SDL_DestroyTexture(coverTexture); for (auto st : spriteTexts) { delete st.sprite; delete st.texture; delete st.coverSprite; delete st.coverTexture; } spriteTexts.clear(); for (auto sp : spritePics) { delete sp.sprite; delete sp.coverSprite; delete sp.coverTexture; } spritePics.clear(); JA_DeleteMusic(music); } // Actualiza el objeto void Ending::update() { // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego if (SDL_GetTicks() - ticks > ticksSpeed) { // Actualiza el contador de ticks ticks = SDL_GetTicks(); // Comprueba el manejador de eventos checkEventHandler(); // Actualiza el contador updateCounters(); // Actualiza las cortinillas de los elementos updateSpriteCovers(); // Comprueba si se ha de cambiar de escena checkChangeScene(); // Actualiza el volumen de la musica updateMusicVolume(); // Actualiza las notificaciones screen->updateNotifier(); } } // Dibuja el final en pantalla void Ending::render() { // Prepara para empezar a dibujar en la textura de juego screen->start(); // Limpia la pantalla screen->clean(stringToColor(options->palette, "black")); // Dibuja las imagenes de la escena spritePics.at(scene).sprite->render(); spritePics.at(scene).coverSprite->render(); // Dibuja los textos de la escena for (auto ti : scenes.at(scene).textIndex) { if (counter > ti.trigger) { spriteTexts.at(ti.index).sprite->render(); spriteTexts.at(ti.index).coverSprite->render(); } } // Dibuja la cortinilla de cambio de escena renderCoverTexture(); // text->write(0, 0, std::to_string(counter)); // Vuelca el contenido del renderizador en pantalla screen->blit(); } // Comprueba el manejador de eventos void Ending::checkEventHandler() { // Comprueba los eventos que hay en la cola while (SDL_PollEvent(eventHandler) != 0) { // Evento de salida de la aplicación if (eventHandler->type == SDL_QUIT) { section.name = SECTION_PROG_QUIT; break; } // Comprueba las teclas que se han pulsado if ((eventHandler->type == SDL_KEYDOWN && eventHandler->key.repeat == 0) || (eventHandler->type == SDL_JOYBUTTONDOWN)) { switch (eventHandler->key.keysym.scancode) { case SDL_SCANCODE_ESCAPE: section.name = SECTION_PROG_QUIT; break; case SDL_SCANCODE_B: screen->switchBorder(); resource->reLoadTextures(); break; case SDL_SCANCODE_F: screen->switchVideoMode(); resource->reLoadTextures(); break; case SDL_SCANCODE_F1: screen->setWindowSize(1); resource->reLoadTextures(); break; case SDL_SCANCODE_F2: screen->setWindowSize(2); resource->reLoadTextures(); break; case SDL_SCANCODE_F3: screen->setWindowSize(3); resource->reLoadTextures(); break; case SDL_SCANCODE_F4: screen->setWindowSize(4); resource->reLoadTextures(); break; case SDL_SCANCODE_F5: switchPalette(); break; default: break; } } } } // Inicializa los textos void Ending::iniTexts() { // Vector con los textos std::vector texts; // Escena #0 texts.push_back({"HE FINALLY MANAGED", 32}); texts.push_back({"TO GET TO THE JAIL", 42}); texts.push_back({"WITH ALL HIS PROJECTS", 142}); texts.push_back({"READY TO BE FREED", 152}); // Escena #1 texts.push_back({"ALL THE JAILERS WERE THERE", 1}); texts.push_back({"WAITING FOR THE JAILGAMES", 11}); texts.push_back({"TO BE RELEASED", 21}); texts.push_back({"THERE WERE EVEN BARRULLS AND", 161}); texts.push_back({"BEGINNERS AMONG THE CROWD", 171}); texts.push_back({"BRY WAS CRYING...", 181}); // Escena #2 texts.push_back({"BUT SUDDENLY SOMETHING", 19}); texts.push_back({"CAUGHT HIS ATTENTION", 29}); // Escena #3 texts.push_back({"A PILE OF JUNK!", 36}); texts.push_back({"FULL OF NON WORKING TRASH!!", 46}); // Escena #4 texts.push_back({"AND THEN,", 36}); texts.push_back({"FOURTY NEW PROJECTS", 46}); texts.push_back({"WERE BORN...", 158}); // Crea los sprites for (auto st : spriteTexts) { delete st.sprite; delete st.texture; delete st.coverSprite; delete st.coverTexture; } spriteTexts.clear(); for (auto t : texts) { endingTexture_t st; const int width = text->lenght(t.caption, 1) + 2 + 2; const int height = text->getCharacterSize() + 2 + 2; color_t c = stringToColor(options->palette, "black"); // Crea la texture st.texture = new Texture(renderer); st.texture->createBlank(renderer, width, height, SDL_TEXTUREACCESS_TARGET); st.texture->setAsRenderTarget(renderer); st.texture->setBlendMode(SDL_BLENDMODE_BLEND); text->writeDX(TXT_STROKE, 2, 2, t.caption, 1, c, 2, c); // Crea el sprite st.sprite = new Sprite({0, 0, st.texture->getWidth(), st.texture->getHeight()}, st.texture, renderer); st.sprite->setPos({(GAMECANVAS_WIDTH - st.texture->getWidth()) / 2, t.pos}); // Crea la coverTexture st.coverTexture = new Texture(renderer); st.coverTexture->createBlank(renderer, width, height + 8, SDL_TEXTUREACCESS_TARGET); st.coverTexture->setAsRenderTarget(renderer); st.coverTexture->setBlendMode(SDL_BLENDMODE_BLEND); // Rellena la coverTexture con color transparente SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_RenderClear(renderer); // Los primeros 8 pixels crea una malla c = stringToColor(options->palette, "black"); SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, 0xFF); for (int i = 0; i < width; i += 2) { SDL_RenderDrawPoint(renderer, i, 0); SDL_RenderDrawPoint(renderer, i, 2); SDL_RenderDrawPoint(renderer, i, 4); SDL_RenderDrawPoint(renderer, i, 6); SDL_RenderDrawPoint(renderer, i + 1, 5); SDL_RenderDrawPoint(renderer, i + 1, 7); } // El resto se rellena de color sólido SDL_Rect rect = {0, 8, width, height}; c = stringToColor(options->palette, "black"); SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, 0xFF); SDL_RenderFillRect(renderer, &rect); // Crea el sprite st.coverSprite = new Sprite({0, 0, st.coverTexture->getWidth(), st.coverTexture->getHeight() - 8}, st.coverTexture, renderer); st.coverSprite->setPos({(GAMECANVAS_WIDTH - st.coverTexture->getWidth()) / 2, t.pos}); st.coverSprite->setSpriteClip(0, 8, -1, -1); // Inicializa variables st.clipDesp = 8; st.clipHeight = height; spriteTexts.push_back(st); } } // Inicializa las imagenes void Ending::iniPics() { // Vector con las rutas y la posición std::vector pics; if (options->palette == p_zxspectrum) { pics.push_back({"ending1.png", 48}); pics.push_back({"ending2.png", 26}); pics.push_back({"ending3.png", 29}); pics.push_back({"ending4.png", 63}); pics.push_back({"ending5.png", 53}); } else { pics.push_back({"ending1_zxarne.png", 48}); pics.push_back({"ending2_zxarne.png", 26}); pics.push_back({"ending3_zxarne.png", 29}); pics.push_back({"ending4_zxarne.png", 63}); pics.push_back({"ending5_zxarne.png", 53}); } // Crea los sprites for (auto sp : spritePics) { delete sp.sprite; delete sp.coverSprite; delete sp.coverTexture; } spritePics.clear(); for (auto p : pics) { endingTexture_t sp; // Crea la texture sp.texture = resource->getTexture(p.caption); const int width = sp.texture->getWidth(); const int height = sp.texture->getHeight(); // Crea el sprite sp.sprite = new Sprite({0, 0, width, height}, sp.texture, renderer); sp.sprite->setPos({(GAMECANVAS_WIDTH - width) / 2, p.pos}); // Crea la coverTexture sp.coverTexture = new Texture(renderer); sp.coverTexture->createBlank(renderer, width, height + 8, SDL_TEXTUREACCESS_TARGET); sp.coverTexture->setAsRenderTarget(renderer); sp.coverTexture->setBlendMode(SDL_BLENDMODE_BLEND); // Rellena la coverTexture con color transparente SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_RenderClear(renderer); // Los primeros 8 pixels crea una malla color_t c = stringToColor(options->palette, "black"); SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, 0xFF); for (int i = 0; i < width; i += 2) { SDL_RenderDrawPoint(renderer, i, 0); SDL_RenderDrawPoint(renderer, i, 2); SDL_RenderDrawPoint(renderer, i, 4); SDL_RenderDrawPoint(renderer, i, 6); SDL_RenderDrawPoint(renderer, i + 1, 5); SDL_RenderDrawPoint(renderer, i + 1, 7); } // El resto se rellena de color sólido SDL_Rect rect = {0, 8, width, height}; c = stringToColor(options->palette, "black"); SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, 0xFF); SDL_RenderFillRect(renderer, &rect); // Crea el sprite sp.coverSprite = new Sprite({0, 0, sp.coverTexture->getWidth(), sp.coverTexture->getHeight() - 8}, sp.coverTexture, renderer); sp.coverSprite->setPos({(GAMECANVAS_WIDTH - sp.coverTexture->getWidth()) / 2, p.pos}); sp.coverSprite->setSpriteClip(0, 8, -1, -1); // Inicializa variables sp.clipDesp = 8; sp.clipHeight = height; spritePics.push_back(sp); } } // Inicializa las escenas void Ending::iniScenes() { // Variable para los tiempos int trigger; const int lapse = 80; // Crea el contenedor scene_t sc; // Inicializa el vector scenes.clear(); // Crea la escena #0 sc.counterEnd = 1000; sc.pictureIndex = 0; sc.textIndex.clear(); trigger = 85 * 2; trigger += lapse; sc.textIndex.push_back({0, trigger}); trigger += lapse; sc.textIndex.push_back({1, trigger}); trigger += lapse * 3; sc.textIndex.push_back({2, trigger}); trigger += lapse; sc.textIndex.push_back({3, trigger}); scenes.push_back(sc); // Crea la escena #1 sc.counterEnd = 1400; sc.pictureIndex = 1; sc.textIndex.clear(); trigger = 140 * 2; trigger += lapse; sc.textIndex.push_back({4, trigger}); trigger += lapse; sc.textIndex.push_back({5, trigger}); trigger += lapse; sc.textIndex.push_back({6, trigger}); trigger += lapse * 3; sc.textIndex.push_back({7, trigger}); trigger += lapse; sc.textIndex.push_back({8, trigger}); trigger += lapse * 3; sc.textIndex.push_back({9, trigger}); scenes.push_back(sc); // Crea la escena #2 sc.counterEnd = 1000; sc.pictureIndex = 2; sc.textIndex.clear(); trigger = 148 / 2; trigger += lapse; sc.textIndex.push_back({10, trigger}); trigger += lapse; sc.textIndex.push_back({11, trigger}); scenes.push_back(sc); // Crea la escena #3 sc.counterEnd = 800; sc.pictureIndex = 3; sc.textIndex.clear(); trigger = 87 / 2; trigger += lapse; sc.textIndex.push_back({12, trigger}); trigger += lapse / 2; sc.textIndex.push_back({13, trigger}); scenes.push_back(sc); // Crea la escena #4 sc.counterEnd = 1000; sc.pictureIndex = 4; sc.textIndex.clear(); trigger = 91 * 2; trigger += lapse; sc.textIndex.push_back({14, trigger}); trigger += lapse * 2; sc.textIndex.push_back({15, trigger}); trigger += lapse * 3; sc.textIndex.push_back({16, trigger}); scenes.push_back(sc); } // Bucle principal section_t Ending::run() { JA_PlayMusic(music); while (section.name == SECTION_PROG_ENDING) { update(); render(); } JA_StopMusic(); JA_SetVolume(128); return section; } // Actualiza los contadores void Ending::updateCounters() { // Incrementa el contador if (preCounter < 200) { preCounter++; } else { counter++; } if (counter > scenes.at(scene).counterEnd - 100) { coverCounter++; } } // Actualiza las cortinillas de los elementos void Ending::updateSpriteCovers() { // Actualiza la cortinilla de los textos if (counter % 4 == 0) { for (auto ti : scenes.at(scene).textIndex) { if (counter > ti.trigger) { if (spriteTexts.at(ti.index).clipDesp > 0) { spriteTexts.at(ti.index).clipDesp -= 2; } else if (spriteTexts.at(ti.index).clipHeight > 0) { spriteTexts.at(ti.index).clipHeight -= 2; spriteTexts.at(ti.index).coverSprite->setPosY(spriteTexts.at(ti.index).coverSprite->getPosY() + 2); } spriteTexts.at(ti.index).coverSprite->setSpriteClip(0, spriteTexts.at(ti.index).clipDesp, spriteTexts.at(ti.index).coverSprite->getWidth(), spriteTexts.at(ti.index).clipHeight); } } } // Actualiza la cortinilla de las imagenes if (counter % 2 == 0) { if (spritePics.at(scene).clipDesp > 0) { spritePics.at(scene).clipDesp -= 2; } else if (spritePics.at(scene).clipHeight > 0) { spritePics.at(scene).clipHeight -= 2; if (spritePics.at(scene).clipHeight < 0) { spritePics.at(scene).clipHeight = 0; } spritePics.at(scene).coverSprite->setPosY(spritePics.at(scene).coverSprite->getPosY() + 2); } spritePics.at(scene).coverSprite->setSpriteClip(0, spritePics.at(scene).clipDesp, spritePics.at(scene).coverSprite->getWidth(), spritePics.at(scene).clipHeight); } } // Comprueba si se ha de cambiar de escena void Ending::checkChangeScene() { if (counter > scenes.at(scene).counterEnd) { scene++; counter = 0; coverCounter = 0; if (scene == 5) { // Termina el bucle section.name = SECTION_PROG_ENDING2; // Mantiene los valores anteriores scene = 4; coverCounter = 100; } } } // Rellena la textura para la cortinilla void Ending::fillCoverTexture() { // Rellena la textura que cubre el texto con color transparente SDL_SetRenderTarget(renderer, coverTexture); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_RenderClear(renderer); // Los primeros 8 pixels crea una malla const color_t c = stringToColor(options->palette, "brack"); SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, 0xFF); for (int i = 0; i < 256; i += 2) { SDL_RenderDrawPoint(renderer, i + 0, GAMECANVAS_HEIGHT + 0); SDL_RenderDrawPoint(renderer, i + 1, GAMECANVAS_HEIGHT + 1); SDL_RenderDrawPoint(renderer, i + 0, GAMECANVAS_HEIGHT + 2); SDL_RenderDrawPoint(renderer, i + 1, GAMECANVAS_HEIGHT + 3); SDL_RenderDrawPoint(renderer, i, GAMECANVAS_HEIGHT + 4); SDL_RenderDrawPoint(renderer, i, GAMECANVAS_HEIGHT + 6); } // El resto se rellena de color sólido SDL_Rect rect = {0, 0, 256, GAMECANVAS_HEIGHT}; SDL_RenderFillRect(renderer, &rect); SDL_SetRenderTarget(renderer, nullptr); } // Dibuja la cortinilla de cambio de escena void Ending::renderCoverTexture() { if (coverCounter > 0) { // Dibuja la textura que cubre el texto const int offset = std::min(coverCounter, 100); SDL_Rect srcRect = {0, 200 - (coverCounter * 2), 256, offset * 2}; SDL_Rect dstRect = {0, 0, 256, offset * 2}; SDL_RenderCopy(renderer, coverTexture, &srcRect, &dstRect); } } // Actualiza el volumen de la musica void Ending::updateMusicVolume() { if (scene == 4 && coverCounter > 0) { const float step = (100.0f - coverCounter) / 100.0f; const int volume = 128 * step; JA_SetVolume(volume); } } // Cambia la paleta void Ending::switchPalette() { if (options->palette == p_zxspectrum) { options->palette = p_zxarne; spritePics.at(0).sprite->setTexture(resource->getTexture("ending1_zxarne.png")); spritePics.at(1).sprite->setTexture(resource->getTexture("ending2_zxarne.png")); spritePics.at(2).sprite->setTexture(resource->getTexture("ending3_zxarne.png")); spritePics.at(3).sprite->setTexture(resource->getTexture("ending4_zxarne.png")); spritePics.at(4).sprite->setTexture(resource->getTexture("ending5_zxarne.png")); } else { options->palette = p_zxspectrum; spritePics.at(0).sprite->setTexture(resource->getTexture("ending1.png")); spritePics.at(1).sprite->setTexture(resource->getTexture("ending2.png")); spritePics.at(2).sprite->setTexture(resource->getTexture("ending3.png")); spritePics.at(3).sprite->setTexture(resource->getTexture("ending4.png")); spritePics.at(4).sprite->setTexture(resource->getTexture("ending5.png")); } }