barrallantse en la colisió en les habitacions dels costats

fix: surface no clipejava be la copia de surfaces a surfaces que eixien per la dreta amb el flip activat
This commit is contained in:
2026-04-08 19:04:45 +02:00
parent 2a63227ee2
commit 79f79166bd
5 changed files with 191 additions and 13 deletions

View File

@@ -39,7 +39,7 @@ tilemap:
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 269, 269, 269, 269, 269, -1, -1, -1, -1, -1, -1, -1, 24, 25] - [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 269, 269, 269, 269, 269, -1, -1, -1, -1, -1, -1, -1, 24, 25]
- [-1, -1, -1, -1, 141, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, 25] - [-1, -1, -1, -1, 141, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, 25]
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 141, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, 25] - [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 141, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, 25]
- [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, 7, 7, 7, 50, 25] - [124, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, 7, 7, 7, 50, 25]
# Mapa de colisiones (0 = vacio, 1 = solido) # Mapa de colisiones (0 = vacio, 1 = solido)
collision: collision:
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
@@ -62,4 +62,4 @@ tilemap:
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1] - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1]
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1] - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1] - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1] - [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]

View File

@@ -294,32 +294,34 @@ void Surface::render(int x, int y, SDL_FRect* src_rect, SDL_FlipMode flip) { //
float w = (src_rect != nullptr) ? src_rect->w : surface_data_->width; float w = (src_rect != nullptr) ? src_rect->w : surface_data_->width;
float h = (src_rect != nullptr) ? src_rect->h : surface_data_->height; float h = (src_rect != nullptr) ? src_rect->h : surface_data_->height;
// Guardar dimensiones originales antes del clipping (necesarias para flip)
float orig_w = (src_rect != nullptr) ? src_rect->w : static_cast<float>(surface_data_->width);
float orig_h = (src_rect != nullptr) ? src_rect->h : static_cast<float>(surface_data_->height);
// Limitar la región para evitar accesos fuera de rango en origen // Limitar la región para evitar accesos fuera de rango en origen
w = std::min(w, surface_data_->width - sx); w = std::min(w, surface_data_->width - sx);
h = std::min(h, surface_data_->height - sy); h = std::min(h, surface_data_->height - sy);
w = std::min(w, surface_data_dest->width - x);
h = std::min(h, surface_data_dest->height - y);
// Limitar la región para evitar accesos fuera de rango en destino // Limitar la región para evitar accesos fuera de rango en destino
w = std::min(w, surface_data_dest->width - x); w = std::min(w, surface_data_dest->width - static_cast<float>(x));
h = std::min(h, surface_data_dest->height - y); h = std::min(h, surface_data_dest->height - static_cast<float>(y));
// Renderiza píxel por píxel aplicando el flip si es necesario // Renderiza píxel por píxel aplicando el flip si es necesario
const Uint8* src_ptr = surface_data_->data.get(); const Uint8* src_ptr = surface_data_->data.get();
Uint8* dst_ptr = surface_data_dest->data.get(); Uint8* dst_ptr = surface_data_dest->data.get();
for (int iy = 0; iy < h; ++iy) { for (int iy = 0; iy < h; ++iy) {
for (int ix = 0; ix < w; ++ix) { for (int ix = 0; ix < w; ++ix) {
// Coordenadas de origen // Coordenadas de origen (flip usa dimensiones originales, no clipped)
int src_x = (flip == SDL_FLIP_HORIZONTAL) ? (sx + w - 1 - ix) : (sx + ix); int src_x = (flip == SDL_FLIP_HORIZONTAL) ? static_cast<int>(sx + orig_w - 1 - ix) : static_cast<int>(sx + ix);
int src_y = (flip == SDL_FLIP_VERTICAL) ? (sy + h - 1 - iy) : (sy + iy); int src_y = (flip == SDL_FLIP_VERTICAL) ? static_cast<int>(sy + orig_h - 1 - iy) : static_cast<int>(sy + iy);
// Coordenadas de destino // Coordenadas de destino
int dest_x = x + ix; int dest_x = x + ix;
int dest_y = y + iy; int dest_y = y + iy;
// Verificar que las coordenadas de destino están dentro de los límites // Verificar que las coordenadas están dentro de los límites
if (dest_x >= 0 && dest_x < surface_data_dest->width && dest_y >= 0 && dest_y < surface_data_dest->height) { if (dest_x >= 0 && dest_x < surface_data_dest->width && dest_y >= 0 && dest_y < surface_data_dest->height &&
// Copia el píxel si no es transparente src_x >= 0 && src_x < surface_data_->width && src_y >= 0 && src_y < surface_data_->height) {
Uint8 color = src_ptr[static_cast<size_t>(src_x + (src_y * surface_data_->width))]; Uint8 color = src_ptr[static_cast<size_t>(src_x + (src_y * surface_data_->width))];
if (color != static_cast<Uint8>(transparent_color_)) { if (color != static_cast<Uint8>(transparent_color_)) {
dst_ptr[static_cast<size_t>(dest_x + (dest_y * surface_data_dest->width))] = sub_palette_[color]; dst_ptr[static_cast<size_t>(dest_x + (dest_y * surface_data_dest->width))] = sub_palette_[color];

View File

@@ -239,7 +239,7 @@ void Player::moveHorizontal(float delta_time) {
auto [tc, ox, oy] = getCollisionContext(); auto [tc, ox, oy] = getCollisionContext();
float new_x = x_ + (vx_ * delta_time); float new_x = x_ + (vx_ * delta_time);
// Colisión con paredes // Colisión con paredes (room actual)
if (vx_ < 0.0F) { if (vx_ < 0.0F) {
float wall = tc.checkWallLeft(new_x + ox, y_ + oy, WIDTH, HEIGHT); float wall = tc.checkWallLeft(new_x + ox, y_ + oy, WIDTH, HEIGHT);
if (wall != Collision::NONE) { if (wall != Collision::NONE) {
@@ -252,6 +252,10 @@ void Player::moveHorizontal(float delta_time) {
} }
} }
// Cross-room: comprobar muros en rooms adyacentes
auto cross = getCrossRoomChecks();
checkCrossRoomWallH(new_x, cross);
x_ = new_x; x_ = new_x;
// Si estamos en una slope, ajustar Y para seguirla // Si estamos en una slope, ajustar Y para seguirla
@@ -352,6 +356,8 @@ void Player::moveVertical(float delta_time) {
auto [tc, ox, oy] = getCollisionContext(); auto [tc, ox, oy] = getCollisionContext();
float displacement = vy_ * delta_time; float displacement = vy_ * delta_time;
float old_y = y_;
if (vy_ < 0.0F) { if (vy_ < 0.0F) {
// Subiendo: comprobar techo // Subiendo: comprobar techo
float new_y = y_ + displacement; float new_y = y_ + displacement;
@@ -385,6 +391,10 @@ void Player::moveVertical(float delta_time) {
#endif #endif
} }
} }
// Cross-room: comprobar suelo/techo en rooms adyacentes
auto cross = getCrossRoomChecks();
checkCrossRoomFloor(old_y, cross);
} }
// ============================================================================ // ============================================================================
@@ -423,6 +433,12 @@ void Player::checkFalling() {
transitionToState(State::ON_SLOPE); transitionToState(State::ON_SLOPE);
return; return;
} }
// Cross-room: comprobar suelo en rooms adyacentes antes de declarar caída
if (hasCrossRoomGround(getCrossRoomChecks())) {
return;
}
vy_ = 0.0F; vy_ = 0.0F;
transitionToState(State::ON_AIR); transitionToState(State::ON_AIR);
} }
@@ -553,6 +569,109 @@ void Player::syncSpriteAndCollider() {
collider_box_ = getRect(); collider_box_ = getRect();
} }
// Cross-room collision: asigna una room adyacente por índice
void Player::setBorderRoom(int index, std::shared_ptr<Room> room) {
if (index >= 0 && index < BORDER_ROOM_COUNT) {
border_rooms_[index] = std::move(room);
}
}
// Cross-room collision: limpia todas las rooms adyacentes
void Player::clearBorderRooms() {
for (auto& r : border_rooms_) {
r.reset();
}
}
// Cross-room: construye la lista de rooms adyacentes que solapan con la bbox del jugador
auto Player::getCrossRoomChecks() const -> CrossRoomChecks {
// Offsets por room: TOP, RIGHT, BOTTOM, LEFT, TR, BR, BL, TL
static constexpr float PW = static_cast<float>(PlayArea::WIDTH);
static constexpr float PH = static_cast<float>(PlayArea::HEIGHT);
static constexpr struct { float ox; float oy; } OFFSETS[BORDER_ROOM_COUNT] = {
{0, PH}, {-PW, 0}, {0, -PH}, {PW, 0}, {-PW, PH}, {-PW, -PH}, {PW, -PH}, {PW, PH}
};
bool over_top = y_ < 0.0F;
bool over_right = (x_ + WIDTH) > PlayArea::RIGHT;
bool over_bottom = (y_ + HEIGHT) > PlayArea::BOTTOM;
bool over_left = x_ < 0.0F;
bool needed[BORDER_ROOM_COUNT] = {
over_top, over_right, over_bottom, over_left,
over_top && over_right, over_bottom && over_right,
over_bottom && over_left, over_top && over_left
};
CrossRoomChecks result;
for (int i = 0; i < BORDER_ROOM_COUNT; ++i) {
if (needed[i] && border_rooms_[i]) {
result.entries[result.count++] = {&border_rooms_[i]->getTileCollider(), OFFSETS[i].ox, OFFSETS[i].oy};
}
}
return result;
}
// Cross-room: comprueba muros horizontales en rooms adyacentes
void Player::checkCrossRoomWallH(float& new_x, const CrossRoomChecks& checks) const {
for (int i = 0; i < checks.count; ++i) {
const auto& [tc, ox, oy] = checks.entries[i];
if (vx_ < 0.0F) {
float wall = tc->checkWallLeft(new_x + ox, y_ + oy, WIDTH, HEIGHT);
if (wall != Collision::NONE) {
float corrected = wall - ox;
if (corrected > new_x) { new_x = corrected; }
}
} else if (vx_ > 0.0F) {
float wall = tc->checkWallRight(new_x + ox, y_ + oy, WIDTH, HEIGHT);
if (wall != Collision::NONE) {
float corrected = wall - WIDTH - ox;
if (corrected < new_x) { new_x = corrected; }
}
}
}
}
// Cross-room: comprueba suelo/techo en rooms adyacentes (usa old_y para rango correcto de checkFloor)
void Player::checkCrossRoomFloor(float old_y, const CrossRoomChecks& checks) {
for (int i = 0; i < checks.count; ++i) {
const auto& [tc, ox, oy] = checks.entries[i];
if (vy_ < 0.0F) {
float ceiling = tc->checkCeiling(x_ + ox, y_ + oy, WIDTH);
if (ceiling != Collision::NONE) {
float corrected = ceiling - oy;
if (corrected > y_) {
y_ = corrected;
vy_ = 0.0F;
}
}
} else if (vy_ > 0.0F) {
float old_foot = old_y + oy + HEIGHT;
float new_foot = y_ + oy + HEIGHT;
auto hit = tc->checkFloor(x_ + ox, old_foot, WIDTH, new_foot);
if (hit.y != Collision::NONE) {
float corrected = hit.y - HEIGHT - oy;
if (corrected < y_) {
y_ = corrected;
vy_ = 0.0F;
transitionToState(State::ON_GROUND);
}
}
}
}
}
// Cross-room: comprueba si hay suelo bajo el jugador en alguna room adyacente
auto Player::hasCrossRoomGround(const CrossRoomChecks& checks) const -> bool {
for (int i = 0; i < checks.count; ++i) {
const auto& [tc, ox, oy] = checks.entries[i];
float foot = y_ + oy + HEIGHT;
if (tc->hasGroundBelow(x_ + ox, foot, WIDTH)) {
return true;
}
}
return false;
}
// Aplica el desplazamiento de una plataforma móvil al jugador // Aplica el desplazamiento de una plataforma móvil al jugador
void Player::applyPlatformDisplacement(float dx, float surface_y) { void Player::applyPlatformDisplacement(float dx, float surface_y) {
y_ = surface_y - HEIGHT; // Snap vertical al top de la plataforma y_ = surface_y - HEIGHT; // Snap vertical al top de la plataforma

View File

@@ -76,6 +76,12 @@ class Player {
[[nodiscard]] auto getVY() const -> float { return vy_; } [[nodiscard]] auto getVY() const -> float { return vy_; }
void applyPlatformDisplacement(float dx, float surface_y); void applyPlatformDisplacement(float dx, float surface_y);
void clearPlatformFlag() { on_platform_ = false; } void clearPlatformFlag() { on_platform_ = false; }
// Cross-room collision: rooms adyacentes (TOP=0, RIGHT=1, BOTTOM=2, LEFT=3, TR=4, BR=5, BL=6, TL=7)
static constexpr int BORDER_ROOM_COUNT = 8;
void setBorderRoom(int index, std::shared_ptr<Room> room);
void clearBorderRooms();
void setPaused(bool value) { is_paused_ = value; } void setPaused(bool value) { is_paused_ = value; }
void setIgnoreInput(bool value) { ignore_input_ = value; } void setIgnoreInput(bool value) { ignore_input_ = value; }
[[nodiscard]] auto getIgnoreInput() const -> bool { return ignore_input_; } [[nodiscard]] auto getIgnoreInput() const -> bool { return ignore_input_; }
@@ -136,6 +142,9 @@ class Player {
Room::Border border_ = Room::Border::TOP; Room::Border border_ = Room::Border::TOP;
int last_grounded_position_ = 0; int last_grounded_position_ = 0;
// --- Cross-room collision ---
std::shared_ptr<Room> border_rooms_[BORDER_ROOM_COUNT]{};
// --- Renderizado y sonido --- // --- Renderizado y sonido ---
JA_Sound_t* jump_sound_ = nullptr; JA_Sound_t* jump_sound_ = nullptr;
JA_Sound_t* land_sound_ = nullptr; JA_Sound_t* land_sound_ = nullptr;
@@ -147,6 +156,21 @@ class Player {
void handleJumpAndDrop(); void handleJumpAndDrop();
void moveHorizontal(float delta_time); void moveHorizontal(float delta_time);
void moveVertical(float delta_time); void moveVertical(float delta_time);
// Cross-room collision helpers
struct CrossRoomEntry {
const TileCollider* tc;
float ox;
float oy;
};
struct CrossRoomChecks {
CrossRoomEntry entries[BORDER_ROOM_COUNT]{};
int count{0};
};
auto getCrossRoomChecks() const -> CrossRoomChecks;
void checkCrossRoomWallH(float& new_x, const CrossRoomChecks& checks) const;
void checkCrossRoomFloor(float old_y, const CrossRoomChecks& checks);
auto hasCrossRoomGround(const CrossRoomChecks& checks) const -> bool;
void followSlope(); void followSlope();
void exitSlope(); void exitSlope();
void detectSlopeEntry(); void detectSlopeEntry();

View File

@@ -310,6 +310,39 @@ void Game::updatePlaying(float delta_time) {
player_->clearPlatformFlag(); player_->clearPlatformFlag();
checkPlayerAndPlatforms(); checkPlayerAndPlatforms();
// Cross-room collision: precargar rooms adyacentes para colisiones
{
player_->clearBorderRooms();
auto loadBorder = [&](Room::Border b) -> std::shared_ptr<Room> {
auto name = room_->getRoom(b);
return (name != "0") ? getOrCreateRoom(name) : nullptr;
};
auto top = loadBorder(Room::Border::TOP);
auto right = loadBorder(Room::Border::RIGHT);
auto bottom = loadBorder(Room::Border::BOTTOM);
auto left = loadBorder(Room::Border::LEFT);
if (top) { player_->setBorderRoom(0, top); }
if (right) { player_->setBorderRoom(1, right); }
if (bottom) { player_->setBorderRoom(2, bottom); }
if (left) { player_->setBorderRoom(3, left); }
// Diagonales
auto loadDiag = [&](const std::shared_ptr<Room>& a, Room::Border ab,
const std::shared_ptr<Room>& b, Room::Border ba) -> std::shared_ptr<Room> {
std::string name;
if (a) { name = a->getRoom(ab); }
if ((name.empty() || name == "0") && b) { name = b->getRoom(ba); }
return (!name.empty() && name != "0") ? getOrCreateRoom(name) : nullptr;
};
auto tr = loadDiag(top, Room::Border::RIGHT, right, Room::Border::TOP);
auto br = loadDiag(bottom, Room::Border::RIGHT, right, Room::Border::BOTTOM);
auto bl = loadDiag(bottom, Room::Border::LEFT, left, Room::Border::BOTTOM);
auto tl = loadDiag(top, Room::Border::LEFT, left, Room::Border::TOP);
if (tr) { player_->setBorderRoom(4, tr); }
if (br) { player_->setBorderRoom(5, br); }
if (bl) { player_->setBorderRoom(6, bl); }
if (tl) { player_->setBorderRoom(7, tl); }
}
#ifdef _DEBUG #ifdef _DEBUG
// Maneja el arrastre del jugador con el ratón (debug) // Maneja el arrastre del jugador con el ratón (debug)
handleDebugMouseDrag(delta_time); handleDebugMouseDrag(delta_time);