migrades portes i plataformes a solidActor
This commit is contained in:
@@ -9,7 +9,9 @@
|
||||
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||
#include "game/defaults.hpp" // Para Defaults::Game::Player, Defaults::Sound::Files
|
||||
#include "game/entities/solid_actor.hpp" // Para SolidActor
|
||||
#include "game/gameplay/room.hpp" // Para Room
|
||||
#include "game/gameplay/solid_actor_manager.hpp" // Para SolidActorManager
|
||||
#include "game/gameplay/tile_collider.hpp" // Para TileCollider
|
||||
#include "game/options.hpp" // Para Cheat, Options
|
||||
#include "utils/defines.hpp" // Para PlayArea, Collision
|
||||
@@ -18,6 +20,29 @@
|
||||
#include "core/system/debug.hpp" // Para Debug
|
||||
#endif
|
||||
|
||||
// Helpers NONE-aware para combinar un hit del TileCollider con un hit del
|
||||
// SolidActorManager. Devuelven el valor más "clampante" (más a la derecha
|
||||
// para left-wall, más a la izquierda para right-wall, más abajo para ceiling).
|
||||
namespace {
|
||||
auto combineLeftWall(float tile_hit, float actor_hit) -> float {
|
||||
if (tile_hit == Collision::NONE) { return actor_hit; }
|
||||
if (actor_hit == Collision::NONE) { return tile_hit; }
|
||||
return std::max(tile_hit, actor_hit);
|
||||
}
|
||||
|
||||
auto combineRightWall(float tile_hit, float actor_hit) -> float {
|
||||
if (tile_hit == Collision::NONE) { return actor_hit; }
|
||||
if (actor_hit == Collision::NONE) { return tile_hit; }
|
||||
return std::min(tile_hit, actor_hit);
|
||||
}
|
||||
|
||||
auto combineCeiling(float tile_hit, float actor_hit) -> float {
|
||||
if (tile_hit == Collision::NONE) { return actor_hit; }
|
||||
if (actor_hit == Collision::NONE) { return tile_hit; }
|
||||
return std::max(tile_hit, actor_hit);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// ============================================================================
|
||||
// Constructor
|
||||
// ============================================================================
|
||||
@@ -46,6 +71,16 @@ void Player::render() {
|
||||
void Player::update(float delta_time) {
|
||||
if (is_paused_) { return; }
|
||||
|
||||
// 0. Carry de plataforma móvil (SolidActor con CARRY_ON_TOP).
|
||||
// Snap absoluto del eje Y al top del AABB y desplazamiento horizontal
|
||||
// por el delta del último frame del actor. Esto mantiene al Player
|
||||
// pegado a la plataforma cuando sube/baja y lo arrastra lateralmente.
|
||||
if (current_carrier_ != nullptr) {
|
||||
const auto& aabb = current_carrier_->getAABB();
|
||||
x_ += current_carrier_->getLastDelta().x;
|
||||
y_ = aabb.y - HEIGHT;
|
||||
}
|
||||
|
||||
// 1. Leer input
|
||||
handleInput();
|
||||
|
||||
@@ -236,34 +271,48 @@ void Player::startJump() {
|
||||
// Fase 4a: Movimiento horizontal
|
||||
// ============================================================================
|
||||
|
||||
void Player::moveHorizontal(float delta_time) {
|
||||
// Early exit del movimiento horizontal si el player ya está pegado a una
|
||||
// pared en su dirección de movimiento. Sin esto, el player choca pero
|
||||
// conserva vx_ != 0 y animate() reproduce "walk" continuamente.
|
||||
auto Player::stuckAgainstWall() const -> bool {
|
||||
const auto& tc = room_->getTileCollider();
|
||||
|
||||
// Early exit: si hay pared inmediata en la dirección de movimiento, parar
|
||||
// y poner vx_=0. Sin esto, el player choca, queda re-posicionado en el
|
||||
// mismo sitio pero conserva vx_ != 0, así que animate() reproduce walk
|
||||
// anim continuamente mientras empuja contra la pared.
|
||||
if (vx_ > 0.0F && tc.checkWallRight(x_, y_, WIDTH, HEIGHT) != Collision::NONE) {
|
||||
vx_ = 0.0F;
|
||||
return;
|
||||
const auto& sm = room_->getSolidActors();
|
||||
if (vx_ > 0.0F) {
|
||||
return tc.checkWallRight(x_, y_, WIDTH, HEIGHT) != Collision::NONE ||
|
||||
sm.checkWallRight(x_, y_, WIDTH, HEIGHT) != Collision::NONE;
|
||||
}
|
||||
if (vx_ < 0.0F && tc.checkWallLeft(x_, y_, WIDTH, HEIGHT) != Collision::NONE) {
|
||||
if (vx_ < 0.0F) {
|
||||
return tc.checkWallLeft(x_, y_, WIDTH, HEIGHT) != Collision::NONE ||
|
||||
sm.checkWallLeft(x_, y_, WIDTH, HEIGHT) != Collision::NONE;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Player::moveHorizontal(float delta_time) {
|
||||
if (stuckAgainstWall()) {
|
||||
vx_ = 0.0F;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& tc = room_->getTileCollider();
|
||||
const auto& sm = room_->getSolidActors();
|
||||
float new_x = x_ + (vx_ * delta_time);
|
||||
|
||||
// Comprobar ambos muros siempre (el tilemap extendido incluye paredes de rooms
|
||||
// adyacentes; comprobar ambos lados evita solapamiento en zona de borde)
|
||||
float wall = tc.checkWallLeft(new_x, y_, WIDTH, HEIGHT);
|
||||
if (wall != Collision::NONE && wall > new_x) {
|
||||
new_x = wall;
|
||||
// adyacentes; comprobar ambos lados evita solapamiento en zona de borde).
|
||||
// Se combinan tiles + solid actors en cada lado tomando el muro más "clampante".
|
||||
const float LEFT_WALL = combineLeftWall(
|
||||
tc.checkWallLeft(new_x, y_, WIDTH, HEIGHT),
|
||||
sm.checkWallLeft(new_x, y_, WIDTH, HEIGHT));
|
||||
if (LEFT_WALL != Collision::NONE && LEFT_WALL > new_x) {
|
||||
new_x = LEFT_WALL;
|
||||
}
|
||||
wall = tc.checkWallRight(new_x, y_, WIDTH, HEIGHT);
|
||||
if (wall != Collision::NONE) {
|
||||
float corrected = wall - WIDTH;
|
||||
if (corrected < new_x) { new_x = corrected; }
|
||||
const float RIGHT_WALL = combineRightWall(
|
||||
tc.checkWallRight(new_x, y_, WIDTH, HEIGHT),
|
||||
sm.checkWallRight(new_x, y_, WIDTH, HEIGHT));
|
||||
if (RIGHT_WALL != Collision::NONE) {
|
||||
const float CORRECTED = RIGHT_WALL - WIDTH;
|
||||
new_x = std::min(new_x, CORRECTED);
|
||||
}
|
||||
|
||||
x_ = new_x;
|
||||
@@ -355,44 +404,74 @@ void Player::detectSlopeEntry() {
|
||||
// Fase 4b: Movimiento vertical
|
||||
// ============================================================================
|
||||
|
||||
// Subiendo: comprobar techo (tiles + solid actors). Si hay colisión,
|
||||
// snap y parar vy.
|
||||
void Player::moveVerticalUp(float displacement) {
|
||||
const auto& tc = room_->getTileCollider();
|
||||
const auto& sm = room_->getSolidActors();
|
||||
const float NEW_Y = y_ + displacement;
|
||||
const float CEILING = combineCeiling(
|
||||
tc.checkCeiling(x_, NEW_Y, WIDTH),
|
||||
sm.checkCeiling(x_, NEW_Y, WIDTH));
|
||||
if (CEILING != Collision::NONE) {
|
||||
y_ = CEILING;
|
||||
vy_ = 0.0F;
|
||||
} else {
|
||||
y_ = NEW_Y;
|
||||
}
|
||||
}
|
||||
|
||||
// Bajando: comprobar suelo en tiles y en solid actors; el que esté antes
|
||||
// (menor y) gana. Si es un SolidActor con CARRY_ON_TOP, guarda el carrier.
|
||||
void Player::moveVerticalDown(float displacement) {
|
||||
const auto& tc = room_->getTileCollider();
|
||||
const auto& sm = room_->getSolidActors();
|
||||
const float FOOT_Y = y_ + HEIGHT;
|
||||
const float NEW_FOOT_Y = FOOT_Y + displacement;
|
||||
const auto TILE_HIT = tc.checkFloor(x_, FOOT_Y, WIDTH, NEW_FOOT_Y);
|
||||
const auto ACTOR_HIT = sm.checkFloor(x_, FOOT_Y, WIDTH, NEW_FOOT_Y);
|
||||
|
||||
// El tile tiene prioridad si está igual o más arriba que el actor.
|
||||
const bool TILE_WINS = (TILE_HIT.y != Collision::NONE) &&
|
||||
(ACTOR_HIT.y == Collision::NONE || TILE_HIT.y <= ACTOR_HIT.y);
|
||||
const bool ACTOR_WINS = !TILE_WINS && (ACTOR_HIT.y != Collision::NONE);
|
||||
|
||||
if (TILE_WINS) {
|
||||
y_ = TILE_HIT.y - HEIGHT;
|
||||
const bool IS_SLOPE = TILE_HIT.type == TileCollider::Tile::SLOPE_L ||
|
||||
TILE_HIT.type == TileCollider::Tile::SLOPE_R;
|
||||
if (IS_SLOPE) {
|
||||
slope_tile_x_ = TILE_HIT.tile_x;
|
||||
slope_tile_y_ = TILE_HIT.tile_y;
|
||||
slope_type_ = TILE_HIT.type;
|
||||
transitionToState(State::ON_SLOPE);
|
||||
} else {
|
||||
transitionToState(State::ON_GROUND);
|
||||
}
|
||||
current_carrier_ = nullptr;
|
||||
return;
|
||||
}
|
||||
if (ACTOR_WINS) {
|
||||
y_ = ACTOR_HIT.y - HEIGHT;
|
||||
transitionToState(State::ON_GROUND);
|
||||
current_carrier_ = ACTOR_HIT.carrier;
|
||||
return;
|
||||
}
|
||||
|
||||
// Cae libremente
|
||||
y_ += displacement;
|
||||
#ifdef _DEBUG
|
||||
if (y_ > PlayArea::BOTTOM + 100) { y_ = PlayArea::TOP + 2; }
|
||||
#endif
|
||||
}
|
||||
|
||||
void Player::moveVertical(float delta_time) {
|
||||
if (state_ != State::ON_AIR) { return; }
|
||||
|
||||
const auto& tc = room_->getTileCollider();
|
||||
float displacement = vy_ * delta_time;
|
||||
|
||||
const float DISPLACEMENT = vy_ * delta_time;
|
||||
if (vy_ < 0.0F) {
|
||||
// Subiendo: comprobar techo
|
||||
float new_y = y_ + displacement;
|
||||
float ceiling = tc.checkCeiling(x_, new_y, WIDTH);
|
||||
if (ceiling != Collision::NONE) {
|
||||
y_ = ceiling;
|
||||
vy_ = 0.0F;
|
||||
} else {
|
||||
y_ = new_y;
|
||||
}
|
||||
moveVerticalUp(DISPLACEMENT);
|
||||
} else if (vy_ > 0.0F) {
|
||||
// Bajando: comprobar suelo
|
||||
float foot_y = y_ + HEIGHT;
|
||||
float new_foot_y = foot_y + displacement;
|
||||
auto hit = tc.checkFloor(x_, foot_y, WIDTH, new_foot_y);
|
||||
|
||||
if (hit.y != Collision::NONE) {
|
||||
y_ = hit.y - HEIGHT;
|
||||
if (hit.type == TileCollider::Tile::SLOPE_L || hit.type == TileCollider::Tile::SLOPE_R) {
|
||||
slope_tile_x_ = hit.tile_x;
|
||||
slope_tile_y_ = hit.tile_y;
|
||||
slope_type_ = hit.type;
|
||||
transitionToState(State::ON_SLOPE);
|
||||
} else {
|
||||
transitionToState(State::ON_GROUND);
|
||||
}
|
||||
} else {
|
||||
y_ += displacement;
|
||||
#ifdef _DEBUG
|
||||
if (y_ > PlayArea::BOTTOM + 100) { y_ = PlayArea::TOP + 2; }
|
||||
#endif
|
||||
}
|
||||
moveVerticalDown(DISPLACEMENT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,6 +483,7 @@ void Player::checkFalling() {
|
||||
if (state_ == State::ON_AIR) { return; }
|
||||
|
||||
const auto& tc = room_->getTileCollider();
|
||||
const auto& sm = room_->getSolidActors();
|
||||
|
||||
if (state_ == State::ON_SLOPE) {
|
||||
// Verificar que el tile de slope sigue existiendo
|
||||
@@ -415,13 +495,14 @@ void Player::checkFalling() {
|
||||
return;
|
||||
}
|
||||
|
||||
// ON_GROUND: si está sobre una plataforma móvil, no comprobar tiles
|
||||
if (on_platform_) { return; }
|
||||
|
||||
// ON_GROUND: comprobar si sigue habiendo suelo (el tilemap extendido
|
||||
// incluye tiles de las rooms adyacentes, así que no hace falta cross-room)
|
||||
// ON_GROUND: comprobar si sigue habiendo suelo (tile o solid actor).
|
||||
// El tilemap extendido incluye tiles de las rooms adyacentes, así que
|
||||
// no hace falta cross-room para tiles.
|
||||
float foot_y = y_ + HEIGHT;
|
||||
if (!tc.hasGroundBelow(x_, foot_y, WIDTH)) {
|
||||
const bool TILE_GROUND = tc.hasGroundBelow(x_, foot_y, WIDTH);
|
||||
const bool ACTOR_GROUND = sm.hasGroundBelow(x_, foot_y, WIDTH);
|
||||
|
||||
if (!TILE_GROUND && !ACTOR_GROUND) {
|
||||
// Sticking: si no hay suelo pero hay slope debajo, snapear a ella
|
||||
// para transición suave suelo→slope (bajada de rampas sin caer)
|
||||
auto slope = tc.checkSlopeBelow(x_, foot_y, WIDTH);
|
||||
@@ -436,6 +517,16 @@ void Player::checkFalling() {
|
||||
|
||||
vy_ = 0.0F;
|
||||
transitionToState(State::ON_AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Refrescar current_carrier_ para la próxima iteración: si hay actor
|
||||
// debajo, comprobar si lleva CARRY_ON_TOP y guardarlo; si no, limpiarlo.
|
||||
if (ACTOR_GROUND) {
|
||||
auto hit = sm.checkFloor(x_, foot_y, WIDTH, foot_y + 1.0F);
|
||||
current_carrier_ = hit.carrier;
|
||||
} else {
|
||||
current_carrier_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,6 +558,7 @@ void Player::transitionToState(State state) {
|
||||
break;
|
||||
case State::ON_AIR:
|
||||
last_grounded_position_ = static_cast<int>(y_);
|
||||
current_carrier_ = nullptr; // Perder carry al despegar
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -518,18 +610,6 @@ void Player::syncSpriteAndCollider() {
|
||||
collider_box_ = getRect();
|
||||
}
|
||||
|
||||
// Aplica el desplazamiento de una plataforma móvil al jugador
|
||||
void Player::applyPlatformDisplacement(float dx, float surface_y) {
|
||||
y_ = surface_y - HEIGHT; // Snap vertical al top de la plataforma
|
||||
x_ += dx; // Desplazamiento horizontal
|
||||
vy_ = 0.0F;
|
||||
on_platform_ = true;
|
||||
if (state_ != State::ON_GROUND) {
|
||||
transitionToState(State::ON_GROUND);
|
||||
}
|
||||
syncSpriteAndCollider();
|
||||
}
|
||||
|
||||
void Player::placeSprite() {
|
||||
sprite_->setPos(x_, y_);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user