afegit pixel_reveal a credits i a ending

This commit is contained in:
2026-03-19 09:01:23 +01:00
parent 31bbaf997f
commit c80dffe957
12 changed files with 135 additions and 118 deletions

View File

@@ -2,13 +2,14 @@
#include <algorithm> // Para min #include <algorithm> // Para min
#include <numeric> // Para iota #include <numeric> // Para iota
#include <queue> // Para queue (BFS en modo ORDERED)
#include <random> // Para mt19937, shuffle #include <random> // Para mt19937, shuffle
#include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/surface.hpp" // Para Surface
#include "utils/utils.hpp" // Para PaletteColor #include "utils/utils.hpp" // Para PaletteColor
// Constructor // Constructor
PixelReveal::PixelReveal(int width, int height, float pixels_per_second, float step_duration, int num_steps) PixelReveal::PixelReveal(int width, int height, float pixels_per_second, float step_duration, int num_steps, bool reverse, RevealMode mode)
: cover_surface_(std::make_shared<Surface>(width, height)), : cover_surface_(std::make_shared<Surface>(width, height)),
reveal_order_(height), reveal_order_(height),
row_step_(height, 0), row_step_(height, 0),
@@ -16,16 +17,51 @@ PixelReveal::PixelReveal(int width, int height, float pixels_per_second, float s
height_(height), height_(height),
pixels_per_second_(pixels_per_second), pixels_per_second_(pixels_per_second),
step_duration_(step_duration), step_duration_(step_duration),
num_steps_(num_steps) { num_steps_(num_steps),
// Rellena la máscara con negro sólido reverse_(reverse),
cover_surface_->clear(static_cast<Uint8>(PaletteColor::BLACK)); mode_(mode) {
// En modo normal: empieza negro sólido (se irá revelando a transparente)
// En modo inverso: empieza transparente (se irá cubriendo de negro)
const auto INITIAL_COLOR = reverse_ ? static_cast<Uint8>(PaletteColor::TRANSPARENT) : static_cast<Uint8>(PaletteColor::BLACK);
cover_surface_->clear(INITIAL_COLOR);
// Genera el orden aleatorio de columnas por fila usando la fila como semilla (reproducible) if (mode_ == RevealMode::ORDERED) {
for (int r = 0; r < height_; r++) { // Calcula offsets por bisección BFS: 0, N/2, N/4, 3N/4, ...
reveal_order_[r].resize(width_); std::vector<int> offsets;
std::iota(reveal_order_[r].begin(), reveal_order_[r].end(), 0); offsets.push_back(0);
std::mt19937 rng(static_cast<unsigned int>(r)); std::queue<std::pair<int, int>> bq;
std::shuffle(reveal_order_[r].begin(), reveal_order_[r].end(), rng); bq.push({0, num_steps_});
while (static_cast<int>(offsets.size()) < num_steps_) {
auto [lo, hi] = bq.front();
bq.pop();
if (hi - lo <= 1) {
continue;
}
const int MID = (lo + hi) / 2;
offsets.push_back(MID);
bq.push({lo, MID});
bq.push({MID, hi});
}
// Genera el orden: para cada offset, todas las columnas col = offset, offset+N, offset+2N, ...
std::vector<int> ordered_cols;
ordered_cols.reserve(width_);
for (const int off : offsets) {
for (int col = off; col < width_; col += num_steps_) {
ordered_cols.push_back(col);
}
}
// Todas las filas usan el mismo orden (sin aleatoriedad)
for (int r = 0; r < height_; r++) {
reveal_order_[r] = ordered_cols;
}
} else {
// Modo RANDOM: orden aleatorio por fila usando la fila como semilla (reproducible)
for (int r = 0; r < height_; r++) {
reveal_order_[r].resize(width_);
std::iota(reveal_order_[r].begin(), reveal_order_[r].end(), 0);
std::mt19937 rng(static_cast<unsigned int>(r));
std::shuffle(reveal_order_[r].begin(), reveal_order_[r].end(), rng);
}
} }
} }
@@ -34,7 +70,8 @@ PixelReveal::~PixelReveal() = default;
// Actualiza el estado del revelado // Actualiza el estado del revelado
void PixelReveal::update(float time_active) { void PixelReveal::update(float time_active) {
const auto TRANSPARENT = static_cast<Uint8>(PaletteColor::TRANSPARENT); // En modo normal revela (pone transparente); en modo inverso cubre (pone negro)
const auto PIXEL_COLOR = reverse_ ? static_cast<Uint8>(PaletteColor::BLACK) : static_cast<Uint8>(PaletteColor::TRANSPARENT);
for (int r = 0; r < height_; r++) { for (int r = 0; r < height_; r++) {
const float T_START = static_cast<float>(r) / pixels_per_second_; const float T_START = static_cast<float>(r) / pixels_per_second_;
@@ -47,14 +84,14 @@ void PixelReveal::update(float time_active) {
const int STEPS = std::min(num_steps_, static_cast<int>(TIME_IN_ROW / step_duration_)); const int STEPS = std::min(num_steps_, static_cast<int>(TIME_IN_ROW / step_duration_));
if (STEPS > row_step_[r]) { if (STEPS > row_step_[r]) {
// Revela los píxeles de los pasos pendientes // Procesa los píxeles de los pasos pendientes
for (int step = row_step_[r]; step < STEPS; step++) { for (int step = row_step_[r]; step < STEPS; step++) {
const int START_IDX = step * width_ / num_steps_; const int START_IDX = step * width_ / num_steps_;
const int END_IDX = (step == num_steps_ - 1) ? width_ : (step + 1) * width_ / num_steps_; const int END_IDX = (step == num_steps_ - 1) ? width_ : (step + 1) * width_ / num_steps_;
for (int idx = START_IDX; idx < END_IDX; idx++) { for (int idx = START_IDX; idx < END_IDX; idx++) {
const int COL = reveal_order_[r][idx]; const int COL = reveal_order_[r][idx];
cover_surface_->putPixel(COL, r, TRANSPARENT); cover_surface_->putPixel(COL, r, PIXEL_COLOR);
} }
} }
row_step_[r] = STEPS; row_step_[r] = STEPS;

View File

@@ -6,11 +6,15 @@
class Surface; class Surface;
// Efecto de revelado pixel a pixel por filas, de arriba a abajo. // Efecto de revelado pixel a pixel por filas, de arriba a abajo.
// Cada fila se revela en num_steps pasos, con píxeles aleatorios en cada paso. // Cada fila se revela en num_steps pasos, con píxeles en orden aleatorio u ordenado (bisección).
class PixelReveal { class PixelReveal {
public: public:
// Modo de revelado: aleatorio por fila o en orden de bisección (dithering ordenado 1D)
enum class RevealMode { RANDOM,
ORDERED };
// Constructor // Constructor
PixelReveal(int width, int height, float pixels_per_second, float step_duration, int num_steps = 4); PixelReveal(int width, int height, float pixels_per_second, float step_duration, int num_steps = 4, bool reverse = false, RevealMode mode = RevealMode::RANDOM);
// Destructor definido en el .cpp para que unique_ptr<Surface> funcione con forward declaration // Destructor definido en el .cpp para que unique_ptr<Surface> funcione con forward declaration
~PixelReveal(); ~PixelReveal();
@@ -25,12 +29,14 @@ class PixelReveal {
[[nodiscard]] bool isComplete() const; [[nodiscard]] bool isComplete() const;
private: private:
std::shared_ptr<Surface> cover_surface_; // Máscara negra que se va haciendo transparente std::shared_ptr<Surface> cover_surface_; // Máscara negra que se va haciendo transparente
std::vector<std::vector<int>> reveal_order_; // Orden aleatorio de columnas por fila std::vector<std::vector<int>> reveal_order_; // Orden de columnas por fila (aleatorio u ordenado por bisección)
std::vector<int> row_step_; // Paso actual de revelado por fila (0..num_steps_) std::vector<int> row_step_; // Paso actual de revelado por fila (0..num_steps_)
int width_; int width_;
int height_; int height_;
float pixels_per_second_; // Filas reveladas por segundo float pixels_per_second_; // Filas reveladas por segundo
float step_duration_; // Segundos por paso dentro de una fila float step_duration_; // Segundos por paso dentro de una fila
int num_steps_; // Número de pasos de revelado por fila int num_steps_; // Número de pasos de revelado por fila
bool reverse_; // Si true: transparente → negro (ocultar); si false: negro → transparente (revelar)
RevealMode mode_; // Modo de revelado: aleatorio u ordenado por bisección
}; };

View File

@@ -90,9 +90,9 @@ constexpr int GAMEPAD_BUTTON_JUMP = SDL_GAMEPAD_BUTTON_WEST; // Botón s
// --- KIOSK --- // --- KIOSK ---
namespace Kiosk { namespace Kiosk {
constexpr bool ENABLED = false; // Modo kiosko desactivado por defecto constexpr bool ENABLED = false; // Modo kiosko desactivado por defecto
constexpr const char* TEXT = ""; // Texto del modo kiosko por defecto constexpr const char* TEXT = ""; // Texto del modo kiosko por defecto
constexpr bool INFINITE_LIVES = false; // Vidas infinitas en modo kiosko desactivadas por defecto constexpr bool INFINITE_LIVES = false; // Vidas infinitas en modo kiosko desactivadas por defecto
} // namespace Kiosk } // namespace Kiosk
// --- GAME (posición y habitación inicial) --- // --- GAME (posición y habitación inicial) ---
@@ -100,21 +100,21 @@ namespace Game {
namespace Room { namespace Room {
#ifdef _DEBUG #ifdef _DEBUG
constexpr const char* INITIAL = "51.yaml"; // Habitación de inicio en debug constexpr const char* INITIAL = "51.yaml"; // Habitación de inicio en debug
#else #else
constexpr const char* INITIAL = "03.yaml"; // Habitación de inicio en release constexpr const char* INITIAL = "03.yaml"; // Habitación de inicio en release
#endif #endif
} // namespace Room } // namespace Room
namespace Player { namespace Player {
#ifdef _DEBUG #ifdef _DEBUG
constexpr int SPAWN_X = 26 * Tile::SIZE; // Posición X inicial en debug constexpr int SPAWN_X = 26 * Tile::SIZE; // Posición X inicial en debug
constexpr int SPAWN_Y = 10 * Tile::SIZE; // Posición Y inicial en debug constexpr int SPAWN_Y = 10 * Tile::SIZE; // Posición Y inicial en debug
constexpr SDL_FlipMode SPAWN_FLIP = Flip::LEFT; // Orientación inicial en debug constexpr SDL_FlipMode SPAWN_FLIP = Flip::LEFT; // Orientación inicial en debug
#else #else
constexpr int SPAWN_X = 25 * Tile::SIZE; // Posición X inicial en release constexpr int SPAWN_X = 25 * Tile::SIZE; // Posición X inicial en release
constexpr int SPAWN_Y = 13 * Tile::SIZE; // Posición Y inicial en release constexpr int SPAWN_Y = 13 * Tile::SIZE; // Posición Y inicial en release
constexpr SDL_FlipMode SPAWN_FLIP = Flip::LEFT; // Orientación inicial en release constexpr SDL_FlipMode SPAWN_FLIP = Flip::LEFT; // Orientación inicial en release
#endif #endif
} // namespace Player } // namespace Player

View File

@@ -872,14 +872,14 @@ auto Player::getProjection(Direction direction, float displacement) -> SDL_FRect
.x = x_ + displacement, .x = x_ + displacement,
.y = y_, .y = y_,
.w = std::ceil(std::fabs(displacement)), // Para evitar que tenga una anchura de 0 pixels .w = std::ceil(std::fabs(displacement)), // Para evitar que tenga una anchura de 0 pixels
.h = HEIGHT - 1}; // -1 para dar ventana de 2px en aperturas de altura exacta .h = HEIGHT - 1}; // -1 para dar ventana de 2px en aperturas de altura exacta
case Direction::RIGHT: case Direction::RIGHT:
return { return {
.x = x_ + WIDTH, .x = x_ + WIDTH,
.y = y_, .y = y_,
.w = std::ceil(displacement), // Para evitar que tenga una anchura de 0 pixels .w = std::ceil(displacement), // Para evitar que tenga una anchura de 0 pixels
.h = HEIGHT - 1}; // -1 para dar ventana de 2px en aperturas de altura exacta .h = HEIGHT - 1}; // -1 para dar ventana de 2px en aperturas de altura exacta
case Direction::UP: case Direction::UP:
return { return {

View File

@@ -99,7 +99,7 @@ class Player {
auto getRect() -> SDL_FRect { return {x_, y_, WIDTH, HEIGHT}; } // Obtiene el rectangulo que delimita al jugador auto getRect() -> SDL_FRect { return {x_, y_, WIDTH, HEIGHT}; } // Obtiene el rectangulo que delimita al jugador
auto getCollider() -> SDL_FRect& { return collider_box_; } // Obtiene el rectangulo de colision del jugador auto getCollider() -> SDL_FRect& { return collider_box_; } // Obtiene el rectangulo de colision del jugador
auto getSpawnParams() -> SpawnData { return {.x = x_, .y = y_, .vx = vx_, .vy = vy_, .last_grounded_position = last_grounded_position_, .state = state_, .flip = sprite_->getFlip()}; } // Obtiene el estado de reaparición del jugador auto getSpawnParams() -> SpawnData { return {.x = x_, .y = y_, .vx = vx_, .vy = vy_, .last_grounded_position = last_grounded_position_, .state = state_, .flip = sprite_->getFlip()}; } // Obtiene el estado de reaparición del jugador
void setColor(Uint8 color = 0); // Establece el color del jugador (0 = automático según cheats) void setColor(Uint8 color = 0); // Establece el color del jugador (0 = automático según cheats)
void setRoom(std::shared_ptr<Room> room) { room_ = std::move(room); } // Establece la habitación en la que se encuentra el jugador void setRoom(std::shared_ptr<Room> room) { room_ = std::move(room); } // Establece la habitación en la que se encuentra el jugador
//[[nodiscard]] auto isAlive() const -> bool { return is_alive_ || (Options::cheats.invincible == Options::Cheat::State::ENABLED); } // Comprueba si el jugador esta vivo //[[nodiscard]] auto isAlive() const -> bool { return is_alive_ || (Options::cheats.invincible == Options::Cheat::State::ENABLED); } // Comprueba si el jugador esta vivo
[[nodiscard]] auto isAlive() const -> bool { return is_alive_; } // Comprueba si el jugador esta vivo [[nodiscard]] auto isAlive() const -> bool { return is_alive_; } // Comprueba si el jugador esta vivo

View File

@@ -55,9 +55,9 @@ struct Stats {
// Estructura para el modo kiosko // Estructura para el modo kiosko
struct Kiosk { struct Kiosk {
bool enabled{Defaults::Kiosk::ENABLED}; // Indica si el modo kiosko está activo bool enabled{Defaults::Kiosk::ENABLED}; // Indica si el modo kiosko está activo
std::string text{Defaults::Kiosk::TEXT}; // Texto a mostrar en el modo kiosko std::string text{Defaults::Kiosk::TEXT}; // Texto a mostrar en el modo kiosko
bool infinite_lives{Defaults::Kiosk::INFINITE_LIVES}; // Indica si el jugador tiene vidas infinitas en modo kiosko bool infinite_lives{Defaults::Kiosk::INFINITE_LIVES}; // Indica si el jugador tiene vidas infinitas en modo kiosko
}; };
// Estructura con opciones de la ventana // Estructura con opciones de la ventana

View File

@@ -34,7 +34,7 @@ enum class Options {
// --- Variables de estado globales --- // --- Variables de estado globales ---
#ifdef _DEBUG #ifdef _DEBUG
inline Scene current = Scene::ENDING; // Escena actual inline Scene current = Scene::CREDITS; // Escena actual
inline Options options = Options::LOGO_TO_LOADING_SCREEN; // Opciones de la escena actual inline Options options = Options::LOGO_TO_LOADING_SCREEN; // Opciones de la escena actual
#else #else
inline Scene current = Scene::LOGO; // Escena actual inline Scene current = Scene::LOGO; // Escena actual

View File

@@ -31,11 +31,9 @@ Credits::Credits()
SceneManager::options = SceneManager::Options::NONE; SceneManager::options = SceneManager::Options::NONE;
shining_sprite_->setPos({194, 174, 8, 8}); shining_sprite_->setPos({194, 174, 8, 8});
// Cambia el color del borde Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK)); // Cambia el color del borde
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK)); fillTexture(); // Escribe el texto en la textura
Audio::get()->playMusic("title.ogg"); // Inicia la musica
// Escribe el texto en la textura
fillTexture();
} }
// Comprueba el manejador de eventos // Comprueba el manejador de eventos

View File

@@ -48,9 +48,9 @@ class Credits {
static constexpr float TOTAL_DURATION = 20.0F; // 1200 frames @ 60fps static constexpr float TOTAL_DURATION = 20.0F; // 1200 frames @ 60fps
static constexpr float SHINE_START_TIME = 12.833F; // 770 frames @ 60fps static constexpr float SHINE_START_TIME = 12.833F; // 770 frames @ 60fps
static constexpr float FADE_OUT_START = 19.167F; // 1150 frames @ 60fps static constexpr float FADE_OUT_START = 19.167F; // 1150 frames @ 60fps
static constexpr float PIXELS_PER_SECOND = 15.0F; // Filas reveladas por segundo (REVEAL_SPEED/8*2 = 60/8*2 = 15) static constexpr float PIXELS_PER_SECOND = 15.0F; // Filas reveladas por segundo (REVEAL_SPEED/8*2 = 60/8*2 = 15)
static constexpr float STEP_DURATION = 2.0F / 60.0F; // Segundos por paso de revelado (2 frames @ 60fps) static constexpr float STEP_DURATION = 2.0F / 60.0F; // Segundos por paso de revelado (2 frames @ 60fps)
static constexpr int REVEAL_STEPS = 16; // Pasos de revelado por fila (más pasos = efecto más visible) static constexpr int REVEAL_STEPS = 16; // Pasos de revelado por fila (más pasos = efecto más visible)
// --- Métodos privados --- // --- Métodos privados ---
void update(); // Actualiza las variables void update(); // Actualiza las variables

View File

@@ -2,8 +2,6 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <algorithm> // Para min
#include "core/audio/audio.hpp" // Para Audio #include "core/audio/audio.hpp" // Para Audio
#include "core/input/global_inputs.hpp" // Para check #include "core/input/global_inputs.hpp" // Para check
#include "core/input/input.hpp" // Para Input #include "core/input/input.hpp" // Para Input
@@ -16,7 +14,6 @@
#include "core/system/global_events.hpp" // Para check #include "core/system/global_events.hpp" // Para check
#include "game/options.hpp" // Para Options, options, OptionsGame, SectionS... #include "game/options.hpp" // Para Options, options, OptionsGame, SectionS...
#include "game/scene_manager.hpp" // Para SceneManager #include "game/scene_manager.hpp" // Para SceneManager
#include "utils/defines.hpp" // Para GAME_SPEED
#include "utils/delta_timer.hpp" // Para DeltaTimer #include "utils/delta_timer.hpp" // Para DeltaTimer
#include "utils/utils.hpp" // Para PaletteColor #include "utils/utils.hpp" // Para PaletteColor
@@ -30,9 +27,7 @@ Ending::Ending()
iniPics(); // Inicializa las imagenes iniPics(); // Inicializa las imagenes
iniScenes(); // Inicializa las escenas iniScenes(); // Inicializa las escenas
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK)); // Cambia el color del borde Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK)); // Cambia el color del borde
cover_surface_ = std::make_shared<Surface>(Options::game.width, Options::game.height + 8); // Crea la textura para cubrir el texto
fillCoverTexture(); // Rellena la textura para la cortinilla
} }
// Destructor // Destructor
@@ -81,7 +76,9 @@ void Ending::render() {
} }
// Dibuja la cortinilla de cambio de escena // Dibuja la cortinilla de cambio de escena
renderCoverTexture(); if (scene_cover_) {
scene_cover_->render(0, 0);
}
} }
// Vuelca el contenido del renderizador en pantalla // Vuelca el contenido del renderizador en pantalla
@@ -107,9 +104,10 @@ void Ending::transitionToState(State new_state) {
state_ = new_state; state_ = new_state;
state_time_ = 0.0F; state_time_ = 0.0F;
// Al cambiar a una escena, resetear fadeout_time_ // Al cambiar a una escena, resetear la cortinilla de salida y el contador de fade
if (new_state != State::WARMING_UP && new_state != State::ENDING) { if (new_state != State::WARMING_UP && new_state != State::ENDING) {
fadeout_time_ = 0.0F; fadeout_time_ = 0.0F;
scene_cover_.reset();
} }
} }
@@ -127,9 +125,12 @@ void Ending::updateState(float delta_time) {
case State::SCENE_0: case State::SCENE_0:
checkChangeScene(); checkChangeScene();
// Actualizar fadeout_time_ si estamos cerca del final
if (state_time_ >= SCENE_0_DURATION - FADEOUT_START_OFFSET) { if (state_time_ >= SCENE_0_DURATION - FADEOUT_START_OFFSET) {
fadeout_time_ += delta_time; fadeout_time_ += delta_time;
if (!scene_cover_) {
scene_cover_ = std::make_unique<PixelReveal>(Options::game.width, Options::game.height, COVER_PIXELS_PER_SECOND, STEP_DURATION, COVER_STEPS, true);
}
scene_cover_->update(fadeout_time_);
} }
break; break;
@@ -137,6 +138,10 @@ void Ending::updateState(float delta_time) {
checkChangeScene(); checkChangeScene();
if (state_time_ >= SCENE_1_DURATION - FADEOUT_START_OFFSET) { if (state_time_ >= SCENE_1_DURATION - FADEOUT_START_OFFSET) {
fadeout_time_ += delta_time; fadeout_time_ += delta_time;
if (!scene_cover_) {
scene_cover_ = std::make_unique<PixelReveal>(Options::game.width, Options::game.height, COVER_PIXELS_PER_SECOND, STEP_DURATION, COVER_STEPS, true);
}
scene_cover_->update(fadeout_time_);
} }
break; break;
@@ -144,6 +149,10 @@ void Ending::updateState(float delta_time) {
checkChangeScene(); checkChangeScene();
if (state_time_ >= SCENE_2_DURATION - FADEOUT_START_OFFSET) { if (state_time_ >= SCENE_2_DURATION - FADEOUT_START_OFFSET) {
fadeout_time_ += delta_time; fadeout_time_ += delta_time;
if (!scene_cover_) {
scene_cover_ = std::make_unique<PixelReveal>(Options::game.width, Options::game.height, COVER_PIXELS_PER_SECOND, STEP_DURATION, COVER_STEPS, true);
}
scene_cover_->update(fadeout_time_);
} }
break; break;
@@ -151,6 +160,10 @@ void Ending::updateState(float delta_time) {
checkChangeScene(); checkChangeScene();
if (state_time_ >= SCENE_3_DURATION - FADEOUT_START_OFFSET) { if (state_time_ >= SCENE_3_DURATION - FADEOUT_START_OFFSET) {
fadeout_time_ += delta_time; fadeout_time_ += delta_time;
if (!scene_cover_) {
scene_cover_ = std::make_unique<PixelReveal>(Options::game.width, Options::game.height, COVER_PIXELS_PER_SECOND, STEP_DURATION, COVER_STEPS, true);
}
scene_cover_->update(fadeout_time_);
} }
break; break;
@@ -158,6 +171,10 @@ void Ending::updateState(float delta_time) {
checkChangeScene(); checkChangeScene();
if (state_time_ >= SCENE_4_DURATION - FADEOUT_START_OFFSET) { if (state_time_ >= SCENE_4_DURATION - FADEOUT_START_OFFSET) {
fadeout_time_ += delta_time; fadeout_time_ += delta_time;
if (!scene_cover_) {
scene_cover_ = std::make_unique<PixelReveal>(Options::game.width, Options::game.height, COVER_PIXELS_PER_SECOND, STEP_DURATION, COVER_STEPS, true);
}
scene_cover_->update(fadeout_time_);
} }
break; break;
@@ -432,42 +449,3 @@ void Ending::checkChangeScene() {
} }
} }
} }
// Rellena la textura para la cortinilla
void Ending::fillCoverTexture() {
// Rellena la textura que cubre el texto con color transparente
auto previuos_renderer = Screen::get()->getRendererSurface();
Screen::get()->setRendererSurface(cover_surface_);
cover_surface_->clear(static_cast<Uint8>(PaletteColor::TRANSPARENT));
// Los primeros 8 pixels crea una malla
const auto COLOR = static_cast<Uint8>(PaletteColor::BLACK);
auto surface = Screen::get()->getRendererSurface();
for (int i = 0; i < 256; i += 2) {
surface->putPixel(i + 0, Options::game.height + 0, COLOR);
surface->putPixel(i + 1, Options::game.height + 1, COLOR);
surface->putPixel(i + 0, Options::game.height + 2, COLOR);
surface->putPixel(i + 1, Options::game.height + 3, COLOR);
surface->putPixel(i, Options::game.height + 4, COLOR);
surface->putPixel(i, Options::game.height + 6, COLOR);
}
// El resto se rellena de color sólido
SDL_FRect rect = {0, 0, 256, Options::game.height};
surface->fillRect(&rect, COLOR);
Screen::get()->setRendererSurface(previuos_renderer);
}
// Dibuja la cortinilla de cambio de escena
void Ending::renderCoverTexture() {
if (fadeout_time_ > 0.0F) {
// Convertir fadeout_time_ a equivalente de cover_counter_ @ 60fps
const float FADEOUT_COUNTER = std::min(fadeout_time_ * 60.0F, 100.0F);
SDL_FRect src_rect = {0.0F, 200.0F - (FADEOUT_COUNTER * 2.0F), 256.0F, FADEOUT_COUNTER * 2.0F};
SDL_FRect dst_rect = {0.0F, 0.0F, 256.0F, FADEOUT_COUNTER * 2.0F};
cover_surface_->render(&src_rect, &dst_rect);
}
}

View File

@@ -57,21 +57,22 @@ class Ending {
}; };
// --- Constantes de tiempo (basado en 60 FPS) --- // --- Constantes de tiempo (basado en 60 FPS) ---
static constexpr float WARMUP_DURATION = 3.333F; // 200 frames @ 60fps static constexpr float WARMUP_DURATION = 3.333F; // 200 frames @ 60fps
static constexpr float FADEOUT_DURATION = 1.667F; // 100 frames @ 60fps static constexpr float SCENE_0_DURATION = 16.667F; // 1000 frames @ 60fps
static constexpr float SCENE_0_DURATION = 16.667F; // 1000 frames @ 60fps static constexpr float SCENE_1_DURATION = 23.333F; // 1400 frames @ 60fps
static constexpr float SCENE_1_DURATION = 23.333F; // 1400 frames @ 60fps static constexpr float SCENE_2_DURATION = 16.667F; // 1000 frames @ 60fps
static constexpr float SCENE_2_DURATION = 16.667F; // 1000 frames @ 60fps static constexpr float SCENE_3_DURATION = 13.333F; // 800 frames @ 60fps
static constexpr float SCENE_3_DURATION = 13.333F; // 800 frames @ 60fps static constexpr float SCENE_4_DURATION = 16.667F; // 1000 frames @ 60fps
static constexpr float SCENE_4_DURATION = 16.667F; // 1000 frames @ 60fps static constexpr float TEXT_PIXELS_PER_SECOND = 30.0F; // Filas de texto reveladas por segundo
static constexpr float TEXT_PIXELS_PER_SECOND = 30.0F; // Filas de texto reveladas por segundo static constexpr float IMAGE_PIXELS_PER_SECOND = 60.0F; // Filas de imagen reveladas por segundo
static constexpr float IMAGE_PIXELS_PER_SECOND = 60.0F; // Filas de imagen reveladas por segundo static constexpr float STEP_DURATION = 2.0F / 60.0F; // Segundos por paso de revelado (2 frames @ 60fps)
static constexpr float STEP_DURATION = 2.0F / 60.0F; // Segundos por paso de revelado (2 frames @ 60fps) static constexpr int REVEAL_STEPS = 4; // Pasos de revelado por fila
static constexpr int REVEAL_STEPS = 10; // Pasos de revelado por fila (más pasos = efecto más visible) static constexpr float TEXT_LAPSE = 1.333F; // 80 frames @ 60fps
static constexpr float TEXT_LAPSE = 1.333F; // 80 frames @ 60fps static constexpr float FADEOUT_START_OFFSET = 1.667F; // Inicio cortinilla 100 frames antes del fin
static constexpr float FADEOUT_START_OFFSET = 1.667F; // Inicio fade-out 100 frames antes del fin static constexpr float COVER_PIXELS_PER_SECOND = 120.0F; // Filas cubiertas por segundo
static constexpr float ENDING_DURATION = 2.0F; // Duración del estado ENDING (2 segundos) static constexpr int COVER_STEPS = 4; // Pasos por fila
static constexpr int MUSIC_FADE_DURATION = 1800; // Fade de audio en milisegundos (1.8 segundos) static constexpr float ENDING_DURATION = 2.0F; // Duración del estado ENDING (2 segundos)
static constexpr int MUSIC_FADE_DURATION = 1800; // Fade de audio en milisegundos (1.8 segundos)
// --- Métodos --- // --- Métodos ---
void update(); // Actualiza el objeto void update(); // Actualiza el objeto
@@ -85,22 +86,20 @@ class Ending {
void transitionToState(State new_state); // Transición entre estados void transitionToState(State new_state); // Transición entre estados
void updateSpriteCovers(); // Actualiza las cortinillas de los elementos void updateSpriteCovers(); // Actualiza las cortinillas de los elementos
void checkChangeScene(); // Comprueba si se ha de cambiar de escena void checkChangeScene(); // Comprueba si se ha de cambiar de escena
void fillCoverTexture(); // Rellena la textura para la cortinilla
void renderCoverTexture(); // Dibuja la cortinilla de cambio de escena
void updateMusicVolume() const; // Actualiza el volumen de la música void updateMusicVolume() const; // Actualiza el volumen de la música
// --- Variables miembro --- // --- Variables miembro ---
// Objetos y punteros a recursos // Objetos y punteros a recursos
std::shared_ptr<Surface> cover_surface_; // Surface para cubrir el texto std::unique_ptr<PixelReveal> scene_cover_; // Cortinilla de salida (negro sobre la escena)
std::unique_ptr<DeltaTimer> delta_timer_; // Timer para time-based update std::unique_ptr<DeltaTimer> delta_timer_; // Timer para time-based update
std::vector<EndingSurface> sprite_texts_; // Vector con los sprites de texto con su cortinilla std::vector<EndingSurface> sprite_texts_; // Vector con los sprites de texto con su cortinilla
std::vector<EndingSurface> sprite_pics_; // Vector con los sprites de imágenes con su cortinilla std::vector<EndingSurface> sprite_pics_; // Vector con los sprites de imágenes con su cortinilla
std::vector<SceneData> scenes_; // Vector con los textos e imágenes de cada escena std::vector<SceneData> scenes_; // Vector con los textos e imágenes de cada escena
// Variables de estado // Variables de estado
State state_{State::WARMING_UP}; // Estado actual State state_{State::WARMING_UP}; // Estado actual
float state_time_{0.0F}; // Tiempo acumulado en el estado actual float state_time_{0.0F}; // Tiempo acumulado en el estado actual
float total_time_{0.0F}; // Tiempo total acumulado desde el inicio float total_time_{0.0F}; // Tiempo total acumulado desde el inicio
float fadeout_time_{0.0F}; // Tiempo acumulado para el fade-out float fadeout_time_{0.0F}; // Tiempo acumulado para la cortinilla de salida
int current_scene_{0}; // Escena actual (0-4) int current_scene_{0}; // Escena actual (0-4)
}; };

View File

@@ -15,6 +15,7 @@
#include "core/resources/resource_cache.hpp" // Para ResourceRoom, Resource #include "core/resources/resource_cache.hpp" // Para ResourceRoom, Resource
#include "core/resources/resource_list.hpp" // Para Asset #include "core/resources/resource_list.hpp" // Para Asset
#include "core/system/global_events.hpp" // Para check #include "core/system/global_events.hpp" // Para check
#include "game/defaults.hpp" // Para Defaults::Game
#include "game/gameplay/cheevos.hpp" // Para Cheevos #include "game/gameplay/cheevos.hpp" // Para Cheevos
#include "game/gameplay/item_tracker.hpp" // Para ItemTracker #include "game/gameplay/item_tracker.hpp" // Para ItemTracker
#include "game/gameplay/room.hpp" // Para Room, RoomData #include "game/gameplay/room.hpp" // Para Room, RoomData
@@ -26,7 +27,6 @@
#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/utils.hpp" // Para PaletteColor, stringToColor #include "utils/utils.hpp" // Para PaletteColor, stringToColor
#include "game/defaults.hpp" // Para Defaults::Game
#ifdef _DEBUG #ifdef _DEBUG
#include "core/system/debug.hpp" // Para Debug #include "core/system/debug.hpp" // Para Debug
@@ -40,8 +40,7 @@ Game::Game(Mode mode)
stats_(std::make_shared<Stats>(Resource::List::get()->get("stats.csv"), Resource::List::get()->get("stats_buffer.csv"))), stats_(std::make_shared<Stats>(Resource::List::get()->get("stats.csv"), Resource::List::get()->get("stats_buffer.csv"))),
mode_(mode), mode_(mode),
current_room_(Defaults::Game::Room::INITIAL), current_room_(Defaults::Game::Room::INITIAL),
spawn_data_(Player::SpawnData(Defaults::Game::Player::SPAWN_X, Defaults::Game::Player::SPAWN_Y, 0, 0, 0, Player::State::ON_GROUND, Defaults::Game::Player::SPAWN_FLIP)) spawn_data_(Player::SpawnData(Defaults::Game::Player::SPAWN_X, Defaults::Game::Player::SPAWN_Y, 0, 0, 0, Player::State::ON_GROUND, Defaults::Game::Player::SPAWN_FLIP)) {
{
// Crea objetos e inicializa variables // Crea objetos e inicializa variables
ItemTracker::init(); ItemTracker::init();
demoInit(); demoInit();