// IWYU pragma: no_include #include "tabe.hpp" #include // Para SDL_FlipMode, SDL_GetTicks #include // Para max #include // Para array #include // Para rand, abs #include // Para basic_string #include "audio.hpp" // Para Audio #include "param.hpp" // Para Param, param, ParamGame, ParamTabe #include "resource.hpp" // Para Resource #include "utils.hpp" // Para Zone // Constructor Tabe::Tabe() : sprite_(std::make_unique(Resource::get()->getTexture("tabe.png"), Resource::get()->getAnimation("tabe.ani"))), timer_(Timer(param.tabe.min_spawn_time, param.tabe.max_spawn_time)) {} // Actualiza la lógica (time-based) void Tabe::update(float delta_time) { if (enabled_ && !timer_.is_paused) { sprite_->update(delta_time); move(delta_time); updateState(delta_time); } timer_.update(); if (timer_.shouldSpawn()) { enable(); } } // Dibuja el objeto void Tabe::render() { if (enabled_) { sprite_->render(); } } // Mueve el objeto (time-based) void Tabe::move(float delta_time) { const int X = static_cast(x_); speed_ += accel_ * delta_time; x_ += speed_ * delta_time; fly_distance_ -= std::abs(X - static_cast(x_)); // Comprueba si sale por los bordes const float MIN_X = param.game.game_area.rect.x - WIDTH; const float MAX_X = param.game.game_area.rect.x + param.game.game_area.rect.w; switch (destiny_) { case Direction::TO_THE_LEFT: { if (x_ < MIN_X) { disable(); } if (x_ > param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH && direction_ == Direction::TO_THE_RIGHT) { setRandomFlyPath(Direction::TO_THE_LEFT, 80); x_ = param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH; } break; } case Direction::TO_THE_RIGHT: { if (x_ > MAX_X) { disable(); } if (x_ < param.game.game_area.rect.x && direction_ == Direction::TO_THE_LEFT) { setRandomFlyPath(Direction::TO_THE_RIGHT, 80); x_ = param.game.game_area.rect.x; } break; } default: break; } if (fly_distance_ <= 0) { if (waiting_counter_ > 0) { accel_ = speed_ = 0.0F; waiting_counter_ -= delta_time; waiting_counter_ = std::max(waiting_counter_, 0); } else { constexpr int CHOICES = 4; const std::array LEFT = { Direction::TO_THE_LEFT, Direction::TO_THE_LEFT, Direction::TO_THE_LEFT, Direction::TO_THE_RIGHT}; const std::array RIGHT = { Direction::TO_THE_LEFT, Direction::TO_THE_RIGHT, Direction::TO_THE_RIGHT, Direction::TO_THE_RIGHT}; const Direction DIRECTION = destiny_ == Direction::TO_THE_LEFT ? LEFT[rand() % CHOICES] : RIGHT[rand() % CHOICES]; setRandomFlyPath(DIRECTION, 20 + (rand() % 40)); } } shiftSprite(); } // Habilita el objeto void Tabe::enable() { if (!enabled_) { enabled_ = true; has_bonus_ = true; hit_counter_ = 0; number_of_hits_ = 0; y_ = param.game.game_area.rect.y + 20.0F; // Establece una dirección aleatoria destiny_ = direction_ = rand() % 2 == 0 ? Direction::TO_THE_LEFT : Direction::TO_THE_RIGHT; // Establece la posición inicial x_ = (direction_ == Direction::TO_THE_LEFT) ? param.game.game_area.rect.x + param.game.game_area.rect.w : param.game.game_area.rect.x - WIDTH; // Crea una ruta de vuelo setRandomFlyPath(direction_, 60); shiftSprite(); } } // Establece un vuelo aleatorio void Tabe::setRandomFlyPath(Direction direction, int length) { direction_ = direction; fly_distance_ = length; waiting_counter_ = 0.083F + (rand() % 15) * 0.0167F; // 5-20 frames converted to seconds (5/60 to 20/60) Audio::get()->playSound("tabe.wav"); constexpr float SPEED = 120.0F; // 2 pixels/frame * 60fps = 120 pixels/second switch (direction) { case Direction::TO_THE_LEFT: { speed_ = -1.0F * SPEED; accel_ = -1.0F * (1 + rand() % 10) * 2.0F; // Converted from frame-based to seconds sprite_->setFlip(SDL_FLIP_NONE); break; } case Direction::TO_THE_RIGHT: { speed_ = SPEED; accel_ = (1 + rand() % 10) * 2.0F; // Converted from frame-based to seconds sprite_->setFlip(SDL_FLIP_HORIZONTAL); break; } default: break; } } // Establece el estado void Tabe::setState(State state) { if (enabled_) { state_ = state; switch (state) { case State::FLY: sprite_->setCurrentAnimation("fly"); break; case State::HIT: sprite_->setCurrentAnimation("hit"); hit_counter_ = 0.083F; // 5 frames converted to seconds (5/60) ++number_of_hits_; break; default: break; } } } // Actualiza el estado (time-based) void Tabe::updateState(float delta_time) { if (state_ == State::HIT) { hit_counter_ -= delta_time; if (hit_counter_ <= 0) { setState(State::FLY); } } } // Intenta obtener el bonus auto Tabe::tryToGetBonus() -> bool { if (has_bonus_ && rand() % std::max(1, 15 - number_of_hits_) == 0) { has_bonus_ = false; return true; } return false; } // Actualiza el temporizador void Tabe::updateTimer() { timer_.current_time = SDL_GetTicks(); timer_.delta_time = timer_.current_time - timer_.last_time; timer_.last_time = timer_.current_time; } // Deshabilita el objeto void Tabe::disable() { enabled_ = false; timer_.reset(); } // Detiene/activa el timer void Tabe::pauseTimer(bool value) { timer_.setPaused(value); } // Deshabilita el spawning permanentemente void Tabe::disableSpawning() { timer_.setSpawnDisabled(true); } // Habilita el spawning nuevamente void Tabe::enableSpawning() { timer_.setSpawnDisabled(false); }