#include "game/entities/moving_platform.hpp" #include // Para std::sqrt #include // Para rand #include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite #include "core/resources/resource_cache.hpp" // Para Resource #include "utils/easing_functions.hpp" // Para Easing::* // Resuelve el nombre de un easing a su función auto MovingPlatform::resolveEasing(const std::string& name) -> EasingFunc { if (name == "quadIn") { return Easing::quadIn; } if (name == "quadOut") { return Easing::quadOut; } if (name == "quadInOut") { return Easing::quadInOut; } if (name == "cubicIn") { return Easing::cubicIn; } if (name == "cubicOut") { return Easing::cubicOut; } if (name == "cubicInOut") { return Easing::cubicInOut; } if (name == "sineIn") { return Easing::sineIn; } if (name == "sineOut") { return Easing::sineOut; } if (name == "sineInOut") { return Easing::sineInOut; } return Easing::linear; } // Constructor MovingPlatform::MovingPlatform(const Data& data) : sprite_(std::make_shared(Resource::Cache::get()->getAnimationData(data.animation_path))), path_(data.path), speed_(data.speed), loop_mode_(data.loop), easing_(resolveEasing(data.easing)) { // Flags del SolidActor: jump-through desde abajo, carry al Player encima. // NOTA: sin BLOCKS_PLAYER, las plataformas móviles no bloquean lateralmente // ni por arriba. ONEWAY_TOP hace que los sweeps verticales solo las vean // al caer desde arriba. flags_ = CARRY_ON_TOP | ONEWAY_TOP; // Colocar el sprite en el primer waypoint if (!path_.empty()) { sprite_->setPosX(path_[0].x); sprite_->setPosY(path_[0].y); } aabb_ = getRect(); // Frame inicial sprite_->setCurrentAnimationFrame((data.frame == -1) ? (rand() % sprite_->getCurrentAnimationSize()) : data.frame); // Calcular longitud del primer segmento recalcSegmentLength(); } // Índice del waypoint origen del segmento actual auto MovingPlatform::getSegmentFrom() const -> int { if (direction_ == 1) { return current_segment_; } // Pingpong retrocediendo: segmento N va de path[N+1] a path[N] return current_segment_ + 1; } // Índice del waypoint destino del segmento actual auto MovingPlatform::getSegmentTo() const -> int { if (direction_ == 1) { if (loop_mode_ == LoopMode::CIRCULAR) { return (current_segment_ + 1) % static_cast(path_.size()); } return current_segment_ + 1; } // Pingpong retrocediendo return current_segment_; } // Recalcula la longitud del segmento actual void MovingPlatform::recalcSegmentLength() { if (path_.size() < 2) { segment_length_ = 0.0F; return; } int from = getSegmentFrom(); int to = getSegmentTo(); float dx = path_[to].x - path_[from].x; float dy = path_[to].y - path_[from].y; segment_length_ = std::sqrt((dx * dx) + (dy * dy)); } // Avanza al siguiente segmento void MovingPlatform::advanceSegment() { int path_size = static_cast(path_.size()); if (loop_mode_ == LoopMode::PINGPONG) { if (direction_ == 1) { if (current_segment_ + 1 >= path_size - 1) { // Llegamos al final, invertir dirección direction_ = -1; current_segment_ = path_size - 2; } else { current_segment_++; } } else { if (current_segment_ <= 0) { // Llegamos al inicio, invertir dirección direction_ = 1; current_segment_ = 0; } else { current_segment_--; } } } else { // CIRCULAR current_segment_ = (current_segment_ + 1) % path_size; } segment_progress_ = 0.0F; recalcSegmentLength(); } // Actualiza posición de la plataforma siguiendo la ruta void MovingPlatform::update(float delta_time) { sprite_->animate(delta_time); if (path_.size() < 2) { last_delta_.x = 0.0F; last_delta_.y = 0.0F; return; } float old_x = sprite_->getPosX(); float old_y = sprite_->getPosY(); // Si estamos esperando en un waypoint if (waiting_) { wait_timer_ -= delta_time; if (wait_timer_ <= 0.0F) { waiting_ = false; advanceSegment(); } else { last_delta_.x = 0.0F; last_delta_.y = 0.0F; return; } } // Avanzar por el segmento if (segment_length_ > 0.0F) { float distance = speed_ * delta_time; float delta_progress = distance / segment_length_; segment_progress_ += delta_progress; } else { // Segmento de longitud 0: saltar al siguiente segment_progress_ = 1.0F; } // Comprobar si llegamos al final del segmento if (segment_progress_ >= 1.0F) { segment_progress_ = 1.0F; // Colocar en el waypoint destino exacto int to = getSegmentTo(); sprite_->setPosX(path_[to].x); sprite_->setPosY(path_[to].y); // Comprobar si hay espera en este waypoint if (path_[to].wait > 0.0F) { waiting_ = true; wait_timer_ = path_[to].wait; } else { advanceSegment(); } } else { // Interpolar posición con easing float t = easing_(segment_progress_); int from = getSegmentFrom(); int to = getSegmentTo(); float new_x = path_[from].x + ((path_[to].x - path_[from].x) * t); float new_y = path_[from].y + ((path_[to].y - path_[from].y) * t); sprite_->setPosX(new_x); sprite_->setPosY(new_y); } last_delta_.x = sprite_->getPosX() - old_x; last_delta_.y = sprite_->getPosY() - old_y; aabb_ = getRect(); } // Pinta la plataforma en pantalla void MovingPlatform::render() { sprite_->render(); } #ifdef _DEBUG // Solo actualiza la animación sin mover la plataforma void MovingPlatform::updateAnimation(float delta_time) { sprite_->animate(delta_time); } // Resetea la plataforma a su posición inicial (para editor) void MovingPlatform::resetToInitialPosition(const Data& data) { path_ = data.path; speed_ = data.speed; loop_mode_ = data.loop; easing_ = resolveEasing(data.easing); current_segment_ = 0; direction_ = 1; segment_progress_ = 0.0F; waiting_ = false; wait_timer_ = 0.0F; if (!path_.empty()) { sprite_->setPosX(path_[0].x); sprite_->setPosY(path_[0].y); } aabb_ = getRect(); recalcSegmentLength(); } #endif // Devuelve el rectangulo que contiene a la plataforma auto MovingPlatform::getRect() -> SDL_FRect { return sprite_->getRect(); }