From 29e0daffb0d8eaede3773d1cfb11a1f4e61c7d24 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Fri, 14 Nov 2025 10:42:30 +0100 Subject: [PATCH] treballant en Player --- .claude/settings.local.json | 3 +- source/game/entities/player.cpp | 580 ++++++++++++------------- source/game/entities/player.hpp | 53 ++- source/game/gameplay/collision_map.cpp | 18 +- source/game/gameplay/collision_map.hpp | 18 +- source/game/gameplay/room.cpp | 18 +- source/game/gameplay/room.hpp | 18 +- source/game/scene_manager.hpp | 2 +- source/utils/defines.hpp | 6 + 9 files changed, 348 insertions(+), 368 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 29da919..5eb388c 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -9,7 +9,8 @@ "Bash(cat:*)", "Bash(git add:*)", "Bash(git commit:*)", - "Bash(git checkout:*)" + "Bash(git checkout:*)", + "Bash(sort:*)" ], "deny": [], "ask": [] diff --git a/source/game/entities/player.cpp b/source/game/entities/player.cpp index 6c217dc..89533a2 100644 --- a/source/game/entities/player.cpp +++ b/source/game/entities/player.cpp @@ -49,24 +49,14 @@ void Player::handleInput() { } else if (Input::get()->checkAction(InputAction::RIGHT)) { wannaGo = Direction::RIGHT; } else { - wannaGo = Direction::STAY; + wannaGo = Direction::NONE; } wannaJump = Input::get()->checkAction(InputAction::JUMP); } -// ANTIGUO move() - Comentado durante migración a paradigma de estados -/* +// La lógica de movimiento está distribuida en move void Player::move(float delta_time) { - handleHorizontalMovement(delta_time); - handleVerticalMovement(delta_time); - updateColliderGeometry(); -} -*/ - -// NUEVO: La lógica de movimiento está distribuida en move*() y se llama desde update*() -void Player::move(float delta_time) { - // Este método ahora es un dispatcher que llama al método de movimiento correspondiente switch (state_) { case State::ON_GROUND: moveOnGround(delta_time); @@ -81,45 +71,11 @@ void Player::move(float delta_time) { moveFalling(delta_time); break; } -} - -void Player::handleHorizontalMovement(float delta_time) { - // TODO: Verificar si esto debe aplicar solo a ON_GROUND o también ON_SLOPE - if (state_ == State::ON_GROUND || state_ == State::ON_SLOPE) { - // Determinama cuál debe ser la velocidad a partir de automovement o de wannaGo - updateVelocity(); - } - - // Aplica el movimiento y el flip basado en la velocidad resultante - if (vx_ < 0.0F) { - moveHorizontal(delta_time, Direction::LEFT); - sprite_->setFlip(SDL_FLIP_HORIZONTAL); - } else if (vx_ > 0.0F) { - moveHorizontal(delta_time, Direction::RIGHT); - sprite_->setFlip(SDL_FLIP_NONE); - } -} - -void Player::handleVerticalMovement(float delta_time) { - // No hay movimiento vertical en estados terrestres - if (state_ == State::ON_GROUND || state_ == State::ON_SLOPE) { - return; - } - - if (state_ == State::JUMPING) { - applyGravity(delta_time); - } - - // Movimiento vertical - if (vy_ < 0.0F) { - moveVerticalUp(delta_time); - } else if (vy_ > 0.0F) { - moveVerticalDown(delta_time); - } + syncSpriteAndCollider(); // Actualiza la posición del sprite y las colisiones } void Player::handleConveyorBelts() { - if (!auto_movement_ and isOnConveyorBelt() and wannaGo == Direction::STAY) { + if (!auto_movement_ and isOnConveyorBelt() and wannaGo == Direction::NONE) { auto_movement_ = true; } @@ -129,7 +85,6 @@ void Player::handleConveyorBelts() { } void Player::handleShouldFall() { - // TODO: También verificar ON_SLOPE if (!isOnFloor() and (state_ == State::ON_GROUND || state_ == State::ON_SLOPE)) { transitionToState(State::FALLING); } @@ -189,126 +144,237 @@ void Player::updateState(float delta_time) { } } -// ============================================================================ -// NUEVO PARADIGMA: Métodos de actualización por estado -// ============================================================================ - -// ANTIGUO updateState() - Comentado durante migración -/* -void Player::updateState(float delta_time) { - switch (state_) { - case State::STANDING: - handleConveyorBelts(); - handleShouldFall(); - if (wannaJump) { - transitionToState(State::JUMPING); - } - break; - case State::JUMPING: - auto_movement_ = false; - playJumpSound(delta_time); - handleJumpEnd(); - break; - case State::FALLING: - auto_movement_ = false; - playFallSound(delta_time); - break; - } -} -*/ - // Actualización lógica del estado ON_GROUND void Player::updateOnGround(float delta_time) { - (void)delta_time; // No usado en este método, pero se mantiene por consistencia - - // Gestiona las cintas transportadoras - handleConveyorBelts(); - - // Verifica si debe caer (no tiene suelo) - handleShouldFall(); + (void)delta_time; // No usado en este método, pero se mantiene por consistencia + handleConveyorBelts(); // Gestiona las cintas transportadoras + handleShouldFall(); // Verifica si debe caer (no tiene suelo) // Verifica si el jugador quiere saltar - if (wannaJump) { - transitionToState(State::JUMPING); - } + if (wannaJump) { transitionToState(State::JUMPING); } } // Actualización lógica del estado ON_SLOPE void Player::updateOnSlope(float delta_time) { - (void)delta_time; // No usado en este método, pero se mantiene por consistencia - - // Verifica si debe caer (no tiene suelo) - handleShouldFall(); + (void)delta_time; // No usado en este método, pero se mantiene por consistencia + handleShouldFall(); // Verifica si debe caer (no tiene suelo) // Verifica si el jugador quiere saltar - if (wannaJump) { - transitionToState(State::JUMPING); - } + if (wannaJump) { transitionToState(State::JUMPING); } } // Actualización lógica del estado JUMPING void Player::updateJumping(float delta_time) { - // Desactiva el movimiento automático durante el salto - auto_movement_ = false; - - // Reproduce los sonidos de salto - playJumpSound(delta_time); - - // Verifica si el salto ha terminado (alcanzó la altura inicial) - handleJumpEnd(); + auto_movement_ = false; // Desactiva el movimiento automático durante el salto + playJumpSound(delta_time); // Reproduce los sonidos de salto + handleJumpEnd(); // Verifica si el salto ha terminado (alcanzó la altura inicial) } // Actualización lógica del estado FALLING void Player::updateFalling(float delta_time) { - // Desactiva el movimiento automático durante la caída - auto_movement_ = false; - - // Reproduce los sonidos de caída - playFallSound(delta_time); + auto_movement_ = false; // Desactiva el movimiento automático durante la caída + playFallSound(delta_time); // Reproduce los sonidos de caída } -// ============================================================================ -// NUEVO PARADIGMA: Métodos de movimiento por estado -// ============================================================================ - // Movimiento físico del estado ON_GROUND void Player::moveOnGround(float delta_time) { - // Movimiento horizontal en suelo plano (migrado de handleHorizontalMovement) - handleHorizontalMovement(delta_time); + // Determinama cuál debe ser la velocidad a partir de automovement o de wannaGo + updateVelocity(); - // Actualiza geometría de colisión - updateColliderGeometry(); + if (vx_ == 0.0F) { return; } + + const float DISPLACEMENT = vx_ * delta_time; + if (vx_ < 0.0F) { + const SDL_FRect PROJECTION = getProjection(Direction::LEFT, DISPLACEMENT); + const int POS = room_->checkRightSurfaces(PROJECTION); + if (POS == Collision::NONE) { + x_ += DISPLACEMENT; + } else { + x_ = POS + 1; + } + } else if (vx_ > 0.0F) { + const SDL_FRect PROJECTION = getProjection(Direction::RIGHT, DISPLACEMENT); + const int POS = room_->checkLeftSurfaces(PROJECTION); + if (POS == Collision::NONE) { + x_ += DISPLACEMENT; + } else { + x_ = POS - WIDTH; + } + } } // Movimiento físico del estado ON_SLOPE void Player::moveOnSlope(float delta_time) { - // Movimiento horizontal en rampa (migrado de handleHorizontalMovement) - // handleSlopeMovement() ya se llama desde dentro de moveHorizontal() - handleHorizontalMovement(delta_time); + // Determinama cuál debe ser la velocidad a partir de automovement o de wannaGo + updateVelocity(); - // Actualiza geometría de colisión - updateColliderGeometry(); + if (vx_ == 0.0F) { return; } + + // Movimiento horizontal + const float DISPLACEMENT = vx_ * delta_time; + if (vx_ < 0.0F) { + const SDL_FRect PROJECTION = getProjection(Direction::LEFT, DISPLACEMENT); + const int POS = room_->checkRightSurfaces(PROJECTION); + if (POS == Collision::NONE) { + x_ += DISPLACEMENT; + } else { + x_ = POS + 1; + } + } else if (vx_ > 0.0F) { + const SDL_FRect PROJECTION = getProjection(Direction::RIGHT, DISPLACEMENT); + const int POS = room_->checkLeftSurfaces(PROJECTION); + if (POS == Collision::NONE) { + x_ += DISPLACEMENT; + } else { + x_ = POS - WIDTH; + } + } + + // Movimiento vertical + // Regla: Si está bajando la rampa, se pega a la slope + if (isOnDownSlope()) { + y_ += 1; + return; + } + + // Regla: Si está STANDING y tropieza lateralmente con una Slope, se pega a la slope + // Comprueba si hay rampa en contacto lateral (solo los dos pixels inferiores) + const int SIDE_X = vx_ < 0.0F ? static_cast(x_) : static_cast(x_) + WIDTH - 1; + const LineVertical SIDE = { + .x = SIDE_X, + .y1 = static_cast(y_) + HEIGHT - 2, + .y2 = static_cast(y_) + HEIGHT - 1}; + + // Comprueba la rampa correspondiente según la dirección + const int SLOPE_Y = vx_ < 0.0F ? room_->checkLeftSlopes(SIDE) : room_->checkRightSlopes(SIDE); + + if (SLOPE_Y != Collision::NONE) { + // Hay rampa: sube al jugador para pegarlo a la rampa + + // --- INICIO DE LA CORRECCIÓN --- + + // Esta es la nueva posición "ideal" a la que nos queremos teletransportar + const float new_y = SLOPE_Y - HEIGHT; + + // Solo aplicamos el "snap" si es para SUBIR (new_y < y_) + if (new_y < y_) { + // "y_ - 1.0F" es la posición máxima que podemos subir en 1 frame. + // std::max coge la posición más alta (más baja en pantalla): + // - O la posición ideal (new_y) + // - O la posición actual - 1 píxel + // Esto "suaviza" el salto de 2 píxeles (p.ej. 84 -> 82) en dos + // fotogramas (84 -> 83, y luego 83 -> 82) + y_ = std::max(new_y, y_ - 1.0F); + } + + // --- FIN DE LA CORRECCIÓN --- + } } // Movimiento físico del estado JUMPING void Player::moveJumping(float delta_time) { - // Movimiento horizontal (migrado de handleHorizontalMovement) - handleHorizontalMovement(delta_time); + // Movimiento horizontal + if (vx_ != 0.0F) { + const float DISPLACEMENT_X = vx_ * delta_time; + const Direction DIRECTION = vx_ > 0.0F ? Direction::RIGHT : Direction::LEFT; + const SDL_FRect PROJECTION = getProjection(DIRECTION, DISPLACEMENT_X); - // Movimiento vertical (migrado de handleVerticalMovement) - handleVerticalMovement(delta_time); + // Comprueba la colisión con las superficies + const int POS = DIRECTION == Direction::LEFT ? room_->checkRightSurfaces(PROJECTION) : room_->checkLeftSurfaces(PROJECTION); - // Actualiza geometría de colisión - updateColliderGeometry(); + // Calcula la nueva posición + if (POS == Collision::NONE) { + // No hay colisión: mueve al jugador + x_ += DISPLACEMENT_X; + } else { + // Hay colisión: reposiciona al jugador en el punto de colisión + x_ = DIRECTION == Direction::LEFT ? POS + 1 : POS - WIDTH; + } + } + + // Movimiento vertical + applyGravity(delta_time); + + const float DISPLACEMENT_Y = vy_ * delta_time; + // Movimiento vertical hacia arriba + if (vy_ < 0.0F) { + const SDL_FRect PROJECTION = getProjection(Direction::UP, DISPLACEMENT_Y); + + // Comprueba la colisión + const int POS = room_->checkBottomSurfaces(PROJECTION); + + // Calcula la nueva posición + if (POS == Collision::NONE) { + // Si no hay colisión + y_ += DISPLACEMENT_Y; + } else { + // Si hay colisión lo mueve hasta donde no colisiona -> FALLING + y_ = POS + 1; + transitionToState(State::FALLING); + } + } + // Movimiento vertical hacia abajo + else if (vy_ > 0.0F) { + // Crea el rectangulo de proyección en el eje Y para ver si colisiona + const SDL_FRect PROJECTION = getProjection(Direction::DOWN, DISPLACEMENT_Y); + + // Comprueba la colisión con las superficies y las cintas transportadoras + const float POS = std::max(room_->checkTopSurfaces(PROJECTION), room_->checkAutoSurfaces(PROJECTION)); + if (POS != Collision::NONE) { + // Si hay colisión lo mueve hasta donde no colisiona y pasa a estar sobre la superficie + y_ = POS - HEIGHT; + transitionToState(State::ON_GROUND); // Aterrizó en superficie plana o conveyor belt + } + // Comprueba la colisión con las rampas + else { + // JUMPING colisiona con rampas solo si vx_ == 0 + if (vx_ == 0.0F) { + auto rect = toSDLRect(PROJECTION); + const LineVertical LEFT_SIDE = {.x = rect.x, .y1 = rect.y, .y2 = rect.y + rect.h}; + const LineVertical RIGHT_SIDE = {.x = rect.x + rect.w - 1, .y1 = rect.y, .y2 = rect.y + rect.h}; + const float POINT = std::max(room_->checkRightSlopes(RIGHT_SIDE), room_->checkLeftSlopes(LEFT_SIDE)); + if (POINT != Collision::NONE) { + y_ = POINT - HEIGHT; + transitionToState(State::ON_SLOPE); // Aterrizó en rampa + } else { + // No hay colisón con una rampa + y_ += DISPLACEMENT_Y; + } + } else { + // Esta saltando con movimiento horizontal y no hay colisión con los muros + // Calcula la nueva posición (atraviesa rampas) + y_ += DISPLACEMENT_Y; + } + } + } } // Movimiento físico del estado FALLING void Player::moveFalling(float delta_time) { - // Movimiento vertical (migrado de handleVerticalMovement) - handleVerticalMovement(delta_time); + // Crea el rectangulo de proyección en el eje Y para ver si colisiona + const float DISPLACEMENT = vy_ * delta_time; + const SDL_FRect PROJECTION = getProjection(Direction::DOWN, DISPLACEMENT); - // Actualiza geometría de colisión - updateColliderGeometry(); + // Comprueba la colisión con las superficies y las cintas transportadoras + const float POS = std::max(room_->checkTopSurfaces(PROJECTION), room_->checkAutoSurfaces(PROJECTION)); + if (POS != Collision::NONE) { + // Si hay colisión lo mueve hasta donde no colisiona y pasa a estar sobre la superficie + y_ = POS - HEIGHT; + transitionToState(State::ON_GROUND); // Aterrizó en superficie plana o conveyor belt + } + // Comprueba la colisión con las rampas + else { + auto rect = toSDLRect(PROJECTION); + const LineVertical LEFT_SIDE = {.x = rect.x, .y1 = rect.y, .y2 = rect.y + rect.h}; + const LineVertical RIGHT_SIDE = {.x = rect.x + rect.w - 1, .y1 = rect.y, .y2 = rect.y + rect.h}; + const float POINT = std::max(room_->checkRightSlopes(RIGHT_SIDE), room_->checkLeftSlopes(LEFT_SIDE)); + if (POINT != Collision::NONE) { + y_ = POINT - HEIGHT; + transitionToState(State::ON_SLOPE); // Aterrizó en rampa + } else { + y_ += DISPLACEMENT; + } + } } // Comprueba si está situado en alguno de los cuatro bordes de la habitación @@ -394,169 +460,6 @@ void Player::applyGravity(float delta_time) { } } -// Maneja el movimiento sobre rampas -void Player::handleSlopeMovement(Direction direction) { - // No procesa rampas durante el salto (permite atravesarlas cuando salta con movimiento horizontal) - // Pero SÍ procesa en STANDING y FALLING (para pegarse a las rampas) - if (state_ == State::JUMPING) { - return; - } - - // Regla: Si está bajando la rampa, se pega a la slope - if (isOnDownSlope()) { - y_ += 1; - return; - } - - // Regla: Si está STANDING y tropieza lateralmente con una Slope, se pega a la slope - // Comprueba si hay rampa en contacto lateral (solo los dos pixels inferiores) - const int SIDE_X = direction == Direction::LEFT ? static_cast(x_) : static_cast(x_) + WIDTH - 1; - const LineVertical SIDE = { - .x = SIDE_X, - .y1 = static_cast(y_) + HEIGHT - 2, - .y2 = static_cast(y_) + HEIGHT - 1}; - - // Comprueba la rampa correspondiente según la dirección - const int SLOPE_Y = direction == Direction::LEFT ? room_->checkLeftSlopes(SIDE) : room_->checkRightSlopes(SIDE); - - if (SLOPE_Y != Collision::NONE) { - // Hay rampa: sube al jugador para pegarlo a la rampa - - // --- INICIO DE LA CORRECCIÓN --- - - // Esta es la nueva posición "ideal" a la que nos queremos teletransportar - const float new_y = SLOPE_Y - HEIGHT; - - // Solo aplicamos el "snap" si es para SUBIR (new_y < y_) - if (new_y < y_) { - // "y_ - 1.0F" es la posición máxima que podemos subir en 1 frame. - // std::max coge la posición más alta (más baja en pantalla): - // - O la posición ideal (new_y) - // - O la posición actual - 1 píxel - // Esto "suaviza" el salto de 2 píxeles (p.ej. 84 -> 82) en dos - // fotogramas (84 -> 83, y luego 83 -> 82) - y_ = std::max(new_y, y_ - 1.0F); - } - - // --- FIN DE LA CORRECCIÓN --- - } -} - -// Maneja el movimiento horizontal -void Player::moveHorizontal(float delta_time, Direction direction) { - const float DISPLACEMENT = vx_ * delta_time; - - // Crea el rectangulo de proyección en el eje X para ver si colisiona - SDL_FRect proj; - switch (direction) { - case Direction::LEFT: - // Movimiento a la izquierda - proj = { - .x = x_ + DISPLACEMENT, - .y = y_, - .w = std::ceil(std::fabs(DISPLACEMENT)), - .h = HEIGHT}; - break; - - case Direction::RIGHT: - // Movimiento a la derecha - proj = { - .x = x_ + WIDTH, - .y = y_, - .w = std::ceil(DISPLACEMENT), - .h = HEIGHT}; - break; - - default: - break; - } - - // Comprueba la colisión con las superficies - const int POS = direction == Direction::LEFT ? room_->checkRightSurfaces(proj) : room_->checkLeftSurfaces(proj); - - // Calcula la nueva posición - if (POS == Collision::NONE) { - // No hay colisión: mueve al jugador - x_ += DISPLACEMENT; - } else { - // Hay colisión: reposiciona al jugador en el punto de colisión - x_ = direction == Direction::LEFT ? POS + 1 : POS - WIDTH; - } - - // Maneja el movimiento sobre rampas - handleSlopeMovement(direction); -} - -// Maneja el movimiento vertical hacia arriba -void Player::moveVerticalUp(float delta_time) { - // Crea el rectangulo de proyección en el eje Y para ver si colisiona - const float DISPLACEMENT = vy_ * delta_time; - SDL_FRect proj = { - .x = x_, - .y = y_ + DISPLACEMENT, - .w = WIDTH, - .h = std::ceil(std::fabs(DISPLACEMENT)) // Para evitar que tenga una altura de 0 pixels - }; - - // Comprueba la colisión - const int POS = room_->checkBottomSurfaces(proj); - - // Calcula la nueva posición - if (POS == Collision::NONE) { - // Si no hay colisión - y_ += DISPLACEMENT; - } else { - // Si hay colisión lo mueve hasta donde no colisiona - // Regla: Si está JUMPING y tropieza contra el techo -> FALLING - y_ = POS + 1; - transitionToState(State::FALLING); - } -} - -// Maneja el movimiento vertical hacia abajo -void Player::moveVerticalDown(float delta_time) { - // Crea el rectangulo de proyección en el eje Y para ver si colisiona - const float DISPLACEMENT = vy_ * delta_time; - SDL_FRect proj = { - .x = x_, - .y = y_ + HEIGHT, - .w = WIDTH, - .h = std::ceil(DISPLACEMENT) // Para evitar que tenga una altura de 0 pixels - }; - - // Comprueba la colisión con las superficies normales y las automáticas - const float POS = std::max(room_->checkTopSurfaces(proj), room_->checkAutoSurfaces(proj)); - if (POS != Collision::NONE) { - // Si hay colisión lo mueve hasta donde no colisiona y pasa a estar sobre la superficie - y_ = POS - HEIGHT; - transitionToState(State::ON_GROUND); // Aterrizó en superficie plana o conveyor belt - } else { - // Si no hay colisión con los muros, comprueba la colisión con las rampas - // CORRECCIÓN: FALLING siempre se pega a rampas, JUMPING se pega solo si vx_ == 0 - if (state_ == State::FALLING || (state_ == State::JUMPING && vx_ == 0.0F)) { - // No está saltando O salta recto: se pega a las rampas - auto rect = toSDLRect(proj); - const LineVertical LEFT_SIDE = {.x = rect.x, .y1 = rect.y, .y2 = rect.y + rect.h}; - const LineVertical RIGHT_SIDE = {.x = rect.x + rect.w - 1, .y1 = rect.y, .y2 = rect.y + rect.h}; - const float POINT = std::max(room_->checkRightSlopes(RIGHT_SIDE), room_->checkLeftSlopes(LEFT_SIDE)); - if (POINT != Collision::NONE) { - // No está saltando y hay colisión con una rampa - // Calcula la nueva posición - y_ = POINT - HEIGHT; - transitionToState(State::ON_SLOPE); // Aterrizó en rampa - } else { - // No está saltando y no hay colisón con una rampa - // Calcula la nueva posición - y_ += DISPLACEMENT; - } - } else { - // Esta saltando con movimiento horizontal y no hay colisión con los muros - // Calcula la nueva posición (atraviesa rampas) - y_ += DISPLACEMENT; - } - } -} - // Establece la animación del jugador void Player::animate(float delta_time) { if (vx_ != 0) { @@ -595,25 +498,38 @@ void Player::playFallSound(float delta_time) { // Comprueba si el jugador tiene suelo debajo de los pies auto Player::isOnFloor() -> bool { - bool on_floor = false; + bool on_top_surface = false; + bool on_conveyor_belt = false; updateFeet(); // Comprueba las superficies - on_floor |= room_->checkTopSurfaces(under_left_foot_); - on_floor |= room_->checkTopSurfaces(under_right_foot_); + on_top_surface |= room_->checkTopSurfaces(under_left_foot_); + on_top_surface |= room_->checkTopSurfaces(under_right_foot_); // Comprueba las cintas transportadoras - on_floor |= room_->checkConveyorBelts(under_left_foot_); - on_floor |= room_->checkConveyorBelts(under_right_foot_); + on_conveyor_belt |= room_->checkConveyorBelts(under_left_foot_); + on_conveyor_belt |= room_->checkConveyorBelts(under_right_foot_); // Comprueba las rampas auto on_slope_l = room_->checkLeftSlopes(under_left_foot_); auto on_slope_r = room_->checkRightSlopes(under_right_foot_); - return on_floor || on_slope_l || on_slope_r; + return on_top_surface || on_conveyor_belt || on_slope_l || on_slope_r; } -// Comprueba si el jugador esta sobre una superficie automática +// Comprueba si el jugador está sobre una superficie +auto Player::isOnTopSurface() -> bool { + bool on_top_surface = false; + updateFeet(); + + // Comprueba las superficies + on_top_surface |= room_->checkTopSurfaces(under_left_foot_); + on_top_surface |= room_->checkTopSurfaces(under_right_foot_); + + return on_top_surface; +} + +// Comprueba si el jugador esta sobre una cinta transportadora auto Player::isOnConveyorBelt() -> bool { bool on_conveyor_belt = false; updateFeet(); @@ -644,6 +560,18 @@ auto Player::isOnDownSlope() -> bool { return on_slope; } +// Comprueba si el jugador está sobre una rampa +auto Player::isOnSlope() -> bool { + bool on_slope = false; + updateFeet(); + + // Comprueba las rampas + on_slope |= room_->checkLeftSlopes(under_left_foot_); + on_slope |= room_->checkRightSlopes(under_right_foot_); + + return on_slope; +} + // Comprueba que el jugador no toque ningun tile de los que matan auto Player::handleKillingTiles() -> bool { // Comprueba si hay contacto con algún tile que mata @@ -806,8 +734,8 @@ void Player::initSprite(const std::string& animations_path) { sprite_->setCurrentAnimation("walk"); } -// Actualiza collider_box y collision points -void Player::updateColliderGeometry() { +// Actualiza la posición del sprite y las colisiones +void Player::syncSpriteAndCollider() { placeSprite(); // Coloca el sprite en la posición del jugador collider_box_ = getRect(); // Actualiza el rectangulo de colisión updateColliderPoints(); // Actualiza los puntos de colisión @@ -831,20 +759,66 @@ void Player::updateVelocity() { if (auto_movement_) { // La cinta transportadora tiene el control vx_ = HORIZONTAL_VELOCITY * room_->getConveyorBeltDirection(); + sprite_->setFlip(vx_ < 0.0F ? Flip::LEFT : Flip::RIGHT); } else { // El jugador tiene el control switch (wannaGo) { case Direction::LEFT: vx_ = -HORIZONTAL_VELOCITY; + sprite_->setFlip(Flip::LEFT); break; case Direction::RIGHT: vx_ = HORIZONTAL_VELOCITY; + sprite_->setFlip(Flip::RIGHT); break; - case Direction::STAY: + case Direction::NONE: vx_ = 0.0F; break; default: + vx_ = 0.0F; break; } } +} + +// Devuelve el rectangulo de proyeccion +auto Player::getProjection(Direction direction, float displacement) -> SDL_FRect { + switch (direction) { + case Direction::LEFT: + return { + .x = x_ + displacement, + .y = y_, + .w = std::ceil(std::fabs(displacement)), // Para evitar que tenga una anchura de 0 pixels + .h = HEIGHT}; + + case Direction::RIGHT: + return { + .x = x_ + WIDTH, + .y = y_, + .w = std::ceil(displacement), // Para evitar que tenga una anchura de 0 pixels + .h = HEIGHT}; + + case Direction::UP: + return { + .x = x_, + .y = y_ + displacement, + .w = WIDTH, + .h = std::ceil(std::fabs(displacement)) // Para evitar que tenga una altura de 0 pixels + }; + + case Direction::DOWN: + return { + .x = x_, + .y = y_ + HEIGHT, + .w = WIDTH, + .h = std::ceil(displacement) // Para evitar que tenga una altura de 0 pixels + }; + + default: + return { + .x = 0.0F, + .y = 0.0F, + .w = 0.0F, + .h = 0.0F}; + } } \ No newline at end of file diff --git a/source/game/entities/player.hpp b/source/game/entities/player.hpp index 657ea24..92e359f 100644 --- a/source/game/entities/player.hpp +++ b/source/game/entities/player.hpp @@ -27,7 +27,9 @@ class Player { enum class Direction { LEFT, RIGHT, - STAY + UP, + DOWN, + NONE }; // --- Constantes de física (públicas para permitir cálculos en structs) --- @@ -118,7 +120,7 @@ class Player { float vx_ = 0.0F; // Velocidad/desplazamiento del jugador en el eje X float vy_ = 0.0F; // Velocidad/desplazamiento del jugador en el eje Y - Direction wannaGo = Direction::STAY; + Direction wannaGo = Direction::NONE; bool wannaJump = false; // --- Variables de estado --- @@ -143,27 +145,25 @@ class Player { Uint8 color_ = 0; // Color del jugador std::array jumping_sound_{}; // Array con todos los sonidos del salto std::array falling_sound_{}; // Array con todos los sonidos de la caída - JumpSoundController jump_sound_ctrl_; // Controlador de sonidos de salto - FallSoundController fall_sound_ctrl_; // Controlador de sonidos de caída - int fall_start_position_ = 0; // Posición Y al iniciar la caída + JumpSoundController jump_sound_ctrl_; // Controlador de sonidos de salto + FallSoundController fall_sound_ctrl_; // Controlador de sonidos de caída + int fall_start_position_ = 0; // Posición Y al iniciar la caída - void handleHorizontalMovement(float delta_time); - void handleVerticalMovement(float delta_time); void handleConveyorBelts(); void handleShouldFall(); void updateState(float delta_time); - // --- Métodos de actualización por estado (nuevo paradigma) --- - void updateOnGround(float delta_time); // Actualización lógica estado ON_GROUND - void updateOnSlope(float delta_time); // Actualización lógica estado ON_SLOPE - void updateJumping(float delta_time); // Actualización lógica estado JUMPING - void updateFalling(float delta_time); // Actualización lógica estado FALLING + // --- Métodos de actualización por estado --- + void updateOnGround(float delta_time); // Actualización lógica estado ON_GROUND + void updateOnSlope(float delta_time); // Actualización lógica estado ON_SLOPE + void updateJumping(float delta_time); // Actualización lógica estado JUMPING + void updateFalling(float delta_time); // Actualización lógica estado FALLING - // --- Métodos de movimiento por estado (nuevo paradigma) --- - void moveOnGround(float delta_time); // Movimiento físico estado ON_GROUND - void moveOnSlope(float delta_time); // Movimiento físico estado ON_SLOPE - void moveJumping(float delta_time); // Movimiento físico estado JUMPING - void moveFalling(float delta_time); // Movimiento físico estado FALLING + // --- Métodos de movimiento por estado --- + void moveOnGround(float delta_time); // Movimiento físico estado ON_GROUND + void moveOnSlope(float delta_time); // Movimiento físico estado ON_SLOPE + void moveJumping(float delta_time); // Movimiento físico estado JUMPING + void moveFalling(float delta_time); // Movimiento físico estado FALLING // --- Funciones de inicialización --- void initSprite(const std::string& animations_path); // Inicializa el sprite del jugador @@ -181,22 +181,21 @@ class Player { void applyGravity(float delta_time); // Aplica gravedad al jugador // --- Funciones de movimiento y colisión --- - void move(float delta_time); // Orquesta el movimiento del jugador - void moveHorizontal(float delta_time, Direction direction); // Maneja el movimiento horizontal (-1: izq, 1: der) - void moveVerticalUp(float delta_time); // Maneja el movimiento vertical hacia arriba - void moveVerticalDown(float delta_time); // Maneja el movimiento vertical hacia abajo - void handleSlopeMovement(Direction direction); // Maneja el movimiento sobre rampas + void move(float delta_time); // Orquesta el movimiento del jugador + auto getProjection(Direction direction, float displacement) -> SDL_FRect; // Devuelve el rectangulo de proyeccion // --- Funciones de detección de superficies --- auto isOnFloor() -> bool; // Comprueba si el jugador tiene suelo debajo de los pies - auto isOnConveyorBelt() -> bool; // Comprueba si el jugador esta sobre una superficie automática + auto isOnTopSurface() -> bool; // Comprueba si el jugador está sobre una superficie + auto isOnConveyorBelt() -> bool; // Comprueba si el jugador esta sobre una cinta transportadora auto isOnDownSlope() -> bool; // Comprueba si el jugador está sobre una rampa hacia abajo + auto isOnSlope() -> bool; // Comprueba si el jugador está sobre una rampa // --- Funciones de actualización de geometría --- - void updateColliderGeometry(); // Actualiza collider_box y collision points - void updateColliderPoints(); // Actualiza los puntos de colisión - void updateFeet(); // Actualiza los puntos de los pies - void placeSprite(); // Coloca el sprite en la posición del jugador + void syncSpriteAndCollider(); // Actualiza collider_box y collision points + void updateColliderPoints(); // Actualiza los puntos de colisión + void updateFeet(); // Actualiza los puntos de los pies + void placeSprite(); // Coloca el sprite en la posición del jugador // --- Funciones de finalización --- void animate(float delta_time); // Establece la animación del jugador diff --git a/source/game/gameplay/collision_map.cpp b/source/game/gameplay/collision_map.cpp index 2998a6d..459cc7f 100644 --- a/source/game/gameplay/collision_map.cpp +++ b/source/game/gameplay/collision_map.cpp @@ -103,7 +103,7 @@ auto CollisionMap::getSlopeHeight(SDL_FPoint p, Tile slope) -> int { // === Queries de colisión === // Comprueba las colisiones con paredes derechas -auto CollisionMap::checkRightSurfaces(SDL_FRect& rect) -> int { +auto CollisionMap::checkRightSurfaces(const SDL_FRect& rect) -> int { for (const auto& s : right_walls_) { if (checkCollision(s, rect)) { return s.x; @@ -113,7 +113,7 @@ auto CollisionMap::checkRightSurfaces(SDL_FRect& rect) -> int { } // Comprueba las colisiones con paredes izquierdas -auto CollisionMap::checkLeftSurfaces(SDL_FRect& rect) -> int { +auto CollisionMap::checkLeftSurfaces(const SDL_FRect& rect) -> int { for (const auto& s : left_walls_) { if (checkCollision(s, rect)) { return s.x; @@ -123,7 +123,7 @@ auto CollisionMap::checkLeftSurfaces(SDL_FRect& rect) -> int { } // Comprueba las colisiones con techos -auto CollisionMap::checkTopSurfaces(SDL_FRect& rect) -> int { +auto CollisionMap::checkTopSurfaces(const SDL_FRect& rect) -> int { for (const auto& s : top_floors_) { if (checkCollision(s, rect)) { return s.y; @@ -133,14 +133,14 @@ auto CollisionMap::checkTopSurfaces(SDL_FRect& rect) -> int { } // Comprueba las colisiones punto con techos -auto CollisionMap::checkTopSurfaces(SDL_FPoint& p) -> bool { +auto CollisionMap::checkTopSurfaces(const SDL_FPoint& p) -> bool { return std::ranges::any_of(top_floors_, [&](const auto& s) { return checkCollision(s, p); }); } // Comprueba las colisiones con suelos -auto CollisionMap::checkBottomSurfaces(SDL_FRect& rect) -> int { +auto CollisionMap::checkBottomSurfaces(const SDL_FRect& rect) -> int { for (const auto& s : bottom_floors_) { if (checkCollision(s, rect)) { return s.y; @@ -150,7 +150,7 @@ auto CollisionMap::checkBottomSurfaces(SDL_FRect& rect) -> int { } // Comprueba las colisiones con conveyor belts -auto CollisionMap::checkAutoSurfaces(SDL_FRect& rect) -> int { +auto CollisionMap::checkAutoSurfaces(const SDL_FRect& rect) -> int { for (const auto& s : conveyor_belt_floors_) { if (checkCollision(s, rect)) { return s.y; @@ -160,7 +160,7 @@ auto CollisionMap::checkAutoSurfaces(SDL_FRect& rect) -> int { } // Comprueba las colisiones punto con conveyor belts -auto CollisionMap::checkConveyorBelts(SDL_FPoint& p) -> bool { +auto CollisionMap::checkConveyorBelts(const SDL_FPoint& p) -> bool { return std::ranges::any_of(conveyor_belt_floors_, [&](const auto& s) { return checkCollision(s, p); }); @@ -178,7 +178,7 @@ auto CollisionMap::checkLeftSlopes(const LineVertical& line) -> int { } // Comprueba las colisiones punto con rampas izquierdas -auto CollisionMap::checkLeftSlopes(SDL_FPoint& p) -> bool { +auto CollisionMap::checkLeftSlopes(const SDL_FPoint& p) -> bool { return std::ranges::any_of(left_slopes_, [&](const auto& slope) { return checkCollision(p, slope); }); @@ -196,7 +196,7 @@ auto CollisionMap::checkRightSlopes(const LineVertical& line) -> int { } // Comprueba las colisiones punto con rampas derechas -auto CollisionMap::checkRightSlopes(SDL_FPoint& p) -> bool { +auto CollisionMap::checkRightSlopes(const SDL_FPoint& p) -> bool { return std::ranges::any_of(right_slopes_, [&](const auto& slope) { return checkCollision(p, slope); }); diff --git a/source/game/gameplay/collision_map.hpp b/source/game/gameplay/collision_map.hpp index 4866a09..e17e741 100644 --- a/source/game/gameplay/collision_map.hpp +++ b/source/game/gameplay/collision_map.hpp @@ -48,21 +48,21 @@ class CollisionMap { auto getTile(int index) const -> Tile; // Devuelve el tipo de tile en un índice del tilemap // --- Queries de colisión con superficies --- - auto checkRightSurfaces(SDL_FRect& rect) -> int; // Colisión con paredes derechas (retorna X) - auto checkLeftSurfaces(SDL_FRect& rect) -> int; // Colisión con paredes izquierdas (retorna X) - auto checkTopSurfaces(SDL_FRect& rect) -> int; // Colisión con techos (retorna Y) - auto checkTopSurfaces(SDL_FPoint& p) -> bool; // Colisión punto con techos - auto checkBottomSurfaces(SDL_FRect& rect) -> int; // Colisión con suelos (retorna Y) + auto checkRightSurfaces(const SDL_FRect& rect) -> int; // Colisión con paredes derechas (retorna X) + auto checkLeftSurfaces(const SDL_FRect& rect) -> int; // Colisión con paredes izquierdas (retorna X) + auto checkTopSurfaces(const SDL_FRect& rect) -> int; // Colisión con techos (retorna Y) + auto checkTopSurfaces(const SDL_FPoint& p) -> bool; // Colisión punto con techos + auto checkBottomSurfaces(const SDL_FRect& rect) -> int; // Colisión con suelos (retorna Y) // --- Queries de colisión con superficies automáticas (conveyor belts) --- - auto checkAutoSurfaces(SDL_FRect& rect) -> int; // Colisión con conveyor belts (retorna Y) - auto checkConveyorBelts(SDL_FPoint& p) -> bool; // Colisión punto con conveyor belts + auto checkAutoSurfaces(const SDL_FRect& rect) -> int; // Colisión con conveyor belts (retorna Y) + auto checkConveyorBelts(const SDL_FPoint& p) -> bool; // Colisión punto con conveyor belts // --- Queries de colisión con rampas --- auto checkLeftSlopes(const LineVertical& line) -> int; // Colisión línea con rampas izquierdas (retorna Y) - auto checkLeftSlopes(SDL_FPoint& p) -> bool; // Colisión punto con rampas izquierdas + auto checkLeftSlopes(const SDL_FPoint& p) -> bool; // Colisión punto con rampas izquierdas auto checkRightSlopes(const LineVertical& line) -> int; // Colisión línea con rampas derechas (retorna Y) - auto checkRightSlopes(SDL_FPoint& p) -> bool; // Colisión punto con rampas derechas + auto checkRightSlopes(const SDL_FPoint& p) -> bool; // Colisión punto con rampas derechas // --- Métodos estáticos --- static auto getTileSize() -> int { return TILE_SIZE; } // Tamaño del tile en pixels diff --git a/source/game/gameplay/room.cpp b/source/game/gameplay/room.cpp index 4df865b..f94a62e 100644 --- a/source/game/gameplay/room.cpp +++ b/source/game/gameplay/room.cpp @@ -192,37 +192,37 @@ auto Room::getSlopeHeight(SDL_FPoint p, Tile slope) -> int { // === Métodos de colisión (delegados a CollisionMap) === // Comprueba las colisiones con paredes derechas -auto Room::checkRightSurfaces(SDL_FRect& rect) -> int { +auto Room::checkRightSurfaces(const SDL_FRect& rect) -> int { return collision_map_->checkRightSurfaces(rect); } // Comprueba las colisiones con paredes izquierdas -auto Room::checkLeftSurfaces(SDL_FRect& rect) -> int { +auto Room::checkLeftSurfaces(const SDL_FRect& rect) -> int { return collision_map_->checkLeftSurfaces(rect); } // Comprueba las colisiones con techos -auto Room::checkTopSurfaces(SDL_FRect& rect) -> int { +auto Room::checkTopSurfaces(const SDL_FRect& rect) -> int { return collision_map_->checkTopSurfaces(rect); } // Comprueba las colisiones punto con techos -auto Room::checkTopSurfaces(SDL_FPoint& p) -> bool { +auto Room::checkTopSurfaces(const SDL_FPoint& p) -> bool { return collision_map_->checkTopSurfaces(p); } // Comprueba las colisiones con suelos -auto Room::checkBottomSurfaces(SDL_FRect& rect) -> int { +auto Room::checkBottomSurfaces(const SDL_FRect& rect) -> int { return collision_map_->checkBottomSurfaces(rect); } // Comprueba las colisiones con conveyor belts -auto Room::checkAutoSurfaces(SDL_FRect& rect) -> int { +auto Room::checkAutoSurfaces(const SDL_FRect& rect) -> int { return collision_map_->checkAutoSurfaces(rect); } // Comprueba las colisiones punto con conveyor belts -auto Room::checkConveyorBelts(SDL_FPoint& p) -> bool { +auto Room::checkConveyorBelts(const SDL_FPoint& p) -> bool { return collision_map_->checkConveyorBelts(p); } @@ -232,7 +232,7 @@ auto Room::checkLeftSlopes(const LineVertical& line) -> int { } // Comprueba las colisiones punto con rampas izquierdas -auto Room::checkLeftSlopes(SDL_FPoint& p) -> bool { +auto Room::checkLeftSlopes(const SDL_FPoint& p) -> bool { return collision_map_->checkLeftSlopes(p); } @@ -242,7 +242,7 @@ auto Room::checkRightSlopes(const LineVertical& line) -> int { } // Comprueba las colisiones punto con rampas derechas -auto Room::checkRightSlopes(SDL_FPoint& p) -> bool { +auto Room::checkRightSlopes(const SDL_FPoint& p) -> bool { return collision_map_->checkRightSlopes(p); } diff --git a/source/game/gameplay/room.hpp b/source/game/gameplay/room.hpp index 15d621c..1c9419b 100644 --- a/source/game/gameplay/room.hpp +++ b/source/game/gameplay/room.hpp @@ -75,17 +75,17 @@ class Room { auto itemCollision(SDL_FRect& rect) -> bool; // Indica si hay colision con un objeto a partir de un rectangulo static auto getTileSize() -> int { return TILE_SIZE; } // Obten el tamaño del tile static auto getSlopeHeight(SDL_FPoint p, Tile slope) -> int; // Obten la coordenada de la cuesta a partir de un punto perteneciente a ese tile - auto checkRightSurfaces(SDL_FRect& rect) -> int; // Comprueba las colisiones - auto checkLeftSurfaces(SDL_FRect& rect) -> int; // Comprueba las colisiones - auto checkTopSurfaces(SDL_FRect& rect) -> int; // Comprueba las colisiones - auto checkBottomSurfaces(SDL_FRect& rect) -> int; // Comprueba las colisiones - auto checkAutoSurfaces(SDL_FRect& rect) -> int; // Comprueba las colisiones - auto checkTopSurfaces(SDL_FPoint& p) -> bool; // Comprueba las colisiones - auto checkConveyorBelts(SDL_FPoint& p) -> bool; // Comprueba las colisiones + auto checkRightSurfaces(const SDL_FRect& rect) -> int; // Comprueba las colisiones + auto checkLeftSurfaces(const SDL_FRect& rect) -> int; // Comprueba las colisiones + auto checkTopSurfaces(const SDL_FRect& rect) -> int; // Comprueba las colisiones + auto checkBottomSurfaces(const SDL_FRect& rect) -> int; // Comprueba las colisiones + auto checkAutoSurfaces(const SDL_FRect& rect) -> int; // Comprueba las colisiones + auto checkTopSurfaces(const SDL_FPoint& p) -> bool; // Comprueba las colisiones + auto checkConveyorBelts(const SDL_FPoint& p) -> bool; // Comprueba las colisiones auto checkLeftSlopes(const LineVertical& line) -> int; // Comprueba las colisiones - auto checkLeftSlopes(SDL_FPoint& p) -> bool; // Comprueba las colisiones + auto checkLeftSlopes(const SDL_FPoint& p) -> bool; // Comprueba las colisiones auto checkRightSlopes(const LineVertical& line) -> int; // Comprueba las colisiones - auto checkRightSlopes(SDL_FPoint& p) -> bool; // Comprueba las colisiones + auto checkRightSlopes(const SDL_FPoint& p) -> bool; // Comprueba las colisiones void setPaused(bool value); // Pone el mapa en modo pausa [[nodiscard]] auto getConveyorBeltDirection() const -> int { return conveyor_belt_direction_; } // Obten la direccion de las superficies automaticas diff --git a/source/game/scene_manager.hpp b/source/game/scene_manager.hpp index ceabe82..62e79ae 100644 --- a/source/game/scene_manager.hpp +++ b/source/game/scene_manager.hpp @@ -34,7 +34,7 @@ enum class Options { // --- Variables de estado globales --- #ifdef _DEBUG -inline Scene current = Scene::LOADING_SCREEN; // Escena actual +inline Scene current = Scene::GAME; // Escena actual inline Options options = Options::LOGO_TO_LOADING_SCREEN; // Opciones de la escena actual #else inline Scene current = Scene::LOGO; // Escena actual diff --git a/source/utils/defines.hpp b/source/utils/defines.hpp index d38b5b8..c8d2975 100644 --- a/source/utils/defines.hpp +++ b/source/utils/defines.hpp @@ -46,4 +46,10 @@ constexpr int GAMECANVAS_THIRD_QUARTER_Y = (GAMECANVAS_HEIGHT / 4) * 3; namespace Collision { constexpr int NONE = -1; +} + +namespace Flip +{ + constexpr SDL_FlipMode LEFT = SDL_FLIP_HORIZONTAL; + constexpr SDL_FlipMode RIGHT = SDL_FLIP_NONE; } \ No newline at end of file