Files
coffee_crisis_arcade_edition/source/game_logo.cpp
Sergio Valor dfa66b0e95 delta-time: game_logo.cpp
delta-time: smart_sprite.cpp
2025-09-16 17:19:05 +02:00

406 lines
13 KiB
C++

#include "game_logo.h"
#include <SDL3/SDL.h> // Para SDL_SetTextureScaleMode, SDL_FlipMode, SDL_ScaleMode
#include <algorithm> // Para max
#include "animated_sprite.h" // Para AnimatedSprite
#include "audio.h" // Para Audio
#include "color.h" // Para Color
#include "param.h" // Para Param, param, ParamGame, ParamTitle
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "smart_sprite.h" // Para SmartSprite
#include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture
constexpr int ZOOM_FACTOR = 5;
constexpr int FLASH_DELAY = 3;
constexpr int FLASH_LENGTH = FLASH_DELAY + 3;
// Constructor
GameLogo::GameLogo(int x, int y)
: dust_texture_(Resource::get()->getTexture("title_dust.png")),
coffee_texture_(Resource::get()->getTexture("title_coffee.png")),
crisis_texture_(Resource::get()->getTexture("title_crisis.png")),
arcade_edition_texture_(Resource::get()->getTexture("title_arcade_edition.png")),
dust_left_sprite_(std::make_unique<AnimatedSprite>(dust_texture_, Resource::get()->getAnimation("title_dust.ani"))),
dust_right_sprite_(std::make_unique<AnimatedSprite>(dust_texture_, Resource::get()->getAnimation("title_dust.ani"))),
coffee_sprite_(std::make_unique<SmartSprite>(coffee_texture_)),
crisis_sprite_(std::make_unique<SmartSprite>(crisis_texture_)),
arcade_edition_sprite_(std::make_unique<Sprite>(arcade_edition_texture_, (param.game.width - arcade_edition_texture_->getWidth()) / 2, param.title.arcade_edition_position, arcade_edition_texture_->getWidth(), arcade_edition_texture_->getHeight())),
x_(x),
y_(y) {}
// Inicializa las variables
void GameLogo::init() {
const auto XP = x_ - (coffee_texture_->getWidth() / 2);
const auto DESP = getInitialVerticalDesp();
// Configura texturas
SDL_SetTextureScaleMode(Resource::get()->getTexture("title_arcade_edition.png")->getSDLTexture(), SDL_SCALEMODE_NEAREST);
// Variables
coffee_crisis_status_ = Status::DISABLED;
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);
coffee_sprite_->setPosY(y_ - coffee_texture_->getHeight() - DESP);
coffee_sprite_->setWidth(coffee_texture_->getWidth());
coffee_sprite_->setHeight(coffee_texture_->getHeight());
coffee_sprite_->setVelX(0.0F);
coffee_sprite_->setVelY(2.5F);
coffee_sprite_->setAccelX(0.0F);
coffee_sprite_->setAccelY(0.1F);
coffee_sprite_->setSpriteClip(0, 0, coffee_texture_->getWidth(), coffee_texture_->getHeight());
coffee_sprite_->setEnabled(true);
coffee_sprite_->setFinishedCounter(0);
coffee_sprite_->setDestX(XP);
coffee_sprite_->setDestY(y_ - coffee_texture_->getHeight());
// Inicializa el bitmap de 'Crisis'
crisis_sprite_->setPosX(XP + 15);
crisis_sprite_->setPosY(y_ + DESP);
crisis_sprite_->setWidth(crisis_texture_->getWidth());
crisis_sprite_->setHeight(crisis_texture_->getHeight());
crisis_sprite_->setVelX(0.0F);
crisis_sprite_->setVelY(-2.5F);
crisis_sprite_->setAccelX(0.0F);
crisis_sprite_->setAccelY(-0.1F);
crisis_sprite_->setSpriteClip(0, 0, crisis_texture_->getWidth(), crisis_texture_->getHeight());
crisis_sprite_->setEnabled(true);
crisis_sprite_->setFinishedCounter(0);
crisis_sprite_->setDestX(XP + 15);
crisis_sprite_->setDestY(y_);
// Inicializa el bitmap de 'DustRight'
dust_right_sprite_->resetAnimation();
dust_right_sprite_->setPosX(coffee_sprite_->getPosX() + coffee_sprite_->getWidth());
dust_right_sprite_->setPosY(y_);
dust_right_sprite_->setWidth(16);
dust_right_sprite_->setHeight(16);
dust_right_sprite_->setFlip(SDL_FLIP_HORIZONTAL);
// Inicializa el bitmap de 'DustLeft'
dust_left_sprite_->resetAnimation();
dust_left_sprite_->setPosX(coffee_sprite_->getPosX() - 16);
dust_left_sprite_->setPosY(y_);
dust_left_sprite_->setWidth(16);
dust_left_sprite_->setHeight(16);
// Inicializa el bitmap de 'Arcade Edition'
arcade_edition_sprite_->setZoom(zoom_);
}
// Pinta la clase en pantalla
void GameLogo::render() {
// Dibuja el logo
coffee_sprite_->render();
crisis_sprite_->render();
if (arcade_edition_status_ != Status::DISABLED) {
arcade_edition_sprite_->render();
}
// Dibuja el polvillo del logo
if (coffee_crisis_status_ != Status::MOVING) {
dust_right_sprite_->render();
dust_left_sprite_->render();
}
}
// 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:
handleCoffeeCrisisMoving();
break;
case Status::SHAKING:
handleCoffeeCrisisShaking();
break;
case Status::FINISHED:
handleCoffeeCrisisFinished();
break;
default:
break;
}
}
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:
handleArcadeEditionMoving();
break;
case Status::SHAKING:
handleArcadeEditionShaking();
break;
default:
break;
}
}
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();
if (coffee_sprite_->hasFinished() && crisis_sprite_->hasFinished()) {
coffee_crisis_status_ = Status::SHAKING;
playTitleEffects();
}
}
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());
} else {
finishCoffeeCrisisShaking();
}
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_);
if (zoom_ <= 1.0F) {
finishArcadeEditionMoving();
}
}
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();
} else {
arcade_edition_sprite_->setX(shake_.origin);
arcade_edition_status_ = Status::FINISHED;
}
}
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--;
} else {
shake_.counter = shake_.delay;
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::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<float>(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--;
} else {
shake_.counter = shake_.delay;
const auto DISPLACEMENT = calculateShakeDisplacement();
arcade_edition_sprite_->setX(shake_.origin + DISPLACEMENT);
shake_.remaining--;
}
}
void GameLogo::processArcadeEditionShake(float deltaTime) {
// Convertir delay (frames) a tiempo: delay frames = delay/60 segundos a 60fps
float delayTime = static_cast<float>(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;
}
void GameLogo::finishCoffeeCrisisShaking() {
coffee_sprite_->setPosX(shake_.origin);
crisis_sprite_->setPosX(shake_.origin + 15);
coffee_crisis_status_ = Status::FINISHED;
arcade_edition_status_ = Status::MOVING;
}
void GameLogo::finishArcadeEditionMoving() {
arcade_edition_status_ = Status::SHAKING;
zoom_ = 1.0F;
arcade_edition_sprite_->setZoom(zoom_);
shake_.init(1, 2, 8, arcade_edition_sprite_->getX());
playTitleEffects();
}
void GameLogo::playTitleEffects() {
Audio::get()->playSound("title.wav");
Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGTH, FLASH_DELAY);
Screen::get()->shake();
}
void GameLogo::updateDustSprites() {
dust_right_sprite_->update();
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 &&
post_finished_counter_ > 0) {
--post_finished_counter_;
}
}
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();
coffee_crisis_status_ = Status::MOVING;
}
// Indica si ha terminado la animación
auto GameLogo::hasFinished() const -> bool {
return post_finished_counter_ == 0;
}
// Calcula el desplazamiento vertical inicial
auto GameLogo::getInitialVerticalDesp() const -> int {
const float OFFSET_UP = y_;
const float OFFSET_DOWN = param.game.height - y_;
return std::max(OFFSET_UP, OFFSET_DOWN);
}