diff --git a/.gitignore b/.gitignore index dccb8b2..05105c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .vscode +.claude build/ data/config/config.txt *.DS_Store diff --git a/source/game_logo.cpp b/source/game_logo.cpp index 5db5d49..b14c820 100644 --- a/source/game_logo.cpp +++ b/source/game_logo.cpp @@ -45,6 +45,7 @@ void GameLogo::init() { arcade_edition_status_ = Status::DISABLED; shake_.init(1, 2, 8, XP); zoom_ = 3.0F * ZOOM_FACTOR; + post_finished_time_accumulator_ = 0.0f; // Inicializa el bitmap de 'Coffee' coffee_sprite_->setPosX(XP); @@ -112,13 +113,20 @@ void GameLogo::render() { } } -// Actualiza la lógica de la clase +// Actualiza la lógica de la clase (frame-based) void GameLogo::update() { updateCoffeeCrisis(); updateArcadeEdition(); updatePostFinishedCounter(); } +// Actualiza la lógica de la clase (time-based) +void GameLogo::update(float deltaTime) { + updateCoffeeCrisis(deltaTime); + updateArcadeEdition(deltaTime); + updatePostFinishedCounter(deltaTime); +} + void GameLogo::updateCoffeeCrisis() { switch (coffee_crisis_status_) { case Status::MOVING: @@ -135,6 +143,22 @@ void GameLogo::updateCoffeeCrisis() { } } +void GameLogo::updateCoffeeCrisis(float deltaTime) { + switch (coffee_crisis_status_) { + case Status::MOVING: + handleCoffeeCrisisMoving(deltaTime); + break; + case Status::SHAKING: + handleCoffeeCrisisShaking(deltaTime); + break; + case Status::FINISHED: + handleCoffeeCrisisFinished(deltaTime); + break; + default: + break; + } +} + void GameLogo::updateArcadeEdition() { switch (arcade_edition_status_) { case Status::MOVING: @@ -148,6 +172,19 @@ void GameLogo::updateArcadeEdition() { } } +void GameLogo::updateArcadeEdition(float deltaTime) { + switch (arcade_edition_status_) { + case Status::MOVING: + handleArcadeEditionMoving(deltaTime); + break; + case Status::SHAKING: + handleArcadeEditionShaking(deltaTime); + break; + default: + break; + } +} + void GameLogo::handleCoffeeCrisisMoving() { coffee_sprite_->update(); crisis_sprite_->update(); @@ -158,6 +195,16 @@ void GameLogo::handleCoffeeCrisisMoving() { } } +void GameLogo::handleCoffeeCrisisMoving(float deltaTime) { + coffee_sprite_->update(deltaTime); + crisis_sprite_->update(deltaTime); + + if (coffee_sprite_->hasFinished() && crisis_sprite_->hasFinished()) { + coffee_crisis_status_ = Status::SHAKING; + playTitleEffects(); + } +} + void GameLogo::handleCoffeeCrisisShaking() { if (shake_.remaining > 0) { processShakeEffect(coffee_sprite_.get(), crisis_sprite_.get()); @@ -168,10 +215,24 @@ void GameLogo::handleCoffeeCrisisShaking() { updateDustSprites(); } +void GameLogo::handleCoffeeCrisisShaking(float deltaTime) { + if (shake_.remaining > 0) { + processShakeEffect(coffee_sprite_.get(), crisis_sprite_.get(), deltaTime); + } else { + finishCoffeeCrisisShaking(); + } + + updateDustSprites(deltaTime); +} + void GameLogo::handleCoffeeCrisisFinished() { updateDustSprites(); } +void GameLogo::handleCoffeeCrisisFinished(float deltaTime) { + updateDustSprites(deltaTime); +} + void GameLogo::handleArcadeEditionMoving() { zoom_ -= 0.1F * ZOOM_FACTOR; arcade_edition_sprite_->setZoom(zoom_); @@ -181,6 +242,16 @@ void GameLogo::handleArcadeEditionMoving() { } } +void GameLogo::handleArcadeEditionMoving(float deltaTime) { + // Convertir 0.1F * ZOOM_FACTOR por frame a por segundo (asumiendo 60fps) + zoom_ -= (0.1F * ZOOM_FACTOR * 60.0F) * deltaTime; + arcade_edition_sprite_->setZoom(zoom_); + + if (zoom_ <= 1.0F) { + finishArcadeEditionMoving(); + } +} + void GameLogo::handleArcadeEditionShaking() { if (shake_.remaining > 0) { processArcadeEditionShake(); @@ -190,6 +261,15 @@ void GameLogo::handleArcadeEditionShaking() { } } +void GameLogo::handleArcadeEditionShaking(float deltaTime) { + if (shake_.remaining > 0) { + processArcadeEditionShake(deltaTime); + } else { + arcade_edition_sprite_->setX(shake_.origin); + arcade_edition_status_ = Status::FINISHED; + } +} + void GameLogo::processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite) { if (shake_.counter > 0) { shake_.counter--; @@ -204,6 +284,23 @@ void GameLogo::processShakeEffect(SmartSprite* primary_sprite, SmartSprite* seco } } +void GameLogo::processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite, float deltaTime) { + // Convertir delay (frames) a tiempo: delay frames = delay/60 segundos a 60fps + float delayTime = static_cast(shake_.delay) / 60.0f; + + shake_.time_accumulator += deltaTime; + + if (shake_.time_accumulator >= delayTime) { + shake_.time_accumulator -= delayTime; + const auto DISPLACEMENT = calculateShakeDisplacement(); + primary_sprite->setPosX(shake_.origin + DISPLACEMENT); + if (secondary_sprite != nullptr) { + secondary_sprite->setPosX(shake_.origin + DISPLACEMENT + 15); + } + shake_.remaining--; + } +} + void GameLogo::processArcadeEditionShake() { if (shake_.counter > 0) { shake_.counter--; @@ -215,6 +312,20 @@ void GameLogo::processArcadeEditionShake() { } } +void GameLogo::processArcadeEditionShake(float deltaTime) { + // Convertir delay (frames) a tiempo: delay frames = delay/60 segundos a 60fps + float delayTime = static_cast(shake_.delay) / 60.0f; + + shake_.time_accumulator += deltaTime; + + if (shake_.time_accumulator >= delayTime) { + shake_.time_accumulator -= delayTime; + const auto DISPLACEMENT = calculateShakeDisplacement(); + arcade_edition_sprite_->setX(shake_.origin + DISPLACEMENT); + shake_.remaining--; + } +} + auto GameLogo::calculateShakeDisplacement() const -> int { return shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp; } @@ -245,6 +356,11 @@ void GameLogo::updateDustSprites() { dust_left_sprite_->update(); } +void GameLogo::updateDustSprites(float deltaTime) { + dust_right_sprite_->update(deltaTime); + dust_left_sprite_->update(deltaTime); +} + void GameLogo::updatePostFinishedCounter() { if (coffee_crisis_status_ == Status::FINISHED && arcade_edition_status_ == Status::FINISHED && @@ -253,6 +369,23 @@ void GameLogo::updatePostFinishedCounter() { } } +void GameLogo::updatePostFinishedCounter(float deltaTime) { + if (coffee_crisis_status_ == Status::FINISHED && + arcade_edition_status_ == Status::FINISHED && + post_finished_counter_ > 0) { + + // Convertir 1 frame a tiempo: 1 frame = 1/60 segundos a 60fps + float frameTime = 1.0f / 60.0f; + + post_finished_time_accumulator_ += deltaTime; + + if (post_finished_time_accumulator_ >= frameTime) { + post_finished_time_accumulator_ -= frameTime; + --post_finished_counter_; + } + } +} + // Activa la clase void GameLogo::enable() { init(); diff --git a/source/game_logo.h b/source/game_logo.h index 07e8563..bbd1d3e 100644 --- a/source/game_logo.h +++ b/source/game_logo.h @@ -16,9 +16,10 @@ class GameLogo { ~GameLogo() = default; // --- Métodos principales --- - void render(); // Pinta la clase en pantalla - void update(); // Actualiza la lógica de la clase - void enable(); // Activa la clase + void render(); // Pinta la clase en pantalla + void update(); // Actualiza la lógica de la clase (frame-based) + void update(float deltaTime); // Actualiza la lógica de la clase (time-based) + void enable(); // Activa la clase // --- Getters --- [[nodiscard]] auto hasFinished() const -> bool; // Indica si ha terminado la animación @@ -34,12 +35,13 @@ class GameLogo { // --- Estructuras privadas --- struct Shake { - int desp = 1; // Pixels de desplazamiento para agitar la pantalla en el eje x - int delay = 2; // Retraso entre cada desplazamiento de la pantalla al agitarse - int length = 8; // Cantidad de desplazamientos a realizar - int remaining = length; // Cantidad de desplazamientos pendientes a realizar - int counter = delay; // Contador para el retraso - int origin = 0; // Valor inicial de la pantalla para dejarla igual tras el desplazamiento + int desp = 1; // Pixels de desplazamiento para agitar la pantalla en el eje x + int delay = 2; // Retraso entre cada desplazamiento de la pantalla al agitarse (frame-based) + int length = 8; // Cantidad de desplazamientos a realizar + int remaining = length; // Cantidad de desplazamientos pendientes a realizar + int counter = delay; // Contador para el retraso (frame-based) + float time_accumulator = 0.0f; // Acumulador de tiempo para deltaTime + int origin = 0; // Valor inicial de la pantalla para dejarla igual tras el desplazamiento Shake() = default; Shake(int d, int de, int l, int o) @@ -56,6 +58,7 @@ class GameLogo { length = l; remaining = l; counter = de; + time_accumulator = 0.0f; origin = o; } }; @@ -79,32 +82,44 @@ class GameLogo { float x_; // Posición X del logo float y_; // Posición Y del logo float zoom_ = 1.0F; // Zoom aplicado al texto "ARCADE EDITION" - int post_finished_counter_ = 1; // Contador final tras animaciones + int post_finished_counter_ = 1; // Contador final tras animaciones (frame-based) + float post_finished_time_accumulator_ = 0.0f; // Acumulador de tiempo para post_finished_counter // --- Inicialización --- void init(); // Inicializa las variables [[nodiscard]] auto getInitialVerticalDesp() const -> int; // Calcula el desplazamiento vertical inicial // --- Actualización de estados específicos --- - void updateCoffeeCrisis(); // Actualiza el estado de "Coffee Crisis" - void updateArcadeEdition(); // Actualiza el estado de "Arcade Edition" - void updatePostFinishedCounter(); // Actualiza el contador tras finalizar una animación + void updateCoffeeCrisis(); // Actualiza el estado de "Coffee Crisis" (frame-based) + void updateCoffeeCrisis(float deltaTime); // Actualiza el estado de "Coffee Crisis" (time-based) + void updateArcadeEdition(); // Actualiza el estado de "Arcade Edition" (frame-based) + void updateArcadeEdition(float deltaTime); // Actualiza el estado de "Arcade Edition" (time-based) + void updatePostFinishedCounter(); // Actualiza el contador tras finalizar una animación (frame-based) + void updatePostFinishedCounter(float deltaTime); // Actualiza el contador tras finalizar una animación (time-based) // --- Efectos visuales: movimiento y sacudidas --- - void handleCoffeeCrisisMoving(); // Maneja el movimiento de "Coffee Crisis" - void handleCoffeeCrisisShaking(); // Maneja la sacudida de "Coffee Crisis" - void handleArcadeEditionMoving(); // Maneja el movimiento de "Arcade Edition" - void handleArcadeEditionShaking(); // Maneja la sacudida de "Arcade Edition" - void processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite = nullptr); // Procesa el efecto de sacudida en sprites - void processArcadeEditionShake(); // Procesa la sacudida específica de "Arcade Edition" - [[nodiscard]] auto calculateShakeDisplacement() const -> int; // Calcula el desplazamiento de la sacudida + void handleCoffeeCrisisMoving(); // Maneja el movimiento de "Coffee Crisis" (frame-based) + void handleCoffeeCrisisMoving(float deltaTime); // Maneja el movimiento de "Coffee Crisis" (time-based) + void handleCoffeeCrisisShaking(); // Maneja la sacudida de "Coffee Crisis" (frame-based) + void handleCoffeeCrisisShaking(float deltaTime); // Maneja la sacudida de "Coffee Crisis" (time-based) + void handleArcadeEditionMoving(); // Maneja el movimiento de "Arcade Edition" (frame-based) + void handleArcadeEditionMoving(float deltaTime); // Maneja el movimiento de "Arcade Edition" (time-based) + void handleArcadeEditionShaking(); // Maneja la sacudida de "Arcade Edition" (frame-based) + void handleArcadeEditionShaking(float deltaTime); // Maneja la sacudida de "Arcade Edition" (time-based) + void processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite = nullptr); // Procesa el efecto de sacudida en sprites (frame-based) + void processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite, float deltaTime); // Procesa el efecto de sacudida en sprites (time-based) + void processArcadeEditionShake(); // Procesa la sacudida específica de "Arcade Edition" (frame-based) + void processArcadeEditionShake(float deltaTime); // Procesa la sacudida específica de "Arcade Edition" (time-based) + [[nodiscard]] auto calculateShakeDisplacement() const -> int; // Calcula el desplazamiento de la sacudida // --- Gestión de finalización de efectos --- - void handleCoffeeCrisisFinished(); // Maneja el final de la animación "Coffee Crisis" - void finishCoffeeCrisisShaking(); // Finaliza la sacudida de "Coffee Crisis" - void finishArcadeEditionMoving(); // Finaliza el movimiento de "Arcade Edition" + void handleCoffeeCrisisFinished(); // Maneja el final de la animación "Coffee Crisis" (frame-based) + void handleCoffeeCrisisFinished(float deltaTime); // Maneja el final de la animación "Coffee Crisis" (time-based) + void finishCoffeeCrisisShaking(); // Finaliza la sacudida de "Coffee Crisis" + void finishArcadeEditionMoving(); // Finaliza el movimiento de "Arcade Edition" // --- Utilidades --- - static void playTitleEffects(); // Reproduce efectos visuales/sonoros del título - void updateDustSprites(); // Actualiza los sprites de polvo + static void playTitleEffects(); // Reproduce efectos visuales/sonoros del título + void updateDustSprites(); // Actualiza los sprites de polvo (frame-based) + void updateDustSprites(float deltaTime); // Actualiza los sprites de polvo (time-based) }; \ No newline at end of file diff --git a/source/smart_sprite.cpp b/source/smart_sprite.cpp index 471121a..ec351cc 100644 --- a/source/smart_sprite.cpp +++ b/source/smart_sprite.cpp @@ -2,7 +2,7 @@ #include "moving_sprite.h" // Para MovingSprite -// Actualiza la posición y comprueba si ha llegado a su destino +// Actualiza la posición y comprueba si ha llegado a su destino (frame-based) void SmartSprite::update() { if (enabled_) { MovingSprite::update(); @@ -11,6 +11,15 @@ void SmartSprite::update() { } } +// Actualiza la posición y comprueba si ha llegado a su destino (time-based) +void SmartSprite::update(float deltaTime) { + if (enabled_) { + MovingSprite::update(deltaTime); + checkMove(); + checkFinished(); + } +} + // Dibuja el sprite void SmartSprite::render() { if (enabled_) { diff --git a/source/smart_sprite.h b/source/smart_sprite.h index 347b6ee..07cefc2 100644 --- a/source/smart_sprite.h +++ b/source/smart_sprite.h @@ -16,8 +16,9 @@ class SmartSprite : public AnimatedSprite { ~SmartSprite() override = default; // --- Métodos principales --- - void update() override; // Actualiza la posición y comprueba si ha llegado a su destino - void render() override; // Dibuja el sprite + void update() override; // Actualiza la posición y comprueba si ha llegado a su destino (frame-based) + void update(float deltaTime) override; // Actualiza la posición y comprueba si ha llegado a su destino (time-based) + void render() override; // Dibuja el sprite // --- Getters --- auto getDestX() const -> int { return dest_x_; } // Obtiene la posición de destino en X