418 lines
13 KiB
C++
418 lines
13 KiB
C++
#include "game/scenes/intro.h"
|
|
|
|
#include <SDL3/SDL.h>
|
|
|
|
#include <string> // 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
|
|
}
|