Files
projecte_2026/source/game/entities/moving_platform.cpp

225 lines
6.5 KiB
C++

#include "game/entities/moving_platform.hpp"
#include <cmath> // Para std::sqrt
#include <cstdlib> // 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<AnimatedSprite>(Resource::Cache::get()->getAnimationData(data.animation_path))),
path_(data.path),
speed_(data.speed),
loop_mode_(data.loop),
easing_(resolveEasing(data.easing)) {
// Colocar el sprite en el primer waypoint
if (!path_.empty()) {
sprite_->setPosX(path_[0].x);
sprite_->setPosY(path_[0].y);
}
collider_ = 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<int>(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<int>(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_dx_ = 0.0F;
last_dy_ = 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_dx_ = 0.0F;
last_dy_ = 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_dx_ = sprite_->getPosX() - old_x;
last_dy_ = sprite_->getPosY() - old_y;
collider_ = 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);
}
collider_ = getRect();
recalcSegmentLength();
}
#endif
// Devuelve el rectangulo que contiene a la plataforma
auto MovingPlatform::getRect() -> SDL_FRect {
return sprite_->getRect();
}
// Obtiene el rectangulo de colisión
auto MovingPlatform::getCollider() -> SDL_FRect& {
return collider_;
}