Merge fix/lane-snap-and-door-corridor — snap a lane i corridor amb porta oberta

This commit is contained in:
JailDesigner committed 2026-05-02 14:31:57 +02:00
commit 643e5442c3
3 files changed
+82 -35

No files matched your search

+1 -5
View File
@@ -49,7 +49,7 @@ geometry:
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
door_col: 14
door_col: 11
data:
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
@@ -128,8 +128,6 @@ entities:
sarc_position: [2, 4]
- type: sarcophagus
sarc_position: [3, 4]
- type: mummy
position: [10, 7]
- type: hole
position: [4, 7]
- type: hole
@@ -140,6 +138,4 @@ entities:
position: [15, 5]
- type: hole
position: [8, 5]
- type: mummy
position: [8, 9]
- type: door
+67 -26
View File
@@ -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);
}
+14 -4
View File
@@ -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