fix(player): no snap a lane si pica contra mur extern; corridor només si porta oberta
This commit is contained in:
1 parent
1ae6912be5
commit
82ccf65ce1
3 files changed
+82
-35
No files matched your search
@@ -1617,24 +1617,13 @@ auto Player::hitBoxFromBase(const SDL_FRect& base) -> SDL_FRect {
|
||||
};
|
||||
}
|
||||
|
||||
void Player::snapToLane(
|
||||
void Player::snapTowardsLane(
|
||||
float delta_time,
|
||||
float base,
|
||||
float pitch,
|
||||
int max_idx,
|
||||
float target_lane,
|
||||
float (Player::*get_pos)() const,
|
||||
void (Player::*set_pos)(float)) {
|
||||
// Lanes en pos_axis: base + i·pitch per a i ∈ [0, max_idx]. Correspon a
|
||||
// la caixa de col·lisió ocupant exactament un passadís perpendicular.
|
||||
const auto THRESHOLD = static_cast<float>(collision_size) / 2.0F;
|
||||
|
||||
const auto IDX = std::clamp(
|
||||
static_cast<int>(std::round(((this->*get_pos)() - base) / pitch)),
|
||||
0,
|
||||
max_idx);
|
||||
const float LANE = base + (static_cast<float>(IDX) * pitch);
|
||||
const float DELTA = LANE - (this->*get_pos)();
|
||||
|
||||
const float DELTA = target_lane - (this->*get_pos)();
|
||||
if (std::abs(DELTA) <= 0.0F || std::abs(DELTA) > THRESHOLD) { return; }
|
||||
|
||||
const float FULL_STEP = std::min(std::abs(DELTA), SPEED_PX_PER_S * delta_time);
|
||||
@@ -1719,20 +1708,72 @@ auto Player::tryPushSnapBlockers(
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Player::wouldHitOuterWall(float dx, float dy) const -> bool {
|
||||
SDL_FRect probe = getBaseBox();
|
||||
probe.x += dx;
|
||||
probe.y += dy;
|
||||
if (Map::insidePlayfield(probe)) { return false; }
|
||||
// Excepció: corridor de la porta OBERTA quan l'usuari prem cap
|
||||
// amunt (dy < 0). No demanem que la probe ja estigui dins del
|
||||
// corridor — encara no ho està, perquè el snap és justament qui
|
||||
// ha d'aliniar-la. En lloc d'això comprovem si la base box està
|
||||
// prou prop del centre del corridor com per a que el snap
|
||||
// (mateix THRESHOLD = collision_size/2) puga completar-lo.
|
||||
if (Map::door_open && dy < 0.0F && map_.door_corridor_w > 0) {
|
||||
const float CORRIDOR_TARGET = static_cast<float>(map_.door_corridor_x) + (static_cast<float>(map_.door_corridor_w - collision_size) / 2.0F);
|
||||
const auto THRESHOLD = static_cast<float>(collision_size) / 2.0F;
|
||||
if (std::abs(CORRIDOR_TARGET - probe.x) <= THRESHOLD) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Player::snapToHorizontalLane(float delta_time) {
|
||||
snapToLane(delta_time,
|
||||
static_cast<float>(map_.playfield_y - collision_y_offset),
|
||||
static_cast<float>(map_.pitch_y),
|
||||
map_.grid_rows,
|
||||
&Player::getPosY,
|
||||
&Player::setPosY);
|
||||
// Si el moviment principal del frame (eix X) està blocat per un mur
|
||||
// extern, no fem snap perpendicular: l'usuari està picant contra el
|
||||
// mur i no té sentit arrossegar-lo a un carril. Step probe d'1 px.
|
||||
if (intended_vx_ != 0.0F && wouldHitOuterWall(std::copysign(1.0F, intended_vx_), 0.0F)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto BASE_Y = static_cast<float>(map_.playfield_y - collision_y_offset);
|
||||
const auto PITCH_Y = static_cast<float>(map_.pitch_y);
|
||||
const int IDX = std::clamp(
|
||||
static_cast<int>(std::round((getPosY() - BASE_Y) / PITCH_Y)),
|
||||
0,
|
||||
map_.grid_rows);
|
||||
const float LANE_REG = BASE_Y + (static_cast<float>(IDX) * PITCH_Y);
|
||||
snapTowardsLane(delta_time, LANE_REG, &Player::getPosY, &Player::setPosY);
|
||||
}
|
||||
|
||||
void Player::snapToVerticalLane(float delta_time) {
|
||||
snapToLane(delta_time,
|
||||
static_cast<float>(map_.playfield_x - collision_x_offset),
|
||||
static_cast<float>(map_.pitch_x),
|
||||
map_.grid_cols,
|
||||
&Player::getPosX,
|
||||
&Player::setPosX);
|
||||
// Mateixa idea que la versió horitzontal: si el moviment principal
|
||||
// (eix Y) està blocat per un mur extern, no aliniem en X. La
|
||||
// excepció del corridor de la porta hi aplica naturalment dins
|
||||
// `wouldHitOuterWall` (només quan és OBERT).
|
||||
if (intended_vy_ != 0.0F && wouldHitOuterWall(0.0F, std::copysign(1.0F, intended_vy_))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Candidat 1: lane regular del laberint.
|
||||
const auto BASE_X = static_cast<float>(map_.playfield_x - collision_x_offset);
|
||||
const auto PITCH_X = static_cast<float>(map_.pitch_x);
|
||||
const int IDX = std::clamp(
|
||||
static_cast<int>(std::round((getPosX() - BASE_X) / PITCH_X)),
|
||||
0,
|
||||
map_.grid_cols);
|
||||
const float LANE_REG = BASE_X + (static_cast<float>(IDX) * PITCH_X);
|
||||
|
||||
// Candidat 2: corridor de la porta. NOMÉS si la porta està oberta —
|
||||
// si està tancada no té sentit arrossegar el jugador cap a un
|
||||
// forat per on no pot passar.
|
||||
float best_lane = LANE_REG;
|
||||
if (Map::door_open && map_.door_corridor_w > 0) {
|
||||
const float CORRIDOR_CENTER = static_cast<float>(map_.door_corridor_x) + (static_cast<float>(map_.door_corridor_w - collision_size) / 2.0F);
|
||||
const float LANE_DOOR = CORRIDOR_CENTER - static_cast<float>(collision_x_offset);
|
||||
const float DELTA_REG = LANE_REG - getPosX();
|
||||
const float DELTA_DOOR = LANE_DOOR - getPosX();
|
||||
if (std::abs(DELTA_DOOR) < std::abs(DELTA_REG)) { best_lane = LANE_DOOR; }
|
||||
}
|
||||
|
||||
snapTowardsLane(delta_time, best_lane, &Player::getPosX, &Player::setPosX);
|
||||
}
|
||||
@@ -436,14 +436,24 @@ class Player : public AnimatedSprite {
|
||||
// attack/recover), el sufix del cardinal (`_north`/`_south`/`_east`/
|
||||
// `_west`).
|
||||
[[nodiscard]] auto animationName(bool moving) const -> std::string;
|
||||
void snapToLane(
|
||||
// Aplica el snap cap a una lane concreta amb la mecànica de fases
|
||||
// 1/2 + push de blockers. Els wrappers (`snapToHorizontalLane` /
|
||||
// `snapToVerticalLane`) decideixen la lane diana (grid regular, o
|
||||
// corridor de la porta com a candidata especial).
|
||||
void snapTowardsLane(
|
||||
float delta_time,
|
||||
float base,
|
||||
float pitch,
|
||||
int max_idx,
|
||||
float target_lane,
|
||||
float (Player::*get_pos)() const,
|
||||
void (Player::*set_pos)(float));
|
||||
|
||||
// Cert si un step (dx, dy) trauria la base box fora del playfield
|
||||
// (mur extern). Excepció: si el moviment és cap amunt (dy<0), la
|
||||
// porta està OBERTA i la box probe queda dins del rang X del
|
||||
// corridor, no és bloqueig — el jugador hi pot sortir. S'usa per
|
||||
// suprimir el snap a lane quan el jugador no pot avançar perquè
|
||||
// pica contra un mur exterior.
|
||||
[[nodiscard]] auto wouldHitOuterWall(float dx, float dy) const -> bool;
|
||||
|
||||
// Quan el snap lateral trobaria un altre jugador al carril objectiu,
|
||||
// l'empentem el mateix step en el mateix eix. Així `this` pot entrar
|
||||
// al passadís encara que l'altre estiga estacionat al mitj — mateix
|
||||
|
||||
Reference in new issue
Block a user