This commit is contained in:
2026-04-07 13:36:18 +02:00
parent e6eeb3396a
commit a50223fcd7
4 changed files with 257 additions and 143 deletions

View File

@@ -28,7 +28,6 @@
#include "game/ui/console.hpp" // Para Console
#include "game/ui/notifier.hpp" // Para Notifier, NotificationText, CHEEVO_NO...
#include "utils/defines.hpp" // Para Tile::SIZE, PlayArea::HEIGHT, RoomBorder::BOTTOM
#include "utils/easing_functions.hpp" // Para Easing::cubicInOut
#include "utils/utils.hpp"
#ifdef _DEBUG
@@ -309,8 +308,8 @@ void Game::updatePlaying(float delta_time) {
// Actualiza los objetos
room_->update(delta_time);
if (transitioning_) {
transition_old_room_->update(delta_time);
if (transitioning_ && transition_adjacent_room_) {
transition_adjacent_room_->update(delta_time);
}
switch (mode_) {
case Mode::GAME:
@@ -325,37 +324,14 @@ void Game::updatePlaying(float delta_time) {
#else
player_->update(delta_time);
#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();
checkPlayerAndItems();
checkPlayerAndEnemies();
checkIfPlayerIsAlive();
// Avanzar transición
// Actualizar cámara de transición
if (transitioning_) {
transition_timer_ += delta_time;
if (transition_timer_ >= TRANSITION_DURATION) {
endTransition();
}
updateTransitionCamera(delta_time);
}
break;
@@ -491,44 +467,11 @@ void Game::renderPlaying() {
if (transitioning_) {
// --- Transición animada entre pantallas ---
const float T = std::min(transition_timer_ / TRANSITION_DURATION, 1.0F);
const float P = Easing::cubicInOut(T);
int cam_x = static_cast<int>(camera_offset_x_);
int cam_y = static_cast<int>(camera_offset_y_);
// Calcular offsets (derivar uno del otro para evitar gap de 1px por truncamiento)
int old_ox = 0;
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);
// Renderizar habitación principal con offset de cámara
Screen::get()->setRenderOffset(cam_x, cam_y);
room_->renderMap();
room_->renderEnemies();
room_->renderItems();
@@ -536,6 +479,23 @@ void Game::renderPlaying() {
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
Screen::get()->setRenderOffset(0, 0);
scoreboard_->render();
@@ -813,59 +773,134 @@ auto Game::changeRoom(const std::string& room_path) -> bool {
// Comprueba si el jugador esta en el borde de la pantalla
void Game::checkPlayerIsOnBorder() {
// No permitir transiciones encadenadas
if (transitioning_) {
return;
}
if (transition_just_ended_) {
transition_just_ended_ = false;
return;
}
if (player_->isOnBorder()) {
const auto BORDER = player_->getBorder();
const auto ROOM_NAME = room_->getRoom(BORDER);
// Si no hay habitación adyacente
if (ROOM_NAME == "0") {
if (BORDER == Room::Border::BOTTOM) {
killPlayer();
}
return;
if (!player_->isOnBorder()) {
// Si hay transición activa y el jugador ha vuelto completamente dentro de bounds,
// 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();
}
return;
}
// Guardar la habitación saliente
transition_old_room_ = room_;
transition_old_room_path_ = current_room_;
transition_direction_ = BORDER;
const auto BORDER = player_->getBorder();
// Crear nueva habitación y reposicionar jugador
if (changeRoom(ROOM_NAME)) {
player_->switchBorders();
// 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();
// Iniciar transición animada
transitioning_ = true;
transition_timer_ = 0.0F;
} else {
// changeRoom falló, limpiar
transition_old_room_.reset();
// 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;
if (BORDER == Room::Border::BOTTOM) {
killPlayer();
// 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);
// Si no hay habitación adyacente
if (ROOM_NAME == "0") {
if (BORDER == Room::Border::BOTTOM) {
killPlayer();
}
return;
}
// Cargar room adyacente
auto adjacent_room = std::make_shared<Room>(ROOM_NAME, scoreboard_data_);
transition_adjacent_room_ = adjacent_room;
transition_adjacent_room_path_ = ROOM_NAME;
transition_direction_ = BORDER;
// Pasar la room adyacente al player para colisiones
player_->setAdjacentRoom(adjacent_room, BORDER);
// Iniciar transición (NO cambiar room_, NO reposicionar jugador)
transitioning_ = true;
if (room_tracker_->addRoom(ROOM_NAME)) {
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
void Game::endTransition() {
transitioning_ = false;
transition_just_ended_ = true;
transition_timer_ = 0.0F;
transition_old_room_.reset();
transition_old_room_path_.clear();
camera_offset_x_ = 0.0F;
camera_offset_y_ = 0.0F;
player_->clearAdjacentRoom();
transition_adjacent_room_.reset();
transition_adjacent_room_path_.clear();
transition_direction_ = Room::Border::NONE;
Screen::get()->setRenderOffset(0, 0);
}

View File

@@ -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 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 TRANSITION_DURATION = 0.5F; // Duración de la transición entre pantallas en segundos
// --- Estructuras ---
struct DemoData {
@@ -78,6 +77,7 @@ class Game {
void togglePause(); // Pone el juego en pausa
void initPlayer(const Player::SpawnData& spawn_point, std::shared_ptr<Room> room); // Inicializa al jugador
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 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
@@ -108,11 +108,11 @@ class Game {
// Transición animada entre pantallas
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_old_room_; // Habitación saliente (se mantiene viva durante la transición)
std::shared_ptr<Room> transition_adjacent_room_; // Room adyacente 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
std::string transition_old_room_path_; // Path de la habitación saliente (para poder restaurarla)
bool transition_just_ended_{false}; // Cooldown de 1 frame tras finalizar transición
float camera_offset_x_{0.0F}; // Offset actual de la cámara (pixeles)
float camera_offset_y_{0.0F}; // Offset actual de la cámara (pixeles)
// Variables de demo mode
DemoData demo_; // Variables para el modo demo