#include "game_logo.h" #include // Para SDL_SetTextureScaleMode, SDL_FlipMode, SDL_ScaleMode #include // 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 float FLASH_DELAY_S = 0.05f; // 3 frames → 0.05s constexpr float FLASH_DURATION_S = 0.1f; // 6 frames → 0.1s (3 + 3) constexpr Color FLASH_COLOR = Color(0xFF, 0xFF, 0xFF); // Color blanco para el flash // 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(dust_texture_, Resource::get()->getAnimation("title_dust.ani"))), dust_right_sprite_(std::make_unique(dust_texture_, Resource::get()->getAnimation("title_dust.ani"))), coffee_sprite_(std::make_unique(coffee_texture_)), crisis_sprite_(std::make_unique(crisis_texture_)), arcade_edition_sprite_(std::make_unique(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_timer_ = 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(COFFEE_VEL_Y); coffee_sprite_->setAccelX(0.0F); coffee_sprite_->setAccelY(COFFEE_ACCEL_Y); coffee_sprite_->setSpriteClip(0, 0, coffee_texture_->getWidth(), coffee_texture_->getHeight()); coffee_sprite_->setEnabled(true); coffee_sprite_->setFinishedDelay(0.0f); coffee_sprite_->setDestX(XP); coffee_sprite_->setDestY(y_ - coffee_texture_->getHeight()); // Inicializa el bitmap de 'Crisis' crisis_sprite_->setPosX(XP + CRISIS_OFFSET_X); 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(CRISIS_VEL_Y); crisis_sprite_->setAccelX(0.0F); crisis_sprite_->setAccelY(CRISIS_ACCEL_Y); crisis_sprite_->setSpriteClip(0, 0, crisis_texture_->getWidth(), crisis_texture_->getHeight()); crisis_sprite_->setEnabled(true); crisis_sprite_->setFinishedDelay(0.0f); crisis_sprite_->setDestX(XP + CRISIS_OFFSET_X); 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(DUST_SIZE); dust_right_sprite_->setHeight(DUST_SIZE); dust_right_sprite_->setFlip(SDL_FLIP_HORIZONTAL); // Inicializa el bitmap de 'DustLeft' dust_left_sprite_->resetAnimation(); dust_left_sprite_->setPosX(coffee_sprite_->getPosX() - DUST_SIZE); dust_left_sprite_->setPosY(y_); dust_left_sprite_->setWidth(DUST_SIZE); dust_left_sprite_->setHeight(DUST_SIZE); // 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 (time-based) void GameLogo::update(float deltaTime) { updateCoffeeCrisis(deltaTime); updateArcadeEdition(deltaTime); updatePostFinishedCounter(deltaTime); } 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(float deltaTime) { switch (arcade_edition_status_) { case Status::MOVING: handleArcadeEditionMoving(deltaTime); break; case Status::SHAKING: handleArcadeEditionShaking(deltaTime); break; default: break; } } 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(float deltaTime) { if (shake_.remaining > 0) { processShakeEffect(coffee_sprite_.get(), crisis_sprite_.get(), deltaTime); } else { finishCoffeeCrisisShaking(); } updateDustSprites(deltaTime); } void GameLogo::handleCoffeeCrisisFinished(float deltaTime) { updateDustSprites(deltaTime); } void GameLogo::handleArcadeEditionMoving(float deltaTime) { // DeltaTime en segundos: decremento por segundo zoom_ -= (ZOOM_DECREMENT_PER_S * ZOOM_FACTOR) * deltaTime; arcade_edition_sprite_->setZoom(zoom_); if (zoom_ <= 1.0F) { finishArcadeEditionMoving(); } } 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, float deltaTime) { shake_.time_accumulator += deltaTime; if (shake_.time_accumulator >= SHAKE_DELAY_S) { shake_.time_accumulator -= SHAKE_DELAY_S; const auto DISPLACEMENT = calculateShakeDisplacement(); primary_sprite->setPosX(shake_.origin + DISPLACEMENT); if (secondary_sprite != nullptr) { secondary_sprite->setPosX(shake_.origin + DISPLACEMENT + CRISIS_OFFSET_X); } shake_.remaining--; } } void GameLogo::processArcadeEditionShake(float deltaTime) { // Delay fijo en segundos (shake_.delay era frames, ahora usamos constante) float delayTime = SHAKE_DELAY_S; 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 + CRISIS_OFFSET_X); 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(FLASH_COLOR, FLASH_DURATION_S, FLASH_DELAY_S); Screen::get()->shake(); } void GameLogo::updateDustSprites(float deltaTime) { dust_right_sprite_->update(deltaTime); dust_left_sprite_->update(deltaTime); } void GameLogo::updatePostFinishedCounter(float deltaTime) { if (coffee_crisis_status_ == Status::FINISHED && arcade_edition_status_ == Status::FINISHED) { post_finished_timer_ += deltaTime; } } // 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_timer_ >= post_finished_delay_s_; } // 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); }