proves
This commit is contained in:
@@ -66,7 +66,8 @@ void Player::update(float delta_time) {
|
|||||||
checkFalling();
|
checkFalling();
|
||||||
|
|
||||||
// 6. Kill tiles
|
// 6. Kill tiles
|
||||||
if (room_->getTileCollider().touchesKillTile(x_, y_, WIDTH, HEIGHT)) {
|
auto [ktc, kox, koy] = getCollisionContext();
|
||||||
|
if (ktc.touchesKillTile(x_ + kox, y_ + koy, WIDTH, HEIGHT)) {
|
||||||
markAsDead();
|
markAsDead();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,11 +191,11 @@ void Player::handleJumpAndDrop() {
|
|||||||
|
|
||||||
// Drop-through: plataforma passable
|
// Drop-through: plataforma passable
|
||||||
if (wanna_down_ && state_ == State::ON_GROUND) {
|
if (wanna_down_ && state_ == State::ON_GROUND) {
|
||||||
const auto& tc = room_->getTileCollider();
|
auto [tc, ox, oy] = getCollisionContext();
|
||||||
float foot_y = y_ + HEIGHT;
|
float foot_y = (y_ + oy) + HEIGHT;
|
||||||
int foot_row = static_cast<int>(foot_y) / Tile::SIZE;
|
int foot_row = static_cast<int>(foot_y) / Tile::SIZE;
|
||||||
int left_col = static_cast<int>(x_) / Tile::SIZE;
|
int left_col = static_cast<int>(x_ + ox) / Tile::SIZE;
|
||||||
int right_col = static_cast<int>(x_ + WIDTH - 1) / Tile::SIZE;
|
int right_col = static_cast<int>((x_ + ox) + WIDTH - 1) / Tile::SIZE;
|
||||||
|
|
||||||
for (int col = left_col; col <= right_col; ++col) {
|
for (int col = left_col; col <= right_col; ++col) {
|
||||||
if (tc.getTileAt(col, foot_row) == TileCollider::Tile::PASSABLE) {
|
if (tc.getTileAt(col, foot_row) == TileCollider::Tile::PASSABLE) {
|
||||||
@@ -225,19 +226,19 @@ void Player::moveHorizontal(float delta_time) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& tc = room_->getTileCollider();
|
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
|
||||||
if (vx_ < 0.0F) {
|
if (vx_ < 0.0F) {
|
||||||
float wall = tc.checkWallLeft(new_x, y_, WIDTH, HEIGHT);
|
float wall = tc.checkWallLeft(new_x + ox, y_ + oy, WIDTH, HEIGHT);
|
||||||
if (wall != Collision::NONE) {
|
if (wall != Collision::NONE) {
|
||||||
new_x = wall;
|
new_x = wall - ox;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
float wall = tc.checkWallRight(new_x, y_, WIDTH, HEIGHT);
|
float wall = tc.checkWallRight(new_x + ox, y_ + oy, WIDTH, HEIGHT);
|
||||||
if (wall != Collision::NONE) {
|
if (wall != Collision::NONE) {
|
||||||
new_x = wall - WIDTH;
|
new_x = wall - WIDTH - ox;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,18 +260,18 @@ void Player::moveHorizontal(float delta_time) {
|
|||||||
// actual y la inferior (las slopes en escalera bajan una fila por tile).
|
// actual y la inferior (las slopes en escalera bajan una fila por tile).
|
||||||
// Si no encuentra slope, llama a exitSlope().
|
// Si no encuentra slope, llama a exitSlope().
|
||||||
void Player::followSlope() {
|
void Player::followSlope() {
|
||||||
const auto& tc = room_->getTileCollider();
|
auto [tc, ox, oy] = getCollisionContext();
|
||||||
|
|
||||||
// SLOPE_L (\): pie izquierdo. SLOPE_R (/): pie derecho.
|
// SLOPE_L (\): pie izquierdo. SLOPE_R (/): pie derecho.
|
||||||
float foot_x = (slope_type_ == TileCollider::Tile::SLOPE_L) ? x_ : x_ + WIDTH - 1;
|
float foot_x = (slope_type_ == TileCollider::Tile::SLOPE_L) ? (x_ + ox) : (x_ + ox) + WIDTH - 1;
|
||||||
|
|
||||||
// Calcular Y en la slope actual
|
// Calcular Y en la slope actual
|
||||||
float surface_y = tc.getSlopeY(slope_tile_x_, slope_tile_y_, foot_x);
|
float surface_y = tc.getSlopeY(slope_tile_x_, slope_tile_y_, foot_x);
|
||||||
y_ = surface_y - HEIGHT;
|
y_ = surface_y - HEIGHT - oy;
|
||||||
|
|
||||||
// Comprobar si hemos salido del tile actual
|
// Comprobar si hemos salido del tile actual
|
||||||
int foot_tile_x = static_cast<int>(foot_x) / Tile::SIZE;
|
int foot_tile_x = static_cast<int>(foot_x) / Tile::SIZE;
|
||||||
int foot_tile_y = static_cast<int>(y_ + HEIGHT) / Tile::SIZE;
|
int foot_tile_y = static_cast<int>((y_ + oy) + HEIGHT) / Tile::SIZE;
|
||||||
|
|
||||||
if (foot_tile_x != slope_tile_x_ || foot_tile_y != slope_tile_y_) {
|
if (foot_tile_x != slope_tile_x_ || foot_tile_y != slope_tile_y_) {
|
||||||
// Buscar slope en el tile calculado y en el de abajo (la escalera de slopes
|
// Buscar slope en el tile calculado y en el de abajo (la escalera de slopes
|
||||||
@@ -282,7 +283,7 @@ void Player::followSlope() {
|
|||||||
slope_tile_y_ = row;
|
slope_tile_y_ = row;
|
||||||
slope_type_ = new_tile;
|
slope_type_ = new_tile;
|
||||||
surface_y = tc.getSlopeY(slope_tile_x_, slope_tile_y_, foot_x);
|
surface_y = tc.getSlopeY(slope_tile_x_, slope_tile_y_, foot_x);
|
||||||
y_ = surface_y - HEIGHT;
|
y_ = surface_y - HEIGHT - oy;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,16 +296,16 @@ void Player::followSlope() {
|
|||||||
// entre filas cuando se sale por el extremo inferior de la slope).
|
// entre filas cuando se sale por el extremo inferior de la slope).
|
||||||
// Si hay suelo, snapea al borde del tile. Si no, empieza a caer.
|
// Si hay suelo, snapea al borde del tile. Si no, empieza a caer.
|
||||||
void Player::exitSlope() {
|
void Player::exitSlope() {
|
||||||
const auto& tc = room_->getTileCollider();
|
auto [tc, ox, oy] = getCollisionContext();
|
||||||
float foot_y = y_ + HEIGHT;
|
float foot_y = (y_ + oy) + HEIGHT;
|
||||||
|
|
||||||
// Comprobar suelo en la fila actual y la siguiente (al salir por abajo de una slope,
|
// Comprobar suelo en la fila actual y la siguiente (al salir por abajo de una slope,
|
||||||
// los pies pueden estar en el último pixel de la fila, justo antes del suelo)
|
// los pies pueden estar en el último pixel de la fila, justo antes del suelo)
|
||||||
for (int check = 0; check <= 1; ++check) {
|
for (int check = 0; check <= 1; ++check) {
|
||||||
float check_y = foot_y + check;
|
float check_y = foot_y + check;
|
||||||
if (tc.hasGroundBelow(x_, check_y, WIDTH)) {
|
if (tc.hasGroundBelow(x_ + ox, check_y, WIDTH)) {
|
||||||
int row = static_cast<int>(check_y) / Tile::SIZE;
|
int row = static_cast<int>(check_y) / Tile::SIZE;
|
||||||
y_ = static_cast<float>(row * Tile::SIZE) - HEIGHT;
|
y_ = static_cast<float>(row * Tile::SIZE) - HEIGHT - oy;
|
||||||
transitionToState(State::ON_GROUND);
|
transitionToState(State::ON_GROUND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -318,12 +319,12 @@ void Player::exitSlope() {
|
|||||||
// Las slopes en escalera están una fila arriba del suelo, así que checkSlopeBelow
|
// Las slopes en escalera están una fila arriba del suelo, así que checkSlopeBelow
|
||||||
// también mira la fila superior.
|
// también mira la fila superior.
|
||||||
void Player::detectSlopeEntry() {
|
void Player::detectSlopeEntry() {
|
||||||
const auto& tc = room_->getTileCollider();
|
auto [tc, ox, oy] = getCollisionContext();
|
||||||
float foot_y = y_ + HEIGHT;
|
float foot_y = (y_ + oy) + HEIGHT;
|
||||||
|
|
||||||
auto slope = tc.checkSlopeBelow(x_, foot_y, WIDTH);
|
auto slope = tc.checkSlopeBelow(x_ + ox, foot_y, WIDTH);
|
||||||
if (slope.on_slope) {
|
if (slope.on_slope) {
|
||||||
y_ = slope.surface_y - HEIGHT;
|
y_ = slope.surface_y - HEIGHT - oy;
|
||||||
slope_tile_x_ = slope.tile_x;
|
slope_tile_x_ = slope.tile_x;
|
||||||
slope_tile_y_ = slope.tile_y;
|
slope_tile_y_ = slope.tile_y;
|
||||||
slope_type_ = slope.type;
|
slope_type_ = slope.type;
|
||||||
@@ -338,27 +339,27 @@ void Player::detectSlopeEntry() {
|
|||||||
void Player::moveVertical(float delta_time) {
|
void Player::moveVertical(float delta_time) {
|
||||||
if (state_ != State::ON_AIR) { return; }
|
if (state_ != State::ON_AIR) { return; }
|
||||||
|
|
||||||
const auto& tc = room_->getTileCollider();
|
auto [tc, ox, oy] = getCollisionContext();
|
||||||
float displacement = vy_ * delta_time;
|
float displacement = vy_ * delta_time;
|
||||||
|
|
||||||
if (vy_ < 0.0F) {
|
if (vy_ < 0.0F) {
|
||||||
// Subiendo: comprobar techo
|
// Subiendo: comprobar techo
|
||||||
float new_y = y_ + displacement;
|
float new_y = y_ + displacement;
|
||||||
float ceiling = tc.checkCeiling(x_, new_y, WIDTH);
|
float ceiling = tc.checkCeiling(x_ + ox, new_y + oy, WIDTH);
|
||||||
if (ceiling != Collision::NONE) {
|
if (ceiling != Collision::NONE) {
|
||||||
y_ = ceiling;
|
y_ = ceiling - oy;
|
||||||
vy_ = 0.0F;
|
vy_ = 0.0F;
|
||||||
} else {
|
} else {
|
||||||
y_ = new_y;
|
y_ = new_y;
|
||||||
}
|
}
|
||||||
} else if (vy_ > 0.0F) {
|
} else if (vy_ > 0.0F) {
|
||||||
// Bajando: comprobar suelo
|
// Bajando: comprobar suelo
|
||||||
float foot_y = y_ + HEIGHT;
|
float foot_y = (y_ + oy) + HEIGHT;
|
||||||
float new_foot_y = foot_y + displacement;
|
float new_foot_y = foot_y + displacement;
|
||||||
auto hit = tc.checkFloor(x_, foot_y, WIDTH, new_foot_y);
|
auto hit = tc.checkFloor(x_ + ox, foot_y, WIDTH, new_foot_y);
|
||||||
|
|
||||||
if (hit.y != Collision::NONE) {
|
if (hit.y != Collision::NONE) {
|
||||||
y_ = hit.y - HEIGHT;
|
y_ = hit.y - HEIGHT - oy;
|
||||||
if (hit.type == TileCollider::Tile::SLOPE_L || hit.type == TileCollider::Tile::SLOPE_R) {
|
if (hit.type == TileCollider::Tile::SLOPE_L || hit.type == TileCollider::Tile::SLOPE_R) {
|
||||||
slope_tile_x_ = hit.tile_x;
|
slope_tile_x_ = hit.tile_x;
|
||||||
slope_tile_y_ = hit.tile_y;
|
slope_tile_y_ = hit.tile_y;
|
||||||
@@ -383,7 +384,7 @@ void Player::moveVertical(float delta_time) {
|
|||||||
void Player::checkFalling() {
|
void Player::checkFalling() {
|
||||||
if (state_ == State::ON_AIR) { return; }
|
if (state_ == State::ON_AIR) { return; }
|
||||||
|
|
||||||
const auto& tc = room_->getTileCollider();
|
auto [tc, ox, oy] = getCollisionContext();
|
||||||
|
|
||||||
if (state_ == State::ON_SLOPE) {
|
if (state_ == State::ON_SLOPE) {
|
||||||
// Verificar que el tile de slope sigue existiendo
|
// Verificar que el tile de slope sigue existiendo
|
||||||
@@ -396,8 +397,8 @@ void Player::checkFalling() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ON_GROUND: comprobar si sigue habiendo suelo
|
// ON_GROUND: comprobar si sigue habiendo suelo
|
||||||
float foot_y = y_ + HEIGHT;
|
float foot_y = (y_ + oy) + HEIGHT;
|
||||||
if (!tc.hasGroundBelow(x_, foot_y, WIDTH)) {
|
if (!tc.hasGroundBelow(x_ + ox, foot_y, WIDTH)) {
|
||||||
vy_ = 0.0F;
|
vy_ = 0.0F;
|
||||||
transitionToState(State::ON_AIR);
|
transitionToState(State::ON_AIR);
|
||||||
}
|
}
|
||||||
@@ -445,8 +446,10 @@ auto Player::handleBorders() const -> Room::Border {
|
|||||||
return Room::Border::NONE;
|
return Room::Border::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::switchBorders() {
|
// Reposiciona al jugador al hacer commit definitivo a la room adyacente.
|
||||||
switch (border_) {
|
// Se llama cuando el rectángulo completo del jugador ha salido de bounds.
|
||||||
|
void Player::commitToRoom(Room::Border border) {
|
||||||
|
switch (border) {
|
||||||
case Room::Border::TOP:
|
case Room::Border::TOP:
|
||||||
y_ += PlayArea::HEIGHT;
|
y_ += PlayArea::HEIGHT;
|
||||||
last_grounded_position_ = static_cast<int>(y_);
|
last_grounded_position_ = static_cast<int>(y_);
|
||||||
@@ -468,6 +471,69 @@ void Player::switchBorders() {
|
|||||||
syncSpriteAndCollider();
|
syncSpriteAndCollider();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::setAdjacentRoom(std::shared_ptr<Room> room, Room::Border direction) {
|
||||||
|
adjacent_room_ = std::move(room);
|
||||||
|
adjacent_direction_ = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::clearAdjacentRoom() {
|
||||||
|
adjacent_room_.reset();
|
||||||
|
adjacent_direction_ = Room::Border::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::isFullyOutOfBounds() const -> bool {
|
||||||
|
switch (adjacent_direction_) {
|
||||||
|
case Room::Border::TOP:
|
||||||
|
return (y_ + HEIGHT) <= PlayArea::TOP;
|
||||||
|
case Room::Border::BOTTOM:
|
||||||
|
return y_ >= PlayArea::BOTTOM;
|
||||||
|
case Room::Border::LEFT:
|
||||||
|
return (x_ + WIDTH) <= PlayArea::LEFT;
|
||||||
|
case Room::Border::RIGHT:
|
||||||
|
return x_ >= PlayArea::RIGHT;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Devuelve el TileCollider correcto y los offsets de traducción de coordenadas
|
||||||
|
// según en qué room está el centro del jugador.
|
||||||
|
auto Player::getCollisionContext() const -> CollisionContext {
|
||||||
|
if (!adjacent_room_) {
|
||||||
|
return {room_->getTileCollider(), 0.0F, 0.0F};
|
||||||
|
}
|
||||||
|
|
||||||
|
const float CENTER_X = x_ + (WIDTH / 2.0F);
|
||||||
|
const float CENTER_Y = y_ + (HEIGHT / 2.0F);
|
||||||
|
|
||||||
|
switch (adjacent_direction_) {
|
||||||
|
case Room::Border::TOP:
|
||||||
|
if (CENTER_Y < PlayArea::TOP) {
|
||||||
|
return {adjacent_room_->getTileCollider(), 0.0F, static_cast<float>(PlayArea::HEIGHT)};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Room::Border::BOTTOM:
|
||||||
|
if (CENTER_Y > PlayArea::BOTTOM) {
|
||||||
|
return {adjacent_room_->getTileCollider(), 0.0F, -static_cast<float>(PlayArea::HEIGHT)};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Room::Border::LEFT:
|
||||||
|
if (CENTER_X < PlayArea::LEFT) {
|
||||||
|
return {adjacent_room_->getTileCollider(), static_cast<float>(PlayArea::WIDTH), 0.0F};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Room::Border::RIGHT:
|
||||||
|
if (CENTER_X > PlayArea::RIGHT) {
|
||||||
|
return {adjacent_room_->getTileCollider(), -static_cast<float>(PlayArea::WIDTH), 0.0F};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {room_->getTileCollider(), 0.0F, 0.0F};
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Geometría y renderizado
|
// Geometría y renderizado
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class Player {
|
|||||||
void update(float delta_time);
|
void update(float delta_time);
|
||||||
[[nodiscard]] auto isOnBorder() const -> bool { return border_ != Room::Border::NONE; }
|
[[nodiscard]] auto isOnBorder() const -> bool { return border_ != Room::Border::NONE; }
|
||||||
[[nodiscard]] auto getBorder() const -> Room::Border { return border_; }
|
[[nodiscard]] auto getBorder() const -> Room::Border { return border_; }
|
||||||
void switchBorders();
|
void commitToRoom(Room::Border border);
|
||||||
auto getRect() -> SDL_FRect { return {.x = x_, .y = y_, .w = WIDTH, .h = HEIGHT}; }
|
auto getRect() -> SDL_FRect { return {.x = x_, .y = y_, .w = WIDTH, .h = HEIGHT}; }
|
||||||
auto getCollider() -> SDL_FRect& { return collider_box_; }
|
auto getCollider() -> SDL_FRect& { return collider_box_; }
|
||||||
auto getSpawnParams() -> SpawnData { return {.x = x_, .y = y_, .vx = vx_, .vy = vy_, .last_grounded_position = last_grounded_position_, .state = state_, .flip = sprite_->getFlip()}; }
|
auto getSpawnParams() -> SpawnData { return {.x = x_, .y = y_, .vx = vx_, .vy = vy_, .last_grounded_position = last_grounded_position_, .state = state_, .flip = sprite_->getFlip()}; }
|
||||||
@@ -71,6 +71,9 @@ class Player {
|
|||||||
void setSkin(const std::string& skin_name);
|
void setSkin(const std::string& skin_name);
|
||||||
static auto skinToAnimationPath(const std::string& skin_name) -> std::string;
|
static auto skinToAnimationPath(const std::string& skin_name) -> std::string;
|
||||||
void setRoom(std::shared_ptr<Room> room) { room_ = std::move(room); }
|
void setRoom(std::shared_ptr<Room> room) { room_ = std::move(room); }
|
||||||
|
void setAdjacentRoom(std::shared_ptr<Room> room, Room::Border direction);
|
||||||
|
void clearAdjacentRoom();
|
||||||
|
[[nodiscard]] auto isFullyOutOfBounds() const -> bool;
|
||||||
[[nodiscard]] auto isAlive() const -> bool { return is_alive_; }
|
[[nodiscard]] auto isAlive() const -> bool { return is_alive_; }
|
||||||
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; }
|
||||||
@@ -86,8 +89,18 @@ class Player {
|
|||||||
static constexpr int WIDTH = 12;
|
static constexpr int WIDTH = 12;
|
||||||
static constexpr int HEIGHT = 24;
|
static constexpr int HEIGHT = 24;
|
||||||
|
|
||||||
|
// --- Contexto de colisión (selección de room + traducción de coordenadas) ---
|
||||||
|
struct CollisionContext {
|
||||||
|
const TileCollider& tc;
|
||||||
|
float offset_x;
|
||||||
|
float offset_y;
|
||||||
|
};
|
||||||
|
auto getCollisionContext() const -> CollisionContext;
|
||||||
|
|
||||||
// --- Objetos y punteros ---
|
// --- Objetos y punteros ---
|
||||||
std::shared_ptr<Room> room_;
|
std::shared_ptr<Room> room_;
|
||||||
|
std::shared_ptr<Room> adjacent_room_;
|
||||||
|
Room::Border adjacent_direction_{Room::Border::NONE};
|
||||||
std::unique_ptr<AnimatedSprite> sprite_;
|
std::unique_ptr<AnimatedSprite> sprite_;
|
||||||
|
|
||||||
// --- Posición y física ---
|
// --- Posición y física ---
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
#include "game/ui/console.hpp" // Para Console
|
#include "game/ui/console.hpp" // Para Console
|
||||||
#include "game/ui/notifier.hpp" // Para Notifier, NotificationText, CHEEVO_NO...
|
#include "game/ui/notifier.hpp" // Para Notifier, NotificationText, CHEEVO_NO...
|
||||||
#include "utils/defines.hpp" // Para Tile::SIZE, PlayArea::HEIGHT, RoomBorder::BOTTOM
|
#include "utils/defines.hpp" // Para Tile::SIZE, PlayArea::HEIGHT, RoomBorder::BOTTOM
|
||||||
#include "utils/easing_functions.hpp" // Para Easing::cubicInOut
|
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
@@ -309,8 +308,8 @@ void Game::updatePlaying(float delta_time) {
|
|||||||
|
|
||||||
// Actualiza los objetos
|
// Actualiza los objetos
|
||||||
room_->update(delta_time);
|
room_->update(delta_time);
|
||||||
if (transitioning_) {
|
if (transitioning_ && transition_adjacent_room_) {
|
||||||
transition_old_room_->update(delta_time);
|
transition_adjacent_room_->update(delta_time);
|
||||||
}
|
}
|
||||||
switch (mode_) {
|
switch (mode_) {
|
||||||
case Mode::GAME:
|
case Mode::GAME:
|
||||||
@@ -325,37 +324,14 @@ void Game::updatePlaying(float delta_time) {
|
|||||||
#else
|
#else
|
||||||
player_->update(delta_time);
|
player_->update(delta_time);
|
||||||
#endif
|
#endif
|
||||||
// Si durante una transición el jugador cruza de vuelta por el borde opuesto,
|
|
||||||
// abortar la transición y volver a la habitación anterior
|
|
||||||
if (transitioning_ && player_->isOnBorder()) {
|
|
||||||
const auto BORDER = player_->getBorder();
|
|
||||||
const bool FELL_BACK =
|
|
||||||
(transition_direction_ == Room::Border::TOP && BORDER == Room::Border::BOTTOM) ||
|
|
||||||
(transition_direction_ == Room::Border::BOTTOM && BORDER == Room::Border::TOP) ||
|
|
||||||
(transition_direction_ == Room::Border::LEFT && BORDER == Room::Border::RIGHT) ||
|
|
||||||
(transition_direction_ == Room::Border::RIGHT && BORDER == Room::Border::LEFT);
|
|
||||||
|
|
||||||
if (FELL_BACK) {
|
|
||||||
room_ = transition_old_room_;
|
|
||||||
player_->setRoom(room_);
|
|
||||||
player_->switchBorders();
|
|
||||||
spawn_data_ = player_->getSpawnParams();
|
|
||||||
current_room_ = transition_old_room_path_;
|
|
||||||
setScoreBoardColor();
|
|
||||||
endTransition();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkPlayerIsOnBorder();
|
checkPlayerIsOnBorder();
|
||||||
checkPlayerAndItems();
|
checkPlayerAndItems();
|
||||||
checkPlayerAndEnemies();
|
checkPlayerAndEnemies();
|
||||||
checkIfPlayerIsAlive();
|
checkIfPlayerIsAlive();
|
||||||
|
|
||||||
// Avanzar transición
|
// Actualizar cámara de transición
|
||||||
if (transitioning_) {
|
if (transitioning_) {
|
||||||
transition_timer_ += delta_time;
|
updateTransitionCamera(delta_time);
|
||||||
if (transition_timer_ >= TRANSITION_DURATION) {
|
|
||||||
endTransition();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -491,44 +467,11 @@ void Game::renderPlaying() {
|
|||||||
|
|
||||||
if (transitioning_) {
|
if (transitioning_) {
|
||||||
// --- Transición animada entre pantallas ---
|
// --- Transición animada entre pantallas ---
|
||||||
const float T = std::min(transition_timer_ / TRANSITION_DURATION, 1.0F);
|
int cam_x = static_cast<int>(camera_offset_x_);
|
||||||
const float P = Easing::cubicInOut(T);
|
int cam_y = static_cast<int>(camera_offset_y_);
|
||||||
|
|
||||||
// Calcular offsets (derivar uno del otro para evitar gap de 1px por truncamiento)
|
// Renderizar habitación principal con offset de cámara
|
||||||
int old_ox = 0;
|
Screen::get()->setRenderOffset(cam_x, cam_y);
|
||||||
int old_oy = 0;
|
|
||||||
int new_ox = 0;
|
|
||||||
int new_oy = 0;
|
|
||||||
|
|
||||||
switch (transition_direction_) {
|
|
||||||
case Room::Border::RIGHT:
|
|
||||||
new_ox = static_cast<int>((1.0F - P) * PlayArea::WIDTH);
|
|
||||||
old_ox = new_ox - PlayArea::WIDTH;
|
|
||||||
break;
|
|
||||||
case Room::Border::LEFT:
|
|
||||||
new_ox = -static_cast<int>((1.0F - P) * PlayArea::WIDTH);
|
|
||||||
old_ox = new_ox + PlayArea::WIDTH;
|
|
||||||
break;
|
|
||||||
case Room::Border::BOTTOM:
|
|
||||||
new_oy = static_cast<int>((1.0F - P) * PlayArea::HEIGHT);
|
|
||||||
old_oy = new_oy - PlayArea::HEIGHT;
|
|
||||||
break;
|
|
||||||
case Room::Border::TOP:
|
|
||||||
new_oy = -static_cast<int>((1.0F - P) * PlayArea::HEIGHT);
|
|
||||||
old_oy = new_oy + PlayArea::HEIGHT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Renderizar habitación saliente con su offset
|
|
||||||
Screen::get()->setRenderOffset(old_ox, old_oy);
|
|
||||||
transition_old_room_->renderMap();
|
|
||||||
transition_old_room_->renderEnemies();
|
|
||||||
transition_old_room_->renderItems();
|
|
||||||
|
|
||||||
// Renderizar habitación entrante + jugador con su offset
|
|
||||||
Screen::get()->setRenderOffset(new_ox, new_oy);
|
|
||||||
room_->renderMap();
|
room_->renderMap();
|
||||||
room_->renderEnemies();
|
room_->renderEnemies();
|
||||||
room_->renderItems();
|
room_->renderItems();
|
||||||
@@ -536,6 +479,23 @@ void Game::renderPlaying() {
|
|||||||
player_->render();
|
player_->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Renderizar habitación adyacente desplazada
|
||||||
|
if (transition_adjacent_room_) {
|
||||||
|
int adj_x = cam_x;
|
||||||
|
int adj_y = cam_y;
|
||||||
|
switch (transition_direction_) {
|
||||||
|
case Room::Border::TOP: adj_y -= PlayArea::HEIGHT; break;
|
||||||
|
case Room::Border::BOTTOM: adj_y += PlayArea::HEIGHT; break;
|
||||||
|
case Room::Border::LEFT: adj_x -= PlayArea::WIDTH; break;
|
||||||
|
case Room::Border::RIGHT: adj_x += PlayArea::WIDTH; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
Screen::get()->setRenderOffset(adj_x, adj_y);
|
||||||
|
transition_adjacent_room_->renderMap();
|
||||||
|
transition_adjacent_room_->renderEnemies();
|
||||||
|
transition_adjacent_room_->renderItems();
|
||||||
|
}
|
||||||
|
|
||||||
// Scoreboard sin offset
|
// Scoreboard sin offset
|
||||||
Screen::get()->setRenderOffset(0, 0);
|
Screen::get()->setRenderOffset(0, 0);
|
||||||
scoreboard_->render();
|
scoreboard_->render();
|
||||||
@@ -813,17 +773,54 @@ auto Game::changeRoom(const std::string& room_path) -> bool {
|
|||||||
|
|
||||||
// Comprueba si el jugador esta en el borde de la pantalla
|
// Comprueba si el jugador esta en el borde de la pantalla
|
||||||
void Game::checkPlayerIsOnBorder() {
|
void Game::checkPlayerIsOnBorder() {
|
||||||
// No permitir transiciones encadenadas
|
if (!player_->isOnBorder()) {
|
||||||
if (transitioning_) {
|
// Si hay transición activa y el jugador ha vuelto completamente dentro de bounds,
|
||||||
return;
|
// comprobar si la cámara también ha vuelto para cancelar la transición
|
||||||
|
if (transitioning_ && std::abs(camera_offset_x_) < 1.0F && std::abs(camera_offset_y_) < 1.0F) {
|
||||||
|
endTransition();
|
||||||
}
|
}
|
||||||
if (transition_just_ended_) {
|
|
||||||
transition_just_ended_ = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player_->isOnBorder()) {
|
|
||||||
const auto BORDER = player_->getBorder();
|
const auto BORDER = player_->getBorder();
|
||||||
|
|
||||||
|
// Si ya hay transición activa, comprobar si el jugador hace commit a la room adyacente
|
||||||
|
if (transitioning_) {
|
||||||
|
// ¿El jugador ha salido completamente por el lado de la transición?
|
||||||
|
if (player_->isFullyOutOfBounds()) {
|
||||||
|
// Commit: la room adyacente pasa a ser la room principal
|
||||||
|
room_ = transition_adjacent_room_;
|
||||||
|
player_->setRoom(room_);
|
||||||
|
player_->commitToRoom(transition_direction_);
|
||||||
|
current_room_ = transition_adjacent_room_path_;
|
||||||
|
spawn_data_ = player_->getSpawnParams();
|
||||||
|
setScoreBoardColor();
|
||||||
|
|
||||||
|
// Ajustar cámara: restar el desplazamiento de una pantalla completa
|
||||||
|
switch (transition_direction_) {
|
||||||
|
case Room::Border::TOP: camera_offset_y_ -= PlayArea::HEIGHT; break;
|
||||||
|
case Room::Border::BOTTOM: camera_offset_y_ += PlayArea::HEIGHT; break;
|
||||||
|
case Room::Border::LEFT: camera_offset_x_ -= PlayArea::WIDTH; break;
|
||||||
|
case Room::Border::RIGHT: camera_offset_x_ += PlayArea::WIDTH; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limpiar transición (pero la cámara sigue animándose hacia 0)
|
||||||
|
player_->clearAdjacentRoom();
|
||||||
|
transition_adjacent_room_.reset();
|
||||||
|
transition_adjacent_room_path_.clear();
|
||||||
|
transition_direction_ = Room::Border::NONE;
|
||||||
|
|
||||||
|
// La cámara aún no está en 0, así que mantenemos transitioning_ = true
|
||||||
|
// Se resolverá cuando la cámara llegue a 0 y el jugador esté dentro de bounds
|
||||||
|
if (std::abs(camera_offset_x_) < 1.0F && std::abs(camera_offset_y_) < 1.0F) {
|
||||||
|
endTransition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No hay transición activa — iniciar una nueva
|
||||||
const auto ROOM_NAME = room_->getRoom(BORDER);
|
const auto ROOM_NAME = room_->getRoom(BORDER);
|
||||||
|
|
||||||
// Si no hay habitación adyacente
|
// Si no hay habitación adyacente
|
||||||
@@ -834,38 +831,76 @@ void Game::checkPlayerIsOnBorder() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guardar la habitación saliente
|
// Cargar room adyacente
|
||||||
transition_old_room_ = room_;
|
auto adjacent_room = std::make_shared<Room>(ROOM_NAME, scoreboard_data_);
|
||||||
transition_old_room_path_ = current_room_;
|
transition_adjacent_room_ = adjacent_room;
|
||||||
|
transition_adjacent_room_path_ = ROOM_NAME;
|
||||||
transition_direction_ = BORDER;
|
transition_direction_ = BORDER;
|
||||||
|
|
||||||
// Crear nueva habitación y reposicionar jugador
|
// Pasar la room adyacente al player para colisiones
|
||||||
if (changeRoom(ROOM_NAME)) {
|
player_->setAdjacentRoom(adjacent_room, BORDER);
|
||||||
player_->switchBorders();
|
|
||||||
spawn_data_ = player_->getSpawnParams();
|
|
||||||
|
|
||||||
// Iniciar transición animada
|
// Iniciar transición (NO cambiar room_, NO reposicionar jugador)
|
||||||
transitioning_ = true;
|
transitioning_ = true;
|
||||||
transition_timer_ = 0.0F;
|
|
||||||
} else {
|
|
||||||
// changeRoom falló, limpiar
|
|
||||||
transition_old_room_.reset();
|
|
||||||
transition_direction_ = Room::Border::NONE;
|
|
||||||
|
|
||||||
if (BORDER == Room::Border::BOTTOM) {
|
if (room_tracker_->addRoom(ROOM_NAME)) {
|
||||||
killPlayer();
|
scoreboard_data_->rooms++;
|
||||||
|
Options::stats.rooms = scoreboard_data_->rooms;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Actualiza la cámara durante la transición: sigue al jugador con inercia
|
||||||
|
void Game::updateTransitionCamera(float delta_time) {
|
||||||
|
// Calcular el offset objetivo basado en la posición del jugador
|
||||||
|
float target_x = 0.0F;
|
||||||
|
float target_y = 0.0F;
|
||||||
|
|
||||||
|
const auto RECT = player_->getRect();
|
||||||
|
const float CENTER_X = RECT.x + (RECT.w / 2.0F);
|
||||||
|
const float CENTER_Y = RECT.y + (RECT.h / 2.0F);
|
||||||
|
|
||||||
|
if (transition_direction_ == Room::Border::TOP || transition_direction_ == Room::Border::BOTTOM) {
|
||||||
|
if (CENTER_Y < PlayArea::TOP) {
|
||||||
|
target_y = static_cast<float>(PlayArea::HEIGHT);
|
||||||
|
} else if (CENTER_Y > PlayArea::BOTTOM) {
|
||||||
|
target_y = -static_cast<float>(PlayArea::HEIGHT);
|
||||||
|
}
|
||||||
|
} else if (transition_direction_ == Room::Border::LEFT || transition_direction_ == Room::Border::RIGHT) {
|
||||||
|
if (CENTER_X < PlayArea::LEFT) {
|
||||||
|
target_x = static_cast<float>(PlayArea::WIDTH);
|
||||||
|
} else if (CENTER_X > PlayArea::RIGHT) {
|
||||||
|
target_x = -static_cast<float>(PlayArea::WIDTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpolar la cámara hacia el objetivo con velocidad constante
|
||||||
|
constexpr float CAMERA_SPEED = 500.0F;
|
||||||
|
auto lerp_towards = [delta_time](float current, float target) -> float {
|
||||||
|
const float DIFF = target - current;
|
||||||
|
if (std::abs(DIFF) < 1.0F) { return target; }
|
||||||
|
const float STEP = CAMERA_SPEED * delta_time;
|
||||||
|
if (std::abs(DIFF) <= STEP) { return target; }
|
||||||
|
return current + (DIFF > 0.0F ? STEP : -STEP);
|
||||||
|
};
|
||||||
|
|
||||||
|
camera_offset_x_ = lerp_towards(camera_offset_x_, target_x);
|
||||||
|
camera_offset_y_ = lerp_towards(camera_offset_y_, target_y);
|
||||||
|
|
||||||
|
// Si no hay room adyacente pendiente, la cámara vuelve a 0, y al llegar terminamos
|
||||||
|
if (!transition_adjacent_room_ &&
|
||||||
|
std::abs(camera_offset_x_) < 1.0F && std::abs(camera_offset_y_) < 1.0F) {
|
||||||
|
endTransition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finaliza la transición entre pantallas
|
// Finaliza la transición entre pantallas
|
||||||
void Game::endTransition() {
|
void Game::endTransition() {
|
||||||
transitioning_ = false;
|
transitioning_ = false;
|
||||||
transition_just_ended_ = true;
|
camera_offset_x_ = 0.0F;
|
||||||
transition_timer_ = 0.0F;
|
camera_offset_y_ = 0.0F;
|
||||||
transition_old_room_.reset();
|
player_->clearAdjacentRoom();
|
||||||
transition_old_room_path_.clear();
|
transition_adjacent_room_.reset();
|
||||||
|
transition_adjacent_room_path_.clear();
|
||||||
transition_direction_ = Room::Border::NONE;
|
transition_direction_ = Room::Border::NONE;
|
||||||
Screen::get()->setRenderOffset(0, 0);
|
Screen::get()->setRenderOffset(0, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ class Game {
|
|||||||
static constexpr float DEMO_ROOM_DURATION = 6.0F; // Duración de cada habitación en modo demo en segundos (400 frames)
|
static constexpr float DEMO_ROOM_DURATION = 6.0F; // Duración de cada habitación en modo demo en segundos (400 frames)
|
||||||
static constexpr float FADE_STEP_INTERVAL = 0.05F; // Intervalo entre pasos de fade en segundos
|
static constexpr float FADE_STEP_INTERVAL = 0.05F; // Intervalo entre pasos de fade en segundos
|
||||||
static constexpr float POST_FADE_DELAY = 2.0F; // Duración de la pantalla negra después del fade
|
static constexpr float POST_FADE_DELAY = 2.0F; // Duración de la pantalla negra después del fade
|
||||||
static constexpr float TRANSITION_DURATION = 0.5F; // Duración de la transición entre pantallas en segundos
|
|
||||||
|
|
||||||
// --- Estructuras ---
|
// --- Estructuras ---
|
||||||
struct DemoData {
|
struct DemoData {
|
||||||
@@ -78,6 +77,7 @@ class Game {
|
|||||||
void togglePause(); // Pone el juego en pausa
|
void togglePause(); // Pone el juego en pausa
|
||||||
void initPlayer(const Player::SpawnData& spawn_point, std::shared_ptr<Room> room); // Inicializa al jugador
|
void initPlayer(const Player::SpawnData& spawn_point, std::shared_ptr<Room> room); // Inicializa al jugador
|
||||||
void endTransition(); // Finaliza la transición entre pantallas
|
void endTransition(); // Finaliza la transición entre pantallas
|
||||||
|
void updateTransitionCamera(float delta_time); // Actualiza la cámara durante la transición
|
||||||
void keepMusicPlaying(); // Hace sonar la música
|
void keepMusicPlaying(); // Hace sonar la música
|
||||||
void demoInit(); // DEMO MODE: Inicializa las variables para el modo demo
|
void demoInit(); // DEMO MODE: Inicializa las variables para el modo demo
|
||||||
void demoCheckRoomChange(float delta_time); // DEMO MODE: Comprueba si se ha de cambiar de habitación
|
void demoCheckRoomChange(float delta_time); // DEMO MODE: Comprueba si se ha de cambiar de habitación
|
||||||
@@ -108,11 +108,11 @@ class Game {
|
|||||||
|
|
||||||
// Transición animada entre pantallas
|
// Transición animada entre pantallas
|
||||||
bool transitioning_{false}; // Indica si hay una transición en curso
|
bool transitioning_{false}; // Indica si hay una transición en curso
|
||||||
float transition_timer_{0.0F}; // Tiempo transcurrido en la transición
|
std::shared_ptr<Room> transition_adjacent_room_; // Room adyacente durante la transición
|
||||||
std::shared_ptr<Room> transition_old_room_; // Habitación saliente (se mantiene viva durante la transición)
|
std::string transition_adjacent_room_path_; // Path de la room adyacente
|
||||||
Room::Border transition_direction_{Room::Border::NONE}; // Dirección de la transición
|
Room::Border transition_direction_{Room::Border::NONE}; // Dirección de la transición
|
||||||
std::string transition_old_room_path_; // Path de la habitación saliente (para poder restaurarla)
|
float camera_offset_x_{0.0F}; // Offset actual de la cámara (pixeles)
|
||||||
bool transition_just_ended_{false}; // Cooldown de 1 frame tras finalizar transición
|
float camera_offset_y_{0.0F}; // Offset actual de la cámara (pixeles)
|
||||||
|
|
||||||
// Variables de demo mode
|
// Variables de demo mode
|
||||||
DemoData demo_; // Variables para el modo demo
|
DemoData demo_; // Variables para el modo demo
|
||||||
|
|||||||
Reference in New Issue
Block a user