#include "game/scenes/intro.h" #include #include // for basic_string #include "core/audio/audio.hpp" // for Audio::get, Audio::update #include "core/input/global_inputs.hpp" // for GlobalInputs::handle #include "core/input/input.h" // for Input, Input::Repeat::OFF, InputAction #include "core/locale/lang.h" // for Lang #include "core/rendering/screen.h" // for Screen #include "core/rendering/smartsprite.h" // for SmartSprite #include "core/rendering/writer.h" // for Writer #include "core/resources/resource.h" #include "core/system/delta_time.hpp" #include "game/defaults.hpp" // for GAMECANVAS_CENTER_X, GAMECANVAS_FIRST_QU... #include "utils/utils.h" // for Section, Color // =========================================================================== // Time-based. Tots els valors precalculats: velocitats en px/s, acceleracions // en px/s^2, durades en segons. La cadència real de la versió frame-based era // empíricament ~60 Hz (el gate `> 15 ms` esperava al següent múltiple del // refresh del SO, típicament 16.67 ms). Per això la conversió és: // vel px/tick → vel * 60 = px/s // acc px/tick² → acc * 3600 = px/s² // counter frames → counter/60 = segons // =========================================================================== namespace { // Durades comunes (segons). Arrodonides amunt respecte a la conversió // exacta des de frames per a tenir valors més "bonics" i una mica més // de respir visual. constexpr float BITMAP_REMAINING_TIME_S = 0.5F; // de 0.333 (20/60) ⇒ 0.5 constexpr float BITMAP_FALLING_REMAINING_TIME_S = 5.0F; // de 4.167 (250/60) ⇒ 5.0 constexpr float TEXT_REMAINING_TIME_S = 3.0F; // 180/60 ⇒ ja és exacte } // namespace // Constructor Intro::Intro(SDL_Renderer *renderer, Section *section) { // Copia los punteros this->renderer_ = renderer; this->section_ = section; // Reserva memoria para los objetos event_handler_ = new SDL_Event(); texture_ = Resource::get()->getTexture("intro.png"); text_ = Resource::get()->getText("nokia"); music_ = Resource::get()->getMusic("intro.ogg"); // Inicializa variables section->name = SECTION_PROG_INTRO; section->subsection = 0; scene_ = 1; // Inicializa los bitmaps de la intro const int TOTAL_BITMAPS = 6; for (int i = 0; i < TOTAL_BITMAPS; ++i) { auto *ss = new SmartSprite(texture_, renderer); ss->setWidth(128); ss->setHeight(96); ss->setRemainingTime(BITMAP_REMAINING_TIME_S); ss->setDestX(GAMECANVAS_CENTER_X - 64); ss->setDestY(GAMECANVAS_FIRST_QUARTER_Y - 24); bitmaps_.push_back(ss); } // bitmap 0: entra des de l'esquerra, accelerant cap a la dreta bitmaps_[0]->setPosX(-128); bitmaps_[0]->setPosY(GAMECANVAS_FIRST_QUARTER_Y - 24); bitmaps_[0]->setVelX(0.0F); bitmaps_[0]->setVelY(0.0F); bitmaps_[0]->setAccelX(2160.0F); // 0.6 px/tick² ⇒ 0.6 * 3600 px/s² bitmaps_[0]->setAccelY(0.0F); bitmaps_[0]->setSpriteClip(0, 0, 128, 96); // bitmap 1: entra des de la dreta amb velocitat negativa i accelera més bitmaps_[1]->setPosX(GAMECANVAS_WIDTH); bitmaps_[1]->setPosY(GAMECANVAS_FIRST_QUARTER_Y - 24); bitmaps_[1]->setVelX(-60.0F); // -1 px/tick ⇒ -60 px/s bitmaps_[1]->setVelY(0.0F); bitmaps_[1]->setAccelX(-1080.0F); // -0.3 px/tick² ⇒ -1080 px/s² bitmaps_[1]->setAccelY(0.0F); bitmaps_[1]->setSpriteClip(128, 0, 128, 96); // bitmap 2: cau des de dalt; queda visible més temps (escena "GRITO") bitmaps_[2]->setPosX(GAMECANVAS_CENTER_X - 64); bitmaps_[2]->setPosY(-96); bitmaps_[2]->setVelX(0.0F); bitmaps_[2]->setVelY(180.0F); // 3 px/tick ⇒ 180 px/s bitmaps_[2]->setAccelX(360.0F); // 0.1 px/tick² ⇒ 360 px/s² bitmaps_[2]->setAccelY(1080.0F); // 0.3 px/tick² ⇒ 1080 px/s² bitmaps_[2]->setSpriteClip(0, 96, 128, 96); bitmaps_[2]->setRemainingTime(BITMAP_FALLING_REMAINING_TIME_S); // bitmap 3: puja lentament des de baix (reflexió) bitmaps_[3]->setPosX(GAMECANVAS_CENTER_X - 64); bitmaps_[3]->setPosY(GAMECANVAS_HEIGHT); bitmaps_[3]->setVelX(0.0F); bitmaps_[3]->setVelY(-42.0F); // -0.7 px/tick ⇒ -42 px/s bitmaps_[3]->setAccelX(0.0F); bitmaps_[3]->setAccelY(0.0F); bitmaps_[3]->setSpriteClip(128, 96, 128, 96); // bitmap 4: cau des de dalt (mateix que bitmap 2, sense temps allargat) bitmaps_[4]->setPosX(GAMECANVAS_CENTER_X - 64); bitmaps_[4]->setPosY(-96); bitmaps_[4]->setVelX(0.0F); bitmaps_[4]->setVelY(180.0F); bitmaps_[4]->setAccelX(360.0F); bitmaps_[4]->setAccelY(1080.0F); bitmaps_[4]->setSpriteClip(0, 192, 128, 96); // bitmap 5: entra des de la dreta lentament bitmaps_[5]->setPosX(GAMECANVAS_WIDTH); bitmaps_[5]->setPosY(GAMECANVAS_FIRST_QUARTER_Y - 24); bitmaps_[5]->setVelX(-42.0F); // -0.7 px/tick ⇒ -42 px/s bitmaps_[5]->setVelY(0.0F); bitmaps_[5]->setAccelX(0.0F); bitmaps_[5]->setAccelY(0.0F); bitmaps_[5]->setSpriteClip(128, 192, 128, 96); // Inicializa los textos de la intro. Time-based: setSecondsPerChar. // Conversió: frames_per_char / 60 = segons_per_char. const int TOTAL_TEXTS = 9; for (int i = 0; i < TOTAL_TEXTS; ++i) { auto *w = new Writer(text_); w->setPosX(BLOCK * 0); w->setPosY(GAMECANVAS_HEIGHT - (BLOCK * 6)); w->setKerning(-1); w->setEnabled(false); w->setRemainingTime(TEXT_REMAINING_TIME_S); texts_.push_back(w); } // Un dia qualsevol de l'any 2000 texts_[0]->setCaption(Lang::get()->getText(27)); texts_[0]->setSecondsPerChar(0.15F); // de 0.1333 (8/60) ⇒ 0.15 // Tot esta tranquil a la UPV texts_[1]->setCaption(Lang::get()->getText(28)); texts_[1]->setSecondsPerChar(0.15F); // Fins que un desaprensiu... texts_[2]->setCaption(Lang::get()->getText(29)); texts_[2]->setSecondsPerChar(0.2F); // 12/60 ⇒ ja és 0.2 // HEY! ME ANE A FERME UN CORTAET... texts_[3]->setCaption(Lang::get()->getText(30)); texts_[3]->setSecondsPerChar(0.15F); // UAAAAAAAAAAAAA!!! texts_[4]->setCaption(Lang::get()->getText(31)); texts_[4]->setSecondsPerChar(0.02F); // de 0.0167 (1/60) ⇒ 0.02 // Espera un moment... texts_[5]->setCaption(Lang::get()->getText(32)); texts_[5]->setSecondsPerChar(0.3F); // de 0.2667 (16/60) ⇒ 0.3 // Si resulta que no tinc solt! texts_[6]->setCaption(Lang::get()->getText(33)); texts_[6]->setSecondsPerChar(0.05F); // de 0.0333 (2/60) ⇒ 0.05 // MERDA DE MAQUINA! texts_[7]->setCaption(Lang::get()->getText(34)); texts_[7]->setSecondsPerChar(0.05F); // 3/60 ⇒ ja és 0.05 // Blop... blop... blop... texts_[8]->setCaption(Lang::get()->getText(35)); texts_[8]->setSecondsPerChar(0.3F); for (auto *t : texts_) { t->center(GAMECANVAS_CENTER_X); } Audio::get()->playMusic(music_, 0); DeltaTime::reset(); } // Destructor Intro::~Intro() { delete event_handler_; // texture, text, music son propiedad de Resource — no liberar aquí. for (auto *bitmap : bitmaps_) { delete bitmap; } for (auto *t : texts_) { delete t; } } // Comprueba las entradas void Intro::checkInput() { // ESC (Action::EXIT) ja el gestiona GlobalInputs::handle() amb doble // pulsació; el quit es propaga via Director::iterate. if (GlobalInputs::handle()) { return; } if (Input::get()->checkInput(Input::Action::ACCEPT, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::FIRE_LEFT, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::FIRE_CENTER, Input::Repeat::OFF) || Input::get()->checkInput(Input::Action::FIRE_RIGHT, Input::Repeat::OFF)) { Audio::get()->stopMusic(); section_->name = SECTION_PROG_TITLE; section_->subsection = SUBSECTION_TITLE_1; } } // Actualiza las escenas de la intro void Intro::updateScenes() { switch (scene_) { case 1: updateScene1(); break; case 2: updateScene2(); break; case 3: updateScene3(); break; case 4: updateScene4(); break; case 5: updateScene5(); break; case 6: updateScene6(); break; default: break; } } // Primera escena - UPV void Intro::updateScene1() { // Primera imagen - UPV if (!bitmaps_[0]->hasFinished()) { bitmaps_[0]->setEnabled(true); } // Primer texto de la primera imagen if (bitmaps_[0]->hasFinished() && !texts_[0]->hasFinished()) { texts_[0]->setEnabled(true); } // Segundo texto de la primera imagen if (texts_[0]->hasFinished() && !texts_[1]->hasFinished()) { texts_[0]->setEnabled(false); texts_[1]->setEnabled(true); } // Tercer texto de la primera imagen if (texts_[1]->hasFinished() && !texts_[2]->hasFinished()) { texts_[1]->setEnabled(false); texts_[2]->setEnabled(true); } // Fin de la primera escena if (texts_[2]->hasFinished()) { bitmaps_[0]->setEnabled(false); texts_[2]->setEnabled(false); scene_++; } } // Segunda escena - Máquina void Intro::updateScene2() { // Segunda imagen - Máquina if (!bitmaps_[1]->hasFinished()) { bitmaps_[1]->setEnabled(true); } // Primer texto de la segunda imagen if (bitmaps_[1]->hasFinished() && !texts_[3]->hasFinished()) { texts_[3]->setEnabled(true); } // Fin de la segunda escena if (texts_[3]->hasFinished()) { bitmaps_[1]->setEnabled(false); texts_[3]->setEnabled(false); scene_++; } } // Tercera escena - GRITO void Intro::updateScene3() { // Tercera imagen junto con primer texto - GRITO if (!bitmaps_[2]->hasFinished() && !texts_[4]->hasFinished()) { bitmaps_[2]->setEnabled(true); texts_[4]->setEnabled(true); } // Fin de la tercera escena if (bitmaps_[2]->hasFinished() && texts_[4]->hasFinished()) { bitmaps_[2]->setEnabled(false); texts_[4]->setEnabled(false); scene_++; } } // Cuarta escena - Reflexión void Intro::updateScene4() { // Cuarta imagen junto con primer texto - Reflexión if (!bitmaps_[3]->hasFinished() && !texts_[5]->hasFinished()) { bitmaps_[3]->setEnabled(true); texts_[5]->setEnabled(true); } // Segundo texto de la cuarta imagen if (texts_[5]->hasFinished() && !texts_[6]->hasFinished()) { texts_[5]->setEnabled(false); texts_[6]->setEnabled(true); } // Fin de la cuarta escena if (bitmaps_[3]->hasFinished() && texts_[6]->hasFinished()) { bitmaps_[3]->setEnabled(false); texts_[6]->setEnabled(false); scene_++; } } // Quinta escena - Patada void Intro::updateScene5() { // Quinta imagen - Patada if (!bitmaps_[4]->hasFinished()) { bitmaps_[4]->setEnabled(true); } // Primer texto de la quinta imagen if (bitmaps_[4]->hasFinished() && !texts_[7]->hasFinished()) { texts_[7]->setEnabled(true); } // Fin de la quinta escena if (bitmaps_[4]->hasFinished() && texts_[7]->hasFinished()) { bitmaps_[4]->setEnabled(false); texts_[7]->setEnabled(false); scene_++; } } // Sexta escena - Globos de café void Intro::updateScene6() { // Sexta imagen junto con texto - Globos de café if (!bitmaps_[5]->hasFinished() && !texts_[8]->hasFinished()) { bitmaps_[5]->setEnabled(true); texts_[8]->setEnabled(true); } // Acaba el último texto if (bitmaps_[5]->hasFinished() && texts_[8]->hasFinished()) { bitmaps_[5]->setEnabled(false); texts_[8]->setEnabled(false); Audio::get()->stopMusic(); section_->name = SECTION_PROG_TITLE; section_->subsection = SUBSECTION_TITLE_1; } } // Actualiza las variables del objeto void Intro::update(float dt_s) { Audio::update(); checkInput(); for (auto *bitmap : bitmaps_) { bitmap->update(dt_s); } for (auto *t : texts_) { t->update(dt_s); } updateScenes(); } // 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(BG_COLOR); // Dibuja los objetos for (auto *bitmap : bitmaps_) { bitmap->render(); } for (auto *t : texts_) { t->render(); } // Vuelca el contenido del renderizador en pantalla Screen::get()->blit(); } // Bucle principal void Intro::run() { Audio::get()->playMusic(music_, 0); while (section_->name == SECTION_PROG_INTRO) { iterate(); } } // Ejecuta un frame void Intro::iterate() { const float DELTA_TIME_S = DeltaTime::tick(); update(DELTA_TIME_S); render(); } // Procesa un evento individual void Intro::handleEvent(const SDL_Event * /*event*/) { // SDL_EVENT_QUIT ya lo maneja Director }