#include "item.hpp" #include // Para clamp #include // Para fmod #include // Para rand #include "animated_sprite.hpp" // Para AnimatedSprite #include "param.hpp" // Para Param, ParamGame, param class Texture; // lines 6-6 Item::Item(ItemType type, float x, float y, SDL_FRect& play_area, const std::shared_ptr& texture, const std::vector& animation) : sprite_(std::make_unique(texture, animation)), play_area_(play_area), type_(type) { switch (type) { case ItemType::COFFEE_MACHINE: { width_ = COFFEE_MACHINE_WIDTH; height_ = COFFEE_MACHINE_HEIGHT; pos_x_ = getCoffeeMachineSpawn(x, width_, play_area_.w); pos_y_ = y; vel_x_ = ((rand() % 3) - 1) * COFFEE_MACHINE_VEL_X_FACTOR; vel_y_ = COFFEE_MACHINE_VEL_Y; accel_y_ = COFFEE_MACHINE_ACCEL_Y; collider_.r = 10; break; } default: { pos_x_ = x; pos_y_ = y; // 6 velocidades: 3 negativas (-1.0, -0.66, -0.33) y 3 positivas (0.33, 0.66, 1.0) const int DIRECTION = rand() % 6; if (DIRECTION < 3) { // Velocidades negativas: -1.0, -0.66, -0.33 vel_x_ = -ITEM_VEL_X_BASE + (DIRECTION * ITEM_VEL_X_STEP); rotate_speed_ = -720.0F; } else { // Velocidades positivas: 0.33, 0.66, 1.0 vel_x_ = ITEM_VEL_X_STEP + ((DIRECTION - 3) * ITEM_VEL_X_STEP); rotate_speed_ = 720.0F; } vel_y_ = ITEM_VEL_Y; accel_y_ = ITEM_ACCEL_Y; collider_.r = width_ / 2; sprite_->startRotate(); sprite_->setRotateAmount(rotate_speed_); sprite_->setCurrentAnimation("no-blink"); break; } } // Actualiza el sprite shiftSprite(); shiftColliders(); } void Item::alignTo(int x) { const float MIN_X = param.game.play_area.rect.x + 1; const float MAX_X = play_area_.w - width_ - 1; pos_x_ = x - (width_ / 2); // Ajusta para que no quede fuera de la zona de juego pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); // Actualiza el sprite shiftSprite(); shiftColliders(); } void Item::render() { if (enabled_) { // Muestra normalmente hasta los últimos ~3.3 segundos constexpr float BLINK_START_S = LIFETIME_DURATION_S - 3.33F; if (lifetime_timer_ < BLINK_START_S) { sprite_->render(); } else { // Efecto de parpadeo en los últimos segundos (cada ~0.33 segundos) constexpr float BLINK_INTERVAL_S = 0.33F; const float PHASE = std::fmod(lifetime_timer_, BLINK_INTERVAL_S); const float HALF_INTERVAL = BLINK_INTERVAL_S / 2.0F; if (PHASE < HALF_INTERVAL) { sprite_->render(); } } } } void Item::move(float delta_time) { floor_collision_ = false; // Calcula la nueva posición usando deltaTime (velocidad en pixels/segundo) pos_x_ += vel_x_ * delta_time; pos_y_ += vel_y_ * delta_time; // Aplica las aceleraciones a la velocidad usando deltaTime (aceleración en pixels/segundo²) vel_x_ += accel_x_ * delta_time; vel_y_ += accel_y_ * delta_time; // Comprueba los laterales de la zona de juego const float MIN_X = param.game.play_area.rect.x; const float MAX_X = play_area_.w - width_; pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); // Si toca el borde lateral if (pos_x_ == MIN_X || pos_x_ == MAX_X) { vel_x_ = -vel_x_; // Invierte la velocidad horizontal sprite_->scaleRotateAmount(-1.0F); // Invierte la rotación } // Si colisiona por arriba, rebota (excepto la máquina de café) if ((pos_y_ < param.game.play_area.rect.y) && !(type_ == ItemType::COFFEE_MACHINE)) { // Corrige pos_y_ = param.game.play_area.rect.y; // Fuerza la velocidad hacia abajo para evitar oscilaciones vel_y_ = std::abs(vel_y_); } // Si colisiona con la parte inferior if (pos_y_ > play_area_.h - height_) { pos_y_ = play_area_.h - height_; // Corrige la posición sprite_->scaleRotateAmount(0.5F); // Reduce la rotación sprite_->stopRotate(300.0F); // Detiene la rotacion switch (type_) { case ItemType::COFFEE_MACHINE: // La máquina de café es mas pesada y tiene una fisica diferente, ademas hace ruido floor_collision_ = true; if (std::abs(vel_y_) < BOUNCE_VEL_THRESHOLD) { // Si la velocidad vertical es baja, detiene el objeto vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0; } else { // Si la velocidad vertical es alta, el objeto rebota y pierde velocidad vel_y_ *= COFFEE_BOUNCE_DAMPING; vel_x_ *= HORIZONTAL_DAMPING; } break; default: // Si no es una máquina de café if (std::abs(vel_y_) < BOUNCE_VEL_THRESHOLD) { // Si la velocidad vertical es baja, detiene el objeto vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0; sprite_->setCurrentAnimation("blink"); } else { // Si la velocidad vertical es alta, el objeto rebota y pierde velocidad vel_y_ *= ITEM_BOUNCE_DAMPING; vel_x_ *= HORIZONTAL_DAMPING; } break; } } // Actualiza la posición del sprite shiftSprite(); shiftColliders(); } void Item::disable() { enabled_ = false; } void Item::update(float delta_time) { move(delta_time); sprite_->update(delta_time); updateTimeToLive(delta_time); } void Item::updateTimeToLive(float delta_time) { lifetime_timer_ += delta_time; if (lifetime_timer_ >= LIFETIME_DURATION_S) { disable(); } } void Item::shiftColliders() { collider_.x = static_cast(pos_x_ + (width_ / 2)); collider_.y = static_cast(pos_y_ + (height_ / 2)); } void Item::shiftSprite() { sprite_->setPosX(pos_x_); sprite_->setPosY(pos_y_); } // Calcula la zona de aparición de la máquina de café auto Item::getCoffeeMachineSpawn(int player_x, int item_width, int area_width, int margin) -> int { // Distancia mínima del jugador (ajusta según necesites) const int MIN_DISTANCE_FROM_PLAYER = area_width / 2; const int LEFT_BOUND = margin; const int RIGHT_BOUND = area_width - item_width - margin; // Calcular zona de exclusión alrededor del jugador int exclude_left = player_x - MIN_DISTANCE_FROM_PLAYER; int exclude_right = player_x + MIN_DISTANCE_FROM_PLAYER; // Verificar si hay espacio suficiente a la izquierda bool can_spawn_left = (exclude_left > LEFT_BOUND) && (exclude_left - LEFT_BOUND > item_width); // Verificar si hay espacio suficiente a la derecha bool can_spawn_right = (exclude_right < RIGHT_BOUND) && (RIGHT_BOUND - exclude_right > item_width); if (can_spawn_left && can_spawn_right) { // Ambos lados disponibles, elegir aleatoriamente if (rand() % 2 == 0) { // Lado izquierdo return (rand() % (exclude_left - LEFT_BOUND)) + LEFT_BOUND; } // Lado derecho return (rand() % (RIGHT_BOUND - exclude_right)) + exclude_right; } if (can_spawn_left) { // Solo lado izquierdo disponible return (rand() % (exclude_left - LEFT_BOUND)) + LEFT_BOUND; } if (can_spawn_right) { // Solo lado derecho disponible return (rand() % (RIGHT_BOUND - exclude_right)) + exclude_right; } // No hay espacio suficiente lejos del jugador // Por ahora, intentar spawn en el extremo más lejano posible int distance_to_left = abs(player_x - LEFT_BOUND); int distance_to_right = abs(RIGHT_BOUND - player_x); if (distance_to_left > distance_to_right) { return LEFT_BOUND; } return RIGHT_BOUND - item_width; }