Compare commits
2 Commits
29e0daffb0
...
8893e8f05b
| Author | SHA1 | Date | |
|---|---|---|---|
| 8893e8f05b | |||
| 7f51f02d96 |
@@ -29,6 +29,7 @@ Player::Player(const Data& player)
|
||||
// Pinta el jugador en pantalla
|
||||
void Player::render() {
|
||||
sprite_->render(1, color_);
|
||||
Screen::get()->getRendererSurface()->putPixel(under_right_foot_.x, under_right_foot_.y, 6);
|
||||
}
|
||||
|
||||
// Actualiza las variables del objeto
|
||||
@@ -96,33 +97,41 @@ void Player::transitionToState(State state) {
|
||||
|
||||
switch (state) {
|
||||
case State::ON_GROUND:
|
||||
std::cout << "ON_GROUND\n";
|
||||
vy_ = 0;
|
||||
handleDeathByFalling();
|
||||
jump_sound_ctrl_.reset();
|
||||
fall_sound_ctrl_.reset();
|
||||
current_slope_ = nullptr;
|
||||
break;
|
||||
case State::ON_SLOPE:
|
||||
case State::ON_SLOPE:
|
||||
std::cout << "ON_SLOPE\n";
|
||||
vy_ = 0;
|
||||
handleDeathByFalling();
|
||||
jump_sound_ctrl_.reset();
|
||||
fall_sound_ctrl_.reset();
|
||||
updateCurrentSlope();
|
||||
break;
|
||||
case State::JUMPING:
|
||||
std::cout << "JUMPING\n";
|
||||
// Puede saltar desde ON_GROUND o ON_SLOPE
|
||||
if (previous_state_ == State::ON_GROUND || previous_state_ == State::ON_SLOPE) {
|
||||
vy_ = -MAX_VY;
|
||||
last_grounded_position_ = y_;
|
||||
updateVelocity();
|
||||
jump_sound_ctrl_.start();
|
||||
current_slope_ = nullptr;
|
||||
}
|
||||
break;
|
||||
case State::FALLING:
|
||||
std::cout << "FALLING\n";
|
||||
fall_start_position_ = static_cast<int>(y_);
|
||||
last_grounded_position_ = static_cast<int>(y_);
|
||||
vy_ = MAX_VY;
|
||||
vx_ = 0.0F;
|
||||
jump_sound_ctrl_.reset();
|
||||
fall_sound_ctrl_.start(y_);
|
||||
current_slope_ = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -156,8 +165,10 @@ void Player::updateOnGround(float delta_time) {
|
||||
|
||||
// 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
|
||||
handleShouldFall(); // Verifica si debe caer (no tiene suelo)
|
||||
(void)delta_time; // No usado en este método, pero se mantiene por consistencia
|
||||
|
||||
// NOTA: No llamamos handleShouldFall() aquí porque moveOnSlope() ya maneja
|
||||
// todas las condiciones de salida de la rampa (out of bounds, transición a superficie plana)
|
||||
|
||||
// Verifica si el jugador quiere saltar
|
||||
if (wannaJump) { transitionToState(State::JUMPING); }
|
||||
@@ -183,6 +194,7 @@ void Player::moveOnGround(float delta_time) {
|
||||
|
||||
if (vx_ == 0.0F) { return; }
|
||||
|
||||
// Movimiento horizontal y colision con muros
|
||||
const float DISPLACEMENT = vx_ * delta_time;
|
||||
if (vx_ < 0.0F) {
|
||||
const SDL_FRect PROJECTION = getProjection(Direction::LEFT, DISPLACEMENT);
|
||||
@@ -201,6 +213,21 @@ void Player::moveOnGround(float delta_time) {
|
||||
x_ = POS - WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba colision con rampas, corrige y cambia estado
|
||||
const int SIDE_X = vx_ < 0.0F ? static_cast<int>(x_) : static_cast<int>(x_) + WIDTH - 1;
|
||||
const LineVertical SIDE = {
|
||||
.x = SIDE_X,
|
||||
.y1 = static_cast<int>(y_) + HEIGHT - 2,
|
||||
.y2 = static_cast<int>(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
|
||||
y_ = SLOPE_Y - HEIGHT;
|
||||
transitionToState(State::ON_SLOPE);
|
||||
}
|
||||
}
|
||||
|
||||
// Movimiento físico del estado ON_SLOPE
|
||||
@@ -210,7 +237,16 @@ void Player::moveOnSlope(float delta_time) {
|
||||
|
||||
if (vx_ == 0.0F) { return; }
|
||||
|
||||
// Movimiento horizontal
|
||||
// Verificar que tenemos una rampa válida
|
||||
if (current_slope_ == nullptr) {
|
||||
transitionToState(State::FALLING);
|
||||
return;
|
||||
}
|
||||
|
||||
// Determinar el tipo de rampa
|
||||
const bool IS_LEFT_SLOPE = isLeftSlope();
|
||||
|
||||
// Movimiento horizontal con colisión lateral
|
||||
const float DISPLACEMENT = vx_ * delta_time;
|
||||
if (vx_ < 0.0F) {
|
||||
const SDL_FRect PROJECTION = getProjection(Direction::LEFT, DISPLACEMENT);
|
||||
@@ -230,44 +266,62 @@ void Player::moveOnSlope(float delta_time) {
|
||||
}
|
||||
}
|
||||
|
||||
// Movimiento vertical
|
||||
// Regla: Si está bajando la rampa, se pega a la slope
|
||||
if (isOnDownSlope()) {
|
||||
y_ += 1;
|
||||
// Actualizar posición de los pies para cálculos
|
||||
updateFeet();
|
||||
|
||||
// Seleccionar el pie apropiado según el tipo de rampa
|
||||
// Left slopes (forma \) colisionan con el pie izquierdo
|
||||
// Right slopes (forma /) colisionan con el pie derecho
|
||||
const float FOOT_X = IS_LEFT_SLOPE ? under_left_foot_.x : under_right_foot_.x;
|
||||
|
||||
// Calcular la Y del pie basado en la ecuación de la rampa (45 grados)
|
||||
// Left slope (\): y aumenta con x -> y = y1 + (x - x1)
|
||||
// Right slope (/): y disminuye con x -> y = y1 - (x - x1)
|
||||
float foot_y = 0.0F;
|
||||
if (IS_LEFT_SLOPE) {
|
||||
foot_y = current_slope_->y1 + (FOOT_X - current_slope_->x1);
|
||||
} else {
|
||||
foot_y = current_slope_->y1 - (FOOT_X - current_slope_->x1);
|
||||
}
|
||||
|
||||
// Ajustar la posición Y del jugador (restar HEIGHT porque foot_y es la posición del pie)
|
||||
y_ = foot_y - HEIGHT;
|
||||
|
||||
// Verificar si el pie ha salido de los límites horizontales de la rampa
|
||||
// Usar min/max porque LEFT slopes tienen x1<x2 pero RIGHT slopes tienen x1>x2
|
||||
const int MIN_X = std::min(current_slope_->x1, current_slope_->x2);
|
||||
const int MAX_X = std::max(current_slope_->x1, current_slope_->x2);
|
||||
const bool OUT_OF_BOUNDS = (FOOT_X < MIN_X) || (FOOT_X > MAX_X);
|
||||
|
||||
if (OUT_OF_BOUNDS) {
|
||||
// Determinar si estamos saliendo por arriba o por abajo de la rampa
|
||||
const bool EXITING_DOWNWARD = (FOOT_X > current_slope_->x2 && IS_LEFT_SLOPE) ||
|
||||
(FOOT_X < current_slope_->x1 && !IS_LEFT_SLOPE);
|
||||
|
||||
if (EXITING_DOWNWARD) {
|
||||
// Salida por abajo: bajar 1 pixel para ayudar detección de suelo
|
||||
y_ += 1.0F;
|
||||
}
|
||||
// Si sale por arriba, mantener altura (ya está en foot_y - HEIGHT)
|
||||
|
||||
// El jugador ya no está en la rampa, limpiar referencia
|
||||
current_slope_ = nullptr;
|
||||
|
||||
// Verificar si hay soporte debajo (suelo plano, conveyor belt, u otra slope)
|
||||
if (isOnFloor()) {
|
||||
// Hay soporte: transición a ON_GROUND (podría ser superficie, conveyor u otra rampa)
|
||||
transitionToState(State::ON_GROUND);
|
||||
} else {
|
||||
// Sin soporte: empezar a caer
|
||||
transitionToState(State::FALLING);
|
||||
}
|
||||
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<int>(x_) : static_cast<int>(x_) + WIDTH - 1;
|
||||
const LineVertical SIDE = {
|
||||
.x = SIDE_X,
|
||||
.y1 = static_cast<int>(y_) + HEIGHT - 2,
|
||||
.y2 = static_cast<int>(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 ---
|
||||
// Verificar transición a superficie plana
|
||||
if (isOnTopSurface()) {
|
||||
transitionToState(State::ON_GROUND);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,21 +458,6 @@ void Player::handleBorders() {
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba el estado del jugador
|
||||
void Player::handleState(float delta_time) {
|
||||
// Reproduce sonidos según el estado
|
||||
if (state_ == State::FALLING) {
|
||||
playFallSound(delta_time);
|
||||
} else if (state_ == State::ON_GROUND || state_ == State::ON_SLOPE) {
|
||||
// Si no tiene suelo debajo y no está en rampa descendente -> FALLING
|
||||
if (!isOnFloor() && !isOnConveyorBelt() && !isOnDownSlope()) {
|
||||
transitionToState(State::FALLING); // setState() establece vx_=0, vy_=MAX_VY
|
||||
}
|
||||
} else if (state_ == State::JUMPING) {
|
||||
playJumpSound(delta_time);
|
||||
}
|
||||
}
|
||||
|
||||
// Cambia al jugador de un borde al opuesto. Util para el cambio de pantalla
|
||||
void Player::switchBorders() {
|
||||
switch (border_) {
|
||||
@@ -541,35 +580,66 @@ auto Player::isOnConveyorBelt() -> bool {
|
||||
return on_conveyor_belt;
|
||||
}
|
||||
|
||||
// Comprueba si el jugador está sobre una rampa hacia abajo
|
||||
auto Player::isOnDownSlope() -> bool {
|
||||
bool on_slope = false;
|
||||
// Comprueba si el jugador está sobre una rampa
|
||||
// Retorna true SOLO si un pie está en rampa Y el otro pie está volando (sin soporte)
|
||||
auto Player::isOnSlope() -> bool {
|
||||
updateFeet();
|
||||
|
||||
// Cuando el jugador baja una escalera, se queda volando
|
||||
// Hay que mirar otro pixel más por debajo
|
||||
SDL_FPoint left_foot = under_left_foot_;
|
||||
SDL_FPoint right_foot = under_right_foot_;
|
||||
left_foot.y += 1.0F;
|
||||
right_foot.y += 1.0F;
|
||||
// Verificar qué pie está en qué tipo de rampa
|
||||
const bool LEFT_FOOT_ON_LEFT_SLOPE = room_->checkLeftSlopes(under_left_foot_);
|
||||
const bool RIGHT_FOOT_ON_RIGHT_SLOPE = room_->checkRightSlopes(under_right_foot_);
|
||||
|
||||
// Comprueba las rampas
|
||||
on_slope |= room_->checkLeftSlopes(left_foot);
|
||||
on_slope |= room_->checkRightSlopes(right_foot);
|
||||
// Verificar si cada pie está "volando" (sin soporte: ni top surface ni conveyor belt)
|
||||
const bool LEFT_FOOT_FLYING = !(room_->checkTopSurfaces(under_left_foot_) ||
|
||||
room_->checkConveyorBelts(under_left_foot_));
|
||||
const bool RIGHT_FOOT_FLYING = !(room_->checkTopSurfaces(under_right_foot_) ||
|
||||
room_->checkConveyorBelts(under_right_foot_));
|
||||
|
||||
return on_slope;
|
||||
// Retornar true si UN pie en rampa Y el OTRO volando
|
||||
return (LEFT_FOOT_ON_LEFT_SLOPE && RIGHT_FOOT_FLYING) ||
|
||||
(RIGHT_FOOT_ON_RIGHT_SLOPE && LEFT_FOOT_FLYING);
|
||||
}
|
||||
|
||||
// Comprueba si el jugador está sobre una rampa
|
||||
auto Player::isOnSlope() -> bool {
|
||||
bool on_slope = false;
|
||||
// Comprueba si current_slope_ es una rampa izquierda (ascendente a la izquierda)
|
||||
// Las rampas izquierdas tienen forma \ con x1 < x2 (x aumenta de izq a der)
|
||||
auto Player::isLeftSlope() -> bool {
|
||||
if (current_slope_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
// Left slopes (\): x1 < x2 (x aumenta de izquierda a derecha)
|
||||
// Right slopes (/): x1 > x2 (x decrece de izquierda a derecha)
|
||||
return current_slope_->x1 < current_slope_->x2;
|
||||
}
|
||||
|
||||
// Actualiza current_slope_ con la rampa correcta según el pie que toca
|
||||
void Player::updateCurrentSlope() {
|
||||
updateFeet();
|
||||
|
||||
// Left slopes (\) ascendentes a izquierda tocan el pie izquierdo
|
||||
if (room_->checkLeftSlopes(under_left_foot_)) {
|
||||
current_slope_ = room_->getSlopeAtPoint(under_left_foot_);
|
||||
}
|
||||
// Right slopes (/) ascendentes a derecha tocan el pie derecho
|
||||
else if (room_->checkRightSlopes(under_right_foot_)) {
|
||||
current_slope_ = room_->getSlopeAtPoint(under_right_foot_);
|
||||
}
|
||||
// Fallback para casos edge
|
||||
else {
|
||||
current_slope_ = room_->getSlopeAtPoint(under_left_foot_);
|
||||
if (current_slope_ == nullptr) {
|
||||
current_slope_ = room_->getSlopeAtPoint(under_right_foot_);
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba las rampas
|
||||
on_slope |= room_->checkLeftSlopes(under_left_foot_);
|
||||
on_slope |= room_->checkRightSlopes(under_right_foot_);
|
||||
|
||||
return on_slope;
|
||||
// Debug output
|
||||
if (current_slope_ != nullptr) {
|
||||
const char* TYPE = isLeftSlope() ? "Left \\" : "Right /";
|
||||
std::cout << "[SLOPE] " << TYPE
|
||||
<< " from (" << current_slope_->x1 << "," << current_slope_->y1 << ")"
|
||||
<< " to (" << current_slope_->x2 << "," << current_slope_->y2 << ")\n";
|
||||
} else {
|
||||
std::cout << "[SLOPE] nullptr\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba que el jugador no toque ningun tile de los que matan
|
||||
|
||||
@@ -132,6 +132,7 @@ class Player {
|
||||
std::array<SDL_FPoint, 8> collider_points_{}; // Puntos de colisión con el mapa
|
||||
SDL_FPoint under_left_foot_ = {0.0F, 0.0F}; // El punto bajo la esquina inferior izquierda del jugador
|
||||
SDL_FPoint under_right_foot_ = {0.0F, 0.0F}; // El punto bajo la esquina inferior derecha del jugador
|
||||
const LineDiagonal* current_slope_{nullptr}; // Rampa actual sobe la que está el jugador
|
||||
|
||||
// --- Variables de juego ---
|
||||
bool is_on_border_ = false; // Indica si el jugador esta en uno de los cuatro bordes de la pantalla
|
||||
@@ -174,7 +175,6 @@ class Player {
|
||||
void handleInput(); // Comprueba las entradas y modifica variables
|
||||
|
||||
// --- Funciones de gestión de estado ---
|
||||
void handleState(float delta_time); // Comprueba el estado del jugador y actualiza variables
|
||||
void transitionToState(State state); // Cambia el estado del jugador
|
||||
|
||||
// --- Funciones de física ---
|
||||
@@ -188,8 +188,9 @@ class Player {
|
||||
auto isOnFloor() -> bool; // Comprueba si el jugador tiene suelo debajo de los pies
|
||||
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
|
||||
auto isLeftSlope() -> bool; // Comprueba si current_slope_ es una rampa izquierda (ascendente a la izquierda)
|
||||
void updateCurrentSlope(); // Actualiza current_slope_ con la rampa correcta y muestra debug info
|
||||
|
||||
// --- Funciones de actualización de geometría ---
|
||||
void syncSpriteAndCollider(); // Actualiza collider_box y collision points
|
||||
|
||||
@@ -202,6 +202,26 @@ auto CollisionMap::checkRightSlopes(const SDL_FPoint& p) -> bool {
|
||||
});
|
||||
}
|
||||
|
||||
// Obtiene puntero a slope en un punto (prioriza left_slopes_ sobre right_slopes_)
|
||||
auto CollisionMap::getSlopeAtPoint(const SDL_FPoint& p) const -> const LineDiagonal* {
|
||||
// Primero busca en rampas izquierdas
|
||||
for (const auto& slope : left_slopes_) {
|
||||
if (checkCollision(p, slope)) {
|
||||
return &slope;
|
||||
}
|
||||
}
|
||||
|
||||
// Luego busca en rampas derechas
|
||||
for (const auto& slope : right_slopes_) {
|
||||
if (checkCollision(p, slope)) {
|
||||
return &slope;
|
||||
}
|
||||
}
|
||||
|
||||
// No hay colisión con ninguna slope
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// === Helpers para recopilar tiles ===
|
||||
|
||||
// Helper: recopila tiles inferiores (muros sin muro debajo)
|
||||
|
||||
@@ -63,6 +63,7 @@ class CollisionMap {
|
||||
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(const SDL_FPoint& p) -> bool; // Colisión punto con rampas derechas
|
||||
auto getSlopeAtPoint(const SDL_FPoint& p) const -> const LineDiagonal*; // Obtiene puntero a slope en un punto
|
||||
|
||||
// --- Métodos estáticos ---
|
||||
static auto getTileSize() -> int { return TILE_SIZE; } // Tamaño del tile en pixels
|
||||
|
||||
@@ -246,6 +246,11 @@ auto Room::checkRightSlopes(const SDL_FPoint& p) -> bool {
|
||||
return collision_map_->checkRightSlopes(p);
|
||||
}
|
||||
|
||||
// Obtiene puntero a slope en un punto
|
||||
auto Room::getSlopeAtPoint(const SDL_FPoint& p) const -> const LineDiagonal* {
|
||||
return collision_map_->getSlopeAtPoint(p);
|
||||
}
|
||||
|
||||
|
||||
// Carga las variables desde un fichero de mapa (delegado a RoomLoader)
|
||||
auto Room::loadRoomFile(const std::string& file_path, bool verbose) -> Data {
|
||||
|
||||
@@ -86,6 +86,7 @@ class Room {
|
||||
auto checkLeftSlopes(const SDL_FPoint& p) -> bool; // Comprueba las colisiones
|
||||
auto checkRightSlopes(const LineVertical& line) -> int; // Comprueba las colisiones
|
||||
auto checkRightSlopes(const SDL_FPoint& p) -> bool; // Comprueba las colisiones
|
||||
auto getSlopeAtPoint(const SDL_FPoint& p) const -> const LineDiagonal*; // Obtiene puntero a slope en un punto
|
||||
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user