pasada de granera
This commit is contained in:
@@ -1,247 +0,0 @@
|
||||
#include "game/scenes/credits.hpp"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "core/audio/audio.hpp" // Para Audio
|
||||
#include "core/input/global_inputs.hpp" // Para check
|
||||
#include "core/input/input.hpp" // Para Input
|
||||
#include "core/locale/locale.hpp" // Para Locale
|
||||
#include "core/rendering/pixel_reveal.hpp" // Para PixelReveal
|
||||
#include "core/rendering/screen.hpp" // Para Screen
|
||||
#include "core/rendering/sprite/animated_sprite.hpp" // Para SAnimatedSprite
|
||||
#include "core/rendering/surface.hpp" // Para Surface
|
||||
#include "core/rendering/text.hpp" // Para Text, Text::CENTER_FLAG, Text::COLOR_FLAG
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||
#include "core/system/global_events.hpp" // Para check
|
||||
#include "game/options.hpp" // Para Options, options, OptionsGame, Sectio...
|
||||
#include "game/scene_manager.hpp" // Para SceneManager
|
||||
#include "utils/defines.hpp" // Para GAME_SPEED, PlayArea::CENTER_X, PLAY_...
|
||||
#include "utils/delta_timer.hpp" // Para DeltaTimer
|
||||
#include "utils/utils.hpp" // Para PaletteColor
|
||||
|
||||
// Destructor
|
||||
Credits::~Credits() = default;
|
||||
|
||||
// Constructor
|
||||
Credits::Credits()
|
||||
: text_surface_(std::make_shared<Surface>(Options::game.width, Options::game.height)),
|
||||
shining_sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData("shine.yaml"))),
|
||||
delta_timer_(std::make_unique<DeltaTimer>()) {
|
||||
// Configura la escena
|
||||
SceneManager::current = SceneManager::Scene::CREDITS;
|
||||
SceneManager::options = SceneManager::Options::NONE;
|
||||
shining_sprite_->setPos({.x = 194, .y = 174, .w = 8, .h = 8});
|
||||
|
||||
Screen::get()->setBorderColor(0); // Cambia el color del borde
|
||||
fillTexture(); // Escribe el texto en la textura
|
||||
Audio::get()->playMusic("574071_EA_DTV.ogg"); // Inicia la musica
|
||||
}
|
||||
|
||||
// Comprueba el manejador de eventos
|
||||
void Credits::handleEvents() {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
GlobalEvents::handle(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba las entradas
|
||||
void Credits::handleInput() {
|
||||
Input::get()->update();
|
||||
GlobalInputs::handle();
|
||||
}
|
||||
|
||||
// Inicializa los textos
|
||||
void Credits::iniTexts() { // NOLINT(readability-convert-member-functions-to-static)
|
||||
auto* loc = Locale::get();
|
||||
|
||||
texts_.clear();
|
||||
texts_.push_back({.label = "", .color = 14});
|
||||
texts_.push_back({.label = loc->get("credits.instructions"), .color = 12});
|
||||
texts_.push_back({.label = "", .color = 14});
|
||||
texts_.push_back({.label = loc->get("credits.l0"), .color = 14});
|
||||
texts_.push_back({.label = loc->get("credits.l1"), .color = 14});
|
||||
texts_.push_back({.label = loc->get("credits.l2"), .color = 14});
|
||||
texts_.push_back({.label = "", .color = 14});
|
||||
texts_.push_back({.label = "", .color = 14});
|
||||
|
||||
texts_.push_back({.label = loc->get("credits.keys"), .color = 12});
|
||||
texts_.push_back({.label = "", .color = 14});
|
||||
texts_.push_back({.label = loc->get("credits.keys_move"), .color = 14});
|
||||
texts_.push_back({.label = loc->get("credits.f8"), .color = 14});
|
||||
texts_.push_back({.label = loc->get("credits.f11"), .color = 14});
|
||||
texts_.push_back({.label = loc->get("credits.f1f2"), .color = 14});
|
||||
texts_.push_back({.label = loc->get("credits.f3"), .color = 14});
|
||||
texts_.push_back({.label = loc->get("credits.f9"), .color = 14});
|
||||
texts_.push_back({.label = "", .color = 14});
|
||||
texts_.push_back({.label = "", .color = 14});
|
||||
|
||||
texts_.push_back({.label = loc->get("credits.author"), .color = 12});
|
||||
texts_.push_back({.label = loc->get("credits.date"), .color = 12});
|
||||
texts_.push_back({.label = "", .color = 14});
|
||||
texts_.push_back({.label = "", .color = 14});
|
||||
|
||||
texts_.push_back({.label = loc->get("credits.love"), .color = 14});
|
||||
texts_.push_back({.label = "", .color = 14});
|
||||
}
|
||||
|
||||
// Escribe el texto en la textura
|
||||
void Credits::fillTexture() {
|
||||
// Inicializa los textos
|
||||
iniTexts();
|
||||
|
||||
// Rellena la textura de texto
|
||||
auto previuos_renderer = Screen::get()->getRendererSurface();
|
||||
Screen::get()->setRendererSurface(text_surface_);
|
||||
text_surface_->clear(0);
|
||||
|
||||
auto text = Resource::Cache::get()->getText("smb2");
|
||||
|
||||
// Escribe el texto en la textura
|
||||
const int SIZE = text->getCharacterSize();
|
||||
int pos_y = 0;
|
||||
|
||||
for (const auto& t : texts_) {
|
||||
text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, pos_y * SIZE, t.label, 1, t.color);
|
||||
pos_y++;
|
||||
}
|
||||
|
||||
// Escribe el corazón
|
||||
const int TEXT_LENGHT = text->length(texts_[22].label, 1) - text->length(" ", 1); // Se resta el ultimo caracter que es un espacio
|
||||
const int POS_X = ((PlayArea::WIDTH - TEXT_LENGHT) / 2) + TEXT_LENGHT;
|
||||
text->writeColored(POS_X, 176, "ä", 5);
|
||||
Screen::get()->setRendererSurface(previuos_renderer);
|
||||
|
||||
// Recoloca el sprite del brillo
|
||||
shining_sprite_->setPosX(POS_X + 2);
|
||||
|
||||
// Crea el efecto de revelado pixel a pixel
|
||||
pixel_reveal_ = std::make_unique<PixelReveal>(Options::game.width, Options::game.height, PIXELS_PER_SECOND, STEP_DURATION, REVEAL_STEPS);
|
||||
}
|
||||
|
||||
// Actualiza las variables
|
||||
void Credits::update() {
|
||||
const float DELTA_TIME = delta_timer_->tick();
|
||||
total_time_ += DELTA_TIME; // Actualiza el tiempo total
|
||||
|
||||
handleEvents(); // Comprueba los eventos
|
||||
handleInput(); // Comprueba las entradas
|
||||
|
||||
updateState(DELTA_TIME); // Actualiza la máquina de estados
|
||||
|
||||
pixel_reveal_->update(reveal_time_); // Actualiza el efecto de revelado
|
||||
|
||||
// Actualiza el sprite con el brillo si está después del tiempo de inicio
|
||||
if (reveal_time_ > SHINE_START_TIME) {
|
||||
shining_sprite_->update(DELTA_TIME);
|
||||
}
|
||||
|
||||
Audio::update(); // Actualiza el objeto Audio
|
||||
Screen::get()->update(DELTA_TIME); // Actualiza el objeto Screen
|
||||
}
|
||||
|
||||
// Transición entre estados
|
||||
void Credits::transitionToState(State new_state) {
|
||||
state_ = new_state;
|
||||
state_time_ = 0.0F;
|
||||
}
|
||||
|
||||
// Actualiza la máquina de estados
|
||||
void Credits::updateState(float delta_time) {
|
||||
state_time_ += delta_time;
|
||||
|
||||
switch (state_) {
|
||||
case State::REVEALING_TEXT:
|
||||
reveal_time_ += delta_time; // Incrementa reveal_time durante revelación
|
||||
if (state_time_ >= REVEAL_PHASE_1_DURATION) {
|
||||
transitionToState(State::PAUSE_1);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::PAUSE_1:
|
||||
// reveal_time_ NO incrementa durante pausa (se congela)
|
||||
if (state_time_ >= PAUSE_DURATION) {
|
||||
transitionToState(State::REVEALING_TEXT_2);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::REVEALING_TEXT_2:
|
||||
reveal_time_ += delta_time; // Incrementa reveal_time durante revelación
|
||||
if (state_time_ >= REVEAL_PHASE_2_DURATION) {
|
||||
transitionToState(State::PAUSE_2);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::PAUSE_2:
|
||||
// reveal_time_ NO incrementa durante pausa (se congela)
|
||||
if (state_time_ >= PAUSE_DURATION) {
|
||||
transitionToState(State::REVEALING_TEXT_3);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::REVEALING_TEXT_3:
|
||||
reveal_time_ += delta_time; // Incrementa reveal_time durante revelación
|
||||
if (state_time_ >= REVEAL_PHASE_3_DURATION) {
|
||||
transitionToState(State::PAUSE_3);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::PAUSE_3:
|
||||
// reveal_time_ NO incrementa durante pausa (se congela)
|
||||
if (state_time_ >= PAUSE_DURATION) {
|
||||
transitionToState(State::DISPLAYING_WITH_SHINE);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::DISPLAYING_WITH_SHINE:
|
||||
reveal_time_ += delta_time; // Incrementa reveal_time durante revelación
|
||||
if (state_time_ >= DISPLAY_WITH_SHINE_DURATION) {
|
||||
transitionToState(State::FADING_OUT);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::FADING_OUT:
|
||||
reveal_time_ += delta_time; // Incrementa reveal_time durante fade
|
||||
if (state_time_ >= FADE_OUT_DURATION) {
|
||||
transitionToState(State::EXITING);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::EXITING:
|
||||
SceneManager::current = SceneManager::Scene::DEMO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Dibuja en pantalla
|
||||
void Credits::render() {
|
||||
// Prepara para empezar a dibujar en la textura de juego
|
||||
Screen::get()->start();
|
||||
|
||||
// Limpia la pantalla
|
||||
Screen::get()->clearSurface(0);
|
||||
|
||||
if (state_ != State::EXITING) {
|
||||
// Dibuja la textura con el texto en pantalla
|
||||
text_surface_->render(0, 0);
|
||||
|
||||
// Dibuja la máscara de revelado pixel a pixel
|
||||
pixel_reveal_->render(0, 0);
|
||||
|
||||
// Dibuja el sprite con el brillo
|
||||
if (reveal_time_ > SHINE_START_TIME) {
|
||||
shining_sprite_->render(1, 15);
|
||||
}
|
||||
}
|
||||
|
||||
// Vuelca el contenido del renderizador en pantalla
|
||||
Screen::get()->render();
|
||||
}
|
||||
|
||||
// Bucle para el logo del juego
|
||||
void Credits::run() {
|
||||
while (SceneManager::current == SceneManager::Scene::CREDITS) {
|
||||
update();
|
||||
render();
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <memory> // Para shared_ptr
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
class AnimatedSprite; // lines 11-11
|
||||
class Surface;
|
||||
class PixelReveal;
|
||||
class DeltaTimer;
|
||||
|
||||
class Credits {
|
||||
public:
|
||||
// --- Constructor y Destructor ---
|
||||
Credits();
|
||||
~Credits(); // NOLINT(modernize-use-equals-default, performance-trivially-destructible) -- defined in .cpp for unique_ptr with forward declarations
|
||||
|
||||
// --- Bucle principal ---
|
||||
void run();
|
||||
|
||||
private:
|
||||
// --- Tipos anidados ---
|
||||
enum class State {
|
||||
REVEALING_TEXT,
|
||||
PAUSE_1,
|
||||
REVEALING_TEXT_2,
|
||||
PAUSE_2,
|
||||
REVEALING_TEXT_3,
|
||||
PAUSE_3,
|
||||
DISPLAYING_WITH_SHINE,
|
||||
FADING_OUT,
|
||||
EXITING
|
||||
};
|
||||
|
||||
struct Captions {
|
||||
std::string label; // Texto a escribir
|
||||
Uint8 color{0}; // Color del texto
|
||||
};
|
||||
|
||||
// --- Constantes de tiempo (basado en 60 FPS) ---
|
||||
static constexpr float REVEAL_PHASE_1_DURATION = 3.733F; // 224 frames @ 60fps
|
||||
static constexpr float PAUSE_DURATION = 1.667F; // 100 frames @ 60fps
|
||||
static constexpr float REVEAL_PHASE_2_DURATION = 5.333F; // 320 frames (544-224) @ 60fps
|
||||
static constexpr float REVEAL_PHASE_3_DURATION = 2.133F; // 128 frames (672-544) @ 60fps
|
||||
static constexpr float DISPLAY_WITH_SHINE_DURATION = 7.967F; // 478 frames (1150-672) @ 60fps
|
||||
static constexpr float FADE_OUT_DURATION = 0.833F; // 50 frames (1200-1150) @ 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 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 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)
|
||||
|
||||
// --- Métodos privados ---
|
||||
void update(); // Actualiza las variables
|
||||
void render(); // Dibuja en pantalla
|
||||
static void handleEvents(); // Comprueba el manejador de eventos
|
||||
static void handleInput(); // Comprueba las entradas
|
||||
void updateState(float delta_time); // Actualiza la máquina de estados
|
||||
void transitionToState(State new_state); // Transición entre estados
|
||||
void iniTexts(); // Inicializa los textos
|
||||
void fillTexture(); // Escribe el texto en la textura
|
||||
|
||||
// --- Variables miembro ---
|
||||
// Recursos gráficos
|
||||
std::shared_ptr<Surface> text_surface_; // Textura para dibujar el texto
|
||||
std::unique_ptr<PixelReveal> pixel_reveal_; // Efecto de revelado pixel a pixel
|
||||
std::shared_ptr<AnimatedSprite> shining_sprite_; // Sprite para el brillo del corazón
|
||||
|
||||
// Temporizadores y estado
|
||||
std::unique_ptr<DeltaTimer> delta_timer_; // Temporizador delta para time-based update
|
||||
State state_{State::REVEALING_TEXT}; // Estado actual
|
||||
float state_time_{0.0F}; // Tiempo acumulado en el estado actual
|
||||
float total_time_{0.0F}; // Tiempo total acumulado
|
||||
float reveal_time_{0.0F}; // Tiempo acumulado solo durante revelación (se congela en pausas)
|
||||
|
||||
// Textos
|
||||
std::vector<Captions> texts_; // Vector con los textos
|
||||
};
|
||||
@@ -1,434 +0,0 @@
|
||||
#include "game/scenes/ending.hpp"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "core/audio/audio.hpp" // Para Audio
|
||||
#include "core/input/global_inputs.hpp" // Para check
|
||||
#include "core/input/input.hpp" // Para Input
|
||||
#include "core/locale/locale.hpp" // Para Locale
|
||||
#include "core/rendering/pixel_reveal.hpp" // Para PixelReveal
|
||||
#include "core/rendering/screen.hpp" // Para Screen
|
||||
#include "core/rendering/sprite/sprite.hpp" // Para SSprite
|
||||
#include "core/rendering/surface.hpp" // Para Surface
|
||||
#include "core/rendering/text.hpp" // Para Text, TEXT_STROKE
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||
#include "core/system/global_events.hpp" // Para check
|
||||
#include "game/options.hpp" // Para Options, options, OptionsGame, SectionS...
|
||||
#include "game/scene_manager.hpp" // Para SceneManager
|
||||
#include "utils/delta_timer.hpp" // Para DeltaTimer
|
||||
#include "utils/utils.hpp" // Para PaletteColor
|
||||
|
||||
// Destructor
|
||||
Ending::~Ending() = default;
|
||||
|
||||
// Constructor
|
||||
Ending::Ending()
|
||||
: delta_timer_(std::make_unique<DeltaTimer>()) {
|
||||
SceneManager::current = SceneManager::Scene::ENDING;
|
||||
SceneManager::options = SceneManager::Options::NONE;
|
||||
|
||||
iniTexts(); // Inicializa los textos
|
||||
iniPics(); // Inicializa las imagenes
|
||||
iniScenes(); // Inicializa las escenas
|
||||
|
||||
Screen::get()->setBorderColor(0); // Cambia el color del borde
|
||||
}
|
||||
|
||||
// Actualiza el objeto
|
||||
void Ending::update() {
|
||||
const float DELTA_TIME = delta_timer_->tick();
|
||||
total_time_ += DELTA_TIME; // Actualiza el tiempo total
|
||||
|
||||
handleEvents(); // Comprueba los eventos
|
||||
handleInput(); // Comprueba las entradas
|
||||
|
||||
updateState(DELTA_TIME); // Actualiza la máquina de estados
|
||||
updateSpriteCovers(); // Actualiza las cortinillas de los elementos
|
||||
|
||||
Audio::update(); // Actualiza el objeto Audio
|
||||
Screen::get()->update(DELTA_TIME); // Actualiza el objeto Screen
|
||||
}
|
||||
|
||||
// Dibuja el final en pantalla
|
||||
void Ending::render() {
|
||||
// Prepara para empezar a dibujar en la textura de juego
|
||||
Screen::get()->start();
|
||||
|
||||
// Limpia la pantalla
|
||||
Screen::get()->clearSurface(0);
|
||||
|
||||
// Skip rendering durante WARMING_UP
|
||||
if (state_ != State::WARMING_UP) {
|
||||
// Dibuja las imagenes de la escena
|
||||
const auto& pic = sprite_pics_.at(current_scene_);
|
||||
pic.image_sprite->render();
|
||||
pic.pixel_reveal->render(pic.pos_x, pic.pos_y);
|
||||
|
||||
// Dibuja los textos de la escena
|
||||
for (const auto& ti : scenes_.at(current_scene_).text_index) {
|
||||
// Convertir trigger de frames a segundos @ 60fps
|
||||
const float TRIGGER_TIME = static_cast<float>(ti.trigger) / 60.0F;
|
||||
|
||||
if (state_time_ > TRIGGER_TIME) {
|
||||
const auto& txt = sprite_texts_.at(ti.index);
|
||||
txt.image_sprite->render();
|
||||
txt.pixel_reveal->render(txt.pos_x, txt.pos_y);
|
||||
}
|
||||
}
|
||||
|
||||
// Dibuja la cortinilla de cambio de escena
|
||||
if (scene_cover_) {
|
||||
scene_cover_->render(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Vuelca el contenido del renderizador en pantalla
|
||||
Screen::get()->render();
|
||||
}
|
||||
|
||||
// Comprueba el manejador de eventos
|
||||
void Ending::handleEvents() {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
GlobalEvents::handle(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba las entradas
|
||||
void Ending::handleInput() {
|
||||
Input::get()->update();
|
||||
GlobalInputs::handle();
|
||||
}
|
||||
|
||||
// Transición entre estados
|
||||
void Ending::transitionToState(State new_state) {
|
||||
state_ = new_state;
|
||||
state_time_ = 0.0F;
|
||||
|
||||
// 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) {
|
||||
fadeout_time_ = 0.0F;
|
||||
scene_cover_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Lógica de fade común a los estados SCENE_N
|
||||
void Ending::handleSceneFadeout(float scene_duration, float delta_time) {
|
||||
if (state_time_ >= scene_duration - FADEOUT_START_OFFSET) {
|
||||
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_);
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza la máquina de estados
|
||||
void Ending::updateState(float delta_time) {
|
||||
state_time_ += delta_time;
|
||||
|
||||
switch (state_) {
|
||||
case State::WARMING_UP:
|
||||
if (state_time_ >= WARMUP_DURATION) {
|
||||
transitionToState(State::SCENE_0);
|
||||
current_scene_ = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::SCENE_0:
|
||||
checkChangeScene();
|
||||
handleSceneFadeout(SCENE_0_DURATION, delta_time);
|
||||
break;
|
||||
|
||||
case State::SCENE_1:
|
||||
checkChangeScene();
|
||||
handleSceneFadeout(SCENE_1_DURATION, delta_time);
|
||||
break;
|
||||
|
||||
case State::SCENE_2:
|
||||
checkChangeScene();
|
||||
handleSceneFadeout(SCENE_2_DURATION, delta_time);
|
||||
break;
|
||||
|
||||
case State::SCENE_3:
|
||||
checkChangeScene();
|
||||
handleSceneFadeout(SCENE_3_DURATION, delta_time);
|
||||
break;
|
||||
|
||||
case State::SCENE_4:
|
||||
checkChangeScene();
|
||||
handleSceneFadeout(SCENE_4_DURATION, delta_time);
|
||||
break;
|
||||
|
||||
case State::ENDING:
|
||||
// Esperar ENDING_DURATION y luego transicionar a ENDING2
|
||||
if (state_time_ >= ENDING_DURATION) {
|
||||
SceneManager::current = SceneManager::Scene::ENDING2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Inicializa los textos
|
||||
void Ending::iniTexts() { // NOLINT(readability-convert-member-functions-to-static)
|
||||
// Vector con los textos (traducidos según el idioma activo)
|
||||
std::vector<TextAndPosition> texts;
|
||||
auto* loc = Locale::get();
|
||||
|
||||
// Escena #0
|
||||
texts.push_back({.caption = loc->get("ending.t0"), .pos = 32});
|
||||
texts.push_back({.caption = loc->get("ending.t1"), .pos = 42});
|
||||
texts.push_back({.caption = loc->get("ending.t2"), .pos = 142});
|
||||
texts.push_back({.caption = loc->get("ending.t3"), .pos = 152});
|
||||
|
||||
// Escena #1
|
||||
texts.push_back({.caption = loc->get("ending.t4"), .pos = 1});
|
||||
texts.push_back({.caption = loc->get("ending.t5"), .pos = 11});
|
||||
texts.push_back({.caption = loc->get("ending.t6"), .pos = 21});
|
||||
|
||||
texts.push_back({.caption = loc->get("ending.t7"), .pos = 161});
|
||||
texts.push_back({.caption = loc->get("ending.t8"), .pos = 171});
|
||||
|
||||
texts.push_back({.caption = loc->get("ending.t9"), .pos = 181});
|
||||
|
||||
// Escena #2
|
||||
texts.push_back({.caption = loc->get("ending.t10"), .pos = 19});
|
||||
texts.push_back({.caption = loc->get("ending.t11"), .pos = 29});
|
||||
|
||||
// Escena #3
|
||||
texts.push_back({.caption = loc->get("ending.t12"), .pos = 36});
|
||||
texts.push_back({.caption = loc->get("ending.t13"), .pos = 46});
|
||||
|
||||
// Escena #4
|
||||
texts.push_back({.caption = loc->get("ending.t14"), .pos = 36});
|
||||
texts.push_back({.caption = loc->get("ending.t15"), .pos = 46});
|
||||
texts.push_back({.caption = loc->get("ending.t16"), .pos = 158});
|
||||
|
||||
// Crea los sprites
|
||||
sprite_texts_.clear();
|
||||
|
||||
for (const auto& txt : texts) {
|
||||
auto text = Resource::Cache::get()->getText("smb2");
|
||||
|
||||
const float WIDTH = text->length(txt.caption, 1) + 2 + 2;
|
||||
const float HEIGHT = text->getCharacterSize() + 2 + 2;
|
||||
auto text_color = 14;
|
||||
auto shadow_color = 0;
|
||||
|
||||
EndingSurface st;
|
||||
|
||||
// Crea la textura
|
||||
st.image_surface = std::make_shared<Surface>(WIDTH, HEIGHT);
|
||||
auto previuos_renderer = Screen::get()->getRendererSurface();
|
||||
Screen::get()->setRendererSurface(st.image_surface);
|
||||
text->writeDX(Text::STROKE_FLAG, 2, 2, txt.caption, 1, text_color, 2, shadow_color);
|
||||
|
||||
// Crea el sprite
|
||||
st.image_sprite = std::make_shared<Sprite>(st.image_surface, 0, 0, st.image_surface->getWidth(), st.image_surface->getHeight());
|
||||
st.pos_x = static_cast<int>((Options::game.width - st.image_surface->getWidth()) / 2);
|
||||
st.pos_y = txt.pos;
|
||||
st.image_sprite->setPosition(st.pos_x, st.pos_y);
|
||||
|
||||
// Crea el efecto de revelado pixel a pixel
|
||||
st.pixel_reveal = std::make_unique<PixelReveal>(static_cast<int>(WIDTH), static_cast<int>(HEIGHT), TEXT_PIXELS_PER_SECOND, STEP_DURATION, REVEAL_STEPS);
|
||||
|
||||
sprite_texts_.push_back(std::move(st));
|
||||
Screen::get()->setRendererSurface(previuos_renderer);
|
||||
}
|
||||
}
|
||||
|
||||
// Inicializa las imagenes
|
||||
void Ending::iniPics() {
|
||||
// Vector con las rutas y la posición
|
||||
std::vector<TextAndPosition> pics;
|
||||
|
||||
pics.push_back({.caption = "ending1.gif", .pos = 48});
|
||||
pics.push_back({.caption = "ending2.gif", .pos = 26});
|
||||
pics.push_back({.caption = "ending3.gif", .pos = 29});
|
||||
pics.push_back({.caption = "ending4.gif", .pos = 63});
|
||||
pics.push_back({.caption = "ending5.gif", .pos = 53});
|
||||
|
||||
// Crea los sprites
|
||||
sprite_pics_.clear();
|
||||
|
||||
for (const auto& pic : pics) {
|
||||
EndingSurface sp;
|
||||
|
||||
// Crea la texture
|
||||
sp.image_surface = Resource::Cache::get()->getSurface(pic.caption);
|
||||
sp.image_surface->setTransparentColor();
|
||||
const float WIDTH = sp.image_surface->getWidth();
|
||||
const float HEIGHT = sp.image_surface->getHeight();
|
||||
|
||||
// Crea el sprite
|
||||
sp.pos_x = static_cast<int>((Options::game.width - WIDTH) / 2);
|
||||
sp.pos_y = pic.pos;
|
||||
sp.image_sprite = std::make_shared<Sprite>(sp.image_surface, 0, 0, WIDTH, HEIGHT);
|
||||
sp.image_sprite->setPosition(sp.pos_x, sp.pos_y);
|
||||
|
||||
// Crea el efecto de revelado pixel a pixel
|
||||
sp.pixel_reveal = std::make_unique<PixelReveal>(static_cast<int>(WIDTH), static_cast<int>(HEIGHT), IMAGE_PIXELS_PER_SECOND, STEP_DURATION, REVEAL_STEPS);
|
||||
|
||||
sprite_pics_.push_back(std::move(sp));
|
||||
}
|
||||
}
|
||||
|
||||
// Inicializa las escenas
|
||||
void Ending::iniScenes() { // NOLINT(readability-convert-member-functions-to-static)
|
||||
// Variable para los tiempos
|
||||
int trigger;
|
||||
constexpr int LAPSE = 80;
|
||||
|
||||
// Crea el contenedor
|
||||
SceneData sc;
|
||||
|
||||
// Inicializa el vector
|
||||
scenes_.clear();
|
||||
|
||||
// Crea la escena #0
|
||||
sc.counter_end = 1000;
|
||||
sc.picture_index = 0;
|
||||
sc.text_index.clear();
|
||||
trigger = 85 * 2;
|
||||
trigger += LAPSE;
|
||||
sc.text_index.push_back({.index = 0, .trigger = trigger});
|
||||
trigger += LAPSE;
|
||||
sc.text_index.push_back({.index = 1, .trigger = trigger});
|
||||
trigger += LAPSE * 3;
|
||||
sc.text_index.push_back({.index = 2, .trigger = trigger});
|
||||
trigger += LAPSE;
|
||||
sc.text_index.push_back({.index = 3, .trigger = trigger});
|
||||
scenes_.push_back(sc);
|
||||
|
||||
// Crea la escena #1
|
||||
sc.counter_end = 1400;
|
||||
sc.picture_index = 1;
|
||||
sc.text_index.clear();
|
||||
trigger = 140 * 2;
|
||||
trigger += LAPSE;
|
||||
sc.text_index.push_back({.index = 4, .trigger = trigger});
|
||||
trigger += LAPSE;
|
||||
sc.text_index.push_back({.index = 5, .trigger = trigger});
|
||||
trigger += LAPSE;
|
||||
sc.text_index.push_back({.index = 6, .trigger = trigger});
|
||||
trigger += LAPSE * 3;
|
||||
sc.text_index.push_back({.index = 7, .trigger = trigger});
|
||||
trigger += LAPSE;
|
||||
sc.text_index.push_back({.index = 8, .trigger = trigger});
|
||||
trigger += LAPSE * 3;
|
||||
sc.text_index.push_back({.index = 9, .trigger = trigger});
|
||||
scenes_.push_back(sc);
|
||||
|
||||
// Crea la escena #2
|
||||
sc.counter_end = 1000;
|
||||
sc.picture_index = 2;
|
||||
sc.text_index.clear();
|
||||
trigger = 148 / 2;
|
||||
trigger += LAPSE;
|
||||
sc.text_index.push_back({.index = 10, .trigger = trigger});
|
||||
trigger += LAPSE;
|
||||
sc.text_index.push_back({.index = 11, .trigger = trigger});
|
||||
scenes_.push_back(sc);
|
||||
|
||||
// Crea la escena #3
|
||||
sc.counter_end = 800;
|
||||
sc.picture_index = 3;
|
||||
sc.text_index.clear();
|
||||
trigger = 87 / 2;
|
||||
trigger += LAPSE;
|
||||
sc.text_index.push_back({.index = 12, .trigger = trigger});
|
||||
trigger += LAPSE / 2;
|
||||
sc.text_index.push_back({.index = 13, .trigger = trigger});
|
||||
scenes_.push_back(sc);
|
||||
|
||||
// Crea la escena #4
|
||||
sc.counter_end = 1000;
|
||||
sc.picture_index = 4;
|
||||
sc.text_index.clear();
|
||||
trigger = 91 * 2;
|
||||
trigger += LAPSE;
|
||||
sc.text_index.push_back({.index = 14, .trigger = trigger});
|
||||
trigger += LAPSE * 2;
|
||||
sc.text_index.push_back({.index = 15, .trigger = trigger});
|
||||
trigger += LAPSE * 3;
|
||||
sc.text_index.push_back({.index = 16, .trigger = trigger});
|
||||
scenes_.push_back(sc);
|
||||
}
|
||||
|
||||
// Bucle principal
|
||||
void Ending::run() {
|
||||
Audio::get()->playMusic("574070_KUVO_Farewell_to_school.ogg");
|
||||
|
||||
while (SceneManager::current == SceneManager::Scene::ENDING) {
|
||||
update();
|
||||
render();
|
||||
}
|
||||
|
||||
Audio::get()->stopMusic();
|
||||
}
|
||||
|
||||
// Actualiza las cortinillas de los elementos
|
||||
void Ending::updateSpriteCovers() {
|
||||
// Skip durante WARMING_UP
|
||||
if (state_ == State::WARMING_UP) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Actualiza el revelado de los textos
|
||||
for (const auto& ti : scenes_.at(current_scene_).text_index) {
|
||||
const float TRIGGER_TIME = static_cast<float>(ti.trigger) / 60.0F;
|
||||
|
||||
if (state_time_ > TRIGGER_TIME) {
|
||||
const float TIME_SINCE_TRIGGER = state_time_ - TRIGGER_TIME;
|
||||
sprite_texts_.at(ti.index).pixel_reveal->update(TIME_SINCE_TRIGGER);
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza el revelado de la imagen (desde el inicio de la escena)
|
||||
sprite_pics_.at(current_scene_).pixel_reveal->update(state_time_);
|
||||
}
|
||||
|
||||
// Comprueba si se ha de cambiar de escena
|
||||
void Ending::checkChangeScene() {
|
||||
// Obtener duración de la escena actual
|
||||
float current_duration = 0.0F;
|
||||
State next_state = State::ENDING;
|
||||
|
||||
switch (state_) {
|
||||
case State::SCENE_0:
|
||||
current_duration = SCENE_0_DURATION;
|
||||
next_state = State::SCENE_1;
|
||||
break;
|
||||
case State::SCENE_1:
|
||||
current_duration = SCENE_1_DURATION;
|
||||
next_state = State::SCENE_2;
|
||||
break;
|
||||
case State::SCENE_2:
|
||||
current_duration = SCENE_2_DURATION;
|
||||
next_state = State::SCENE_3;
|
||||
break;
|
||||
case State::SCENE_3:
|
||||
current_duration = SCENE_3_DURATION;
|
||||
next_state = State::SCENE_4;
|
||||
break;
|
||||
case State::SCENE_4:
|
||||
current_duration = SCENE_4_DURATION;
|
||||
next_state = State::ENDING;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// Comprobar si ha pasado la duración de la escena
|
||||
if (state_time_ >= current_duration) {
|
||||
if (next_state == State::ENDING) {
|
||||
// Transición al estado ENDING con fade de audio
|
||||
transitionToState(State::ENDING);
|
||||
Audio::get()->fadeOutMusic(MUSIC_FADE_DURATION);
|
||||
} else {
|
||||
// Transición a la siguiente escena
|
||||
current_scene_++;
|
||||
transitionToState(next_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <memory> // Para shared_ptr
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
class Sprite; // lines 8-8
|
||||
class Surface; // lines 9-9
|
||||
class PixelReveal;
|
||||
class DeltaTimer;
|
||||
|
||||
class Ending {
|
||||
public:
|
||||
// --- Constructor y Destructor ---
|
||||
Ending();
|
||||
~Ending(); // NOLINT(modernize-use-equals-default, performance-trivially-destructible) -- defined in .cpp for unique_ptr with forward declarations
|
||||
|
||||
// --- Bucle principal ---
|
||||
void run();
|
||||
|
||||
private:
|
||||
// --- Enumeraciones ---
|
||||
enum class State {
|
||||
WARMING_UP,
|
||||
SCENE_0,
|
||||
SCENE_1,
|
||||
SCENE_2,
|
||||
SCENE_3,
|
||||
SCENE_4,
|
||||
ENDING
|
||||
};
|
||||
|
||||
// --- Estructuras ---
|
||||
struct EndingSurface {
|
||||
std::shared_ptr<Surface> image_surface; // Surface a mostrar
|
||||
std::shared_ptr<Sprite> image_sprite; // SSprite para mostrar la textura
|
||||
std::unique_ptr<PixelReveal> pixel_reveal; // Efecto de revelado pixel a pixel
|
||||
int pos_x{0}; // Posición X de renderizado
|
||||
int pos_y{0}; // Posición Y de renderizado
|
||||
};
|
||||
|
||||
struct TextAndPosition {
|
||||
std::string caption; // Texto
|
||||
int pos{0}; // Posición
|
||||
};
|
||||
|
||||
struct TextIndex {
|
||||
int index{0}; // Índice del texto
|
||||
int trigger{0}; // Disparador temporal
|
||||
};
|
||||
|
||||
struct SceneData {
|
||||
std::vector<TextIndex> text_index; // Índices del vector de textos a mostrar y su disparador
|
||||
int picture_index{0}; // Índice del vector de imágenes a mostrar
|
||||
int counter_end{0}; // Valor del contador en el que finaliza la escena
|
||||
};
|
||||
|
||||
// --- Constantes de tiempo (basado en 60 FPS) ---
|
||||
static constexpr float WARMUP_DURATION = 3.333F; // 200 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_2_DURATION = 16.667F; // 1000 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 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 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 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 COVER_PIXELS_PER_SECOND = 120.0F; // Filas cubiertas por segundo
|
||||
static constexpr int COVER_STEPS = 4; // Pasos por fila
|
||||
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 ---
|
||||
void update(); // Actualiza el objeto
|
||||
void render(); // Dibuja el final en pantalla
|
||||
static void handleEvents(); // Comprueba el manejador de eventos
|
||||
static void handleInput(); // Comprueba las entradas
|
||||
void iniTexts(); // Inicializa los textos
|
||||
void iniPics(); // Inicializa las imágenes
|
||||
void iniScenes(); // Inicializa las escenas
|
||||
void updateState(float delta_time); // Actualiza la máquina de estados
|
||||
void handleSceneFadeout(float scene_duration, float delta_time); // Lógica de fade común a los estados SCENE_N
|
||||
void transitionToState(State new_state); // Transición entre estados
|
||||
void updateSpriteCovers(); // Actualiza las cortinillas de los elementos
|
||||
void checkChangeScene(); // Comprueba si se ha de cambiar de escena
|
||||
void updateMusicVolume() const; // Actualiza el volumen de la música
|
||||
|
||||
// --- Variables miembro ---
|
||||
// Objetos y punteros a recursos
|
||||
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::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<SceneData> scenes_; // Vector con los textos e imágenes de cada escena
|
||||
|
||||
// Variables de estado
|
||||
State state_{State::WARMING_UP}; // 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 fadeout_time_{0.0F}; // Tiempo acumulado para la cortinilla de salida
|
||||
int current_scene_{0}; // Escena actual (0-4)
|
||||
};
|
||||
@@ -1,502 +0,0 @@
|
||||
#include "game/scenes/ending2.hpp"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <algorithm> // Para max, replace
|
||||
|
||||
#include "core/audio/audio.hpp" // Para Audio
|
||||
#include "core/input/global_inputs.hpp" // Para check
|
||||
#include "core/input/input.hpp" // Para Input
|
||||
#include "core/locale/locale.hpp" // Para Locale
|
||||
#include "core/rendering/screen.hpp" // Para Screen
|
||||
#include "core/rendering/sprite/dissolve_sprite.hpp" // Para SurfaceDissolveSprite
|
||||
#include "core/rendering/sprite/moving_sprite.hpp" // Para SMovingSprite
|
||||
#include "core/rendering/surface.hpp" // Para Surface
|
||||
#include "core/rendering/text.hpp" // Para Text
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||
#include "core/system/global_events.hpp" // Para check
|
||||
#include "game/options.hpp" // Para Options, options, OptionsGame, Sectio...
|
||||
#include "game/scene_manager.hpp" // Para SceneManager
|
||||
#include "utils/defines.hpp" // Para GameCanvas::CENTER_X, GameCanvas::CENTER_Y
|
||||
#include "utils/delta_timer.hpp" // Para DeltaTimer
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
// Constructor
|
||||
Ending2::Ending2()
|
||||
: delta_timer_(std::make_unique<DeltaTimer>()),
|
||||
state_{.state = EndingState::PRE_CREDITS, .duration = STATE_PRE_CREDITS_DURATION} {
|
||||
// Establece la escena
|
||||
SceneManager::current = SceneManager::Scene::ENDING2;
|
||||
SceneManager::options = SceneManager::Options::NONE;
|
||||
|
||||
// Inicializa el vector de colores
|
||||
colors_ = {14, 12, 10, 8, 6, 4, 2, 0};
|
||||
|
||||
Screen::get()->setBorderColor(0); // Cambia el color del borde
|
||||
iniSpriteList(); // Inicializa la lista de sprites
|
||||
loadSprites(); // Carga todos los sprites desde una lista
|
||||
placeSprites(); // Coloca los sprites en su sito
|
||||
createSpriteTexts(); // Crea los sprites con las texturas con los textos
|
||||
createTexts(); // Crea los sprites con las texturas con los textos del final
|
||||
}
|
||||
|
||||
// Actualiza el objeto
|
||||
void Ending2::update() {
|
||||
const float DELTA_TIME = delta_timer_->tick();
|
||||
|
||||
handleEvents(); // Comprueba los eventos
|
||||
handleInput(); // Comprueba las entradas
|
||||
|
||||
updateState(DELTA_TIME); // Actualiza el estado
|
||||
|
||||
switch (state_.state) {
|
||||
case EndingState::CREDITS:
|
||||
// Actualiza los sprites, los textos y los textos del final
|
||||
updateSprites(DELTA_TIME);
|
||||
updateTextSprites(DELTA_TIME);
|
||||
updateTexts(DELTA_TIME);
|
||||
break;
|
||||
|
||||
case EndingState::FADING:
|
||||
// Actualiza el fade final
|
||||
updateFinalFade();
|
||||
break;
|
||||
|
||||
default:
|
||||
// No hacer nada si el estado no corresponde a un caso manejado
|
||||
break;
|
||||
}
|
||||
|
||||
Audio::update(); // Actualiza el objeto Audio
|
||||
Screen::get()->update(DELTA_TIME); // Actualiza el objeto Screen
|
||||
}
|
||||
|
||||
// Dibuja el final en pantalla
|
||||
void Ending2::render() {
|
||||
// Prepara para empezar a dibujar en la surface de juego
|
||||
Screen::get()->start();
|
||||
|
||||
// Limpia la pantalla
|
||||
Screen::get()->clearSurface(0);
|
||||
|
||||
// Dibuja los sprites
|
||||
renderSprites();
|
||||
|
||||
// Dibuja los sprites con el texto
|
||||
renderSpriteTexts();
|
||||
|
||||
// Dibuja los sprites con el texto del final
|
||||
renderTexts();
|
||||
|
||||
// Vuelca el contenido del renderizador en pantalla
|
||||
Screen::get()->render();
|
||||
}
|
||||
|
||||
// Comprueba el manejador de eventos
|
||||
void Ending2::handleEvents() {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
GlobalEvents::handle(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba las entradas
|
||||
void Ending2::handleInput() {
|
||||
Input::get()->update();
|
||||
GlobalInputs::handle();
|
||||
}
|
||||
|
||||
// Bucle principal
|
||||
void Ending2::run() {
|
||||
Audio::get()->playMusic("574071_EA_DTV.ogg");
|
||||
|
||||
while (SceneManager::current == SceneManager::Scene::ENDING2) {
|
||||
update();
|
||||
render();
|
||||
}
|
||||
|
||||
Audio::get()->stopMusic();
|
||||
}
|
||||
|
||||
// Actualiza el estado
|
||||
void Ending2::updateState(float delta_time) {
|
||||
state_time_ += delta_time;
|
||||
|
||||
switch (state_.state) {
|
||||
case EndingState::PRE_CREDITS:
|
||||
if (state_time_ >= STATE_PRE_CREDITS_DURATION) {
|
||||
transitionToState(EndingState::CREDITS);
|
||||
}
|
||||
break;
|
||||
|
||||
case EndingState::CREDITS:
|
||||
if (texts_.back()->getPosY() <= GameCanvas::CENTER_Y) {
|
||||
transitionToState(EndingState::POST_CREDITS);
|
||||
}
|
||||
break;
|
||||
|
||||
case EndingState::POST_CREDITS:
|
||||
if (state_time_ >= STATE_POST_CREDITS_DURATION) {
|
||||
transitionToState(EndingState::FADING);
|
||||
}
|
||||
break;
|
||||
|
||||
case EndingState::FADING:
|
||||
if (state_time_ >= STATE_FADE_DURATION) {
|
||||
SceneManager::current = SceneManager::Scene::LOGO;
|
||||
SceneManager::options = SceneManager::Options::LOGO_TO_TITLE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Transición entre estados
|
||||
void Ending2::transitionToState(EndingState new_state) {
|
||||
state_.state = new_state;
|
||||
state_time_ = 0.0F;
|
||||
|
||||
// Actualizar duración según el nuevo estado
|
||||
switch (new_state) {
|
||||
case EndingState::PRE_CREDITS:
|
||||
state_.duration = STATE_PRE_CREDITS_DURATION;
|
||||
break;
|
||||
case EndingState::POST_CREDITS:
|
||||
state_.duration = STATE_POST_CREDITS_DURATION;
|
||||
break;
|
||||
case EndingState::FADING:
|
||||
state_.duration = STATE_FADE_DURATION;
|
||||
// Al entrar en FADING, iniciar fade de audio
|
||||
Audio::get()->fadeOutMusic(MUSIC_FADE_DURATION);
|
||||
break;
|
||||
case EndingState::CREDITS:
|
||||
state_.duration = 0.0F; // CREDITS no tiene duración fija, termina cuando el último texto llega al centro
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Inicializa la lista de sprites
|
||||
void Ending2::iniSpriteList() {
|
||||
// Reinicia el vector
|
||||
sprite_list_.clear();
|
||||
|
||||
// Añade los valores
|
||||
sprite_list_.emplace_back("bin");
|
||||
sprite_list_.emplace_back("floppy");
|
||||
sprite_list_.emplace_back("bird");
|
||||
sprite_list_.emplace_back("chip");
|
||||
sprite_list_.emplace_back("jeannine");
|
||||
sprite_list_.emplace_back("spark");
|
||||
sprite_list_.emplace_back("code");
|
||||
sprite_list_.emplace_back("paco");
|
||||
sprite_list_.emplace_back("elsa");
|
||||
sprite_list_.emplace_back("z80");
|
||||
|
||||
sprite_list_.emplace_back("bell");
|
||||
sprite_list_.emplace_back("dong");
|
||||
|
||||
sprite_list_.emplace_back("amstrad_cs");
|
||||
sprite_list_.emplace_back("breakout");
|
||||
|
||||
sprite_list_.emplace_back("flying_arounder");
|
||||
sprite_list_.emplace_back("stopped_arounder");
|
||||
sprite_list_.emplace_back("walking_arounder");
|
||||
sprite_list_.emplace_back("arounders_door");
|
||||
sprite_list_.emplace_back("arounders_machine");
|
||||
|
||||
sprite_list_.emplace_back("abad");
|
||||
sprite_list_.emplace_back("abad_bell");
|
||||
sprite_list_.emplace_back("lord_abad");
|
||||
|
||||
sprite_list_.emplace_back("bat");
|
||||
sprite_list_.emplace_back("batman_bell");
|
||||
sprite_list_.emplace_back("batman_fire");
|
||||
sprite_list_.emplace_back("batman");
|
||||
|
||||
sprite_list_.emplace_back("demon");
|
||||
sprite_list_.emplace_back("heavy");
|
||||
sprite_list_.emplace_back("dimallas");
|
||||
sprite_list_.emplace_back("guitar");
|
||||
|
||||
sprite_list_.emplace_back("jailbattle_alien");
|
||||
sprite_list_.emplace_back("jailbattle_human");
|
||||
|
||||
sprite_list_.emplace_back("jailer1");
|
||||
sprite_list_.emplace_back("jailer2");
|
||||
sprite_list_.emplace_back("jailer3");
|
||||
sprite_list_.emplace_back("bry");
|
||||
sprite_list_.emplace_back("upv_student");
|
||||
|
||||
sprite_list_.emplace_back("lamp");
|
||||
sprite_list_.emplace_back("robot");
|
||||
sprite_list_.emplace_back("congo");
|
||||
sprite_list_.emplace_back("crosshair");
|
||||
sprite_list_.emplace_back("tree_thing");
|
||||
|
||||
sprite_list_.emplace_back("matatunos");
|
||||
sprite_list_.emplace_back("tuno");
|
||||
|
||||
sprite_list_.emplace_back("mummy");
|
||||
sprite_list_.emplace_back("sam");
|
||||
|
||||
sprite_list_.emplace_back("qvoid");
|
||||
sprite_list_.emplace_back("sigmasua");
|
||||
|
||||
sprite_list_.emplace_back("tv_panel");
|
||||
sprite_list_.emplace_back("tv");
|
||||
|
||||
sprite_list_.emplace_back("spider");
|
||||
sprite_list_.emplace_back("shock");
|
||||
sprite_list_.emplace_back("wave");
|
||||
|
||||
sprite_list_.emplace_back("player");
|
||||
}
|
||||
|
||||
// Carga todos los sprites desde una lista
|
||||
void Ending2::loadSprites() {
|
||||
// Inicializa variables
|
||||
sprite_max_width_ = 0;
|
||||
sprite_max_height_ = 0;
|
||||
|
||||
// Carga los sprites
|
||||
for (const auto& file : sprite_list_) {
|
||||
const auto& animation_data = Resource::Cache::get()->getAnimationData(file + ".yaml");
|
||||
sprites_.emplace_back(std::make_shared<DissolveSprite>(animation_data));
|
||||
sprites_.back()->setColorReplace(1, 4);
|
||||
sprites_.back()->setProgress(1.0F); // comença invisible
|
||||
sprite_max_width_ = std::max(sprites_.back()->getWidth(), sprite_max_width_);
|
||||
sprite_max_height_ = std::max(sprites_.back()->getHeight(), sprite_max_height_);
|
||||
}
|
||||
|
||||
// El último sprite (player) va en blanco, no en rojo
|
||||
sprites_.back()->setColorReplace(1, 14);
|
||||
}
|
||||
|
||||
// Actualiza los sprites
|
||||
void Ending2::updateSprites(float delta) {
|
||||
for (const auto& sprite : sprites_) {
|
||||
sprite->update(delta);
|
||||
|
||||
const float Y = sprite->getPosY();
|
||||
const float H = sprite->getHeight();
|
||||
const auto CANVAS_H = Options::game.height;
|
||||
|
||||
// Checkpoint inferior: sprite entra per baix → generar de dalt a baix
|
||||
if (Y > static_cast<float>(ENTRY_EXIT_PADDING) && Y <= CANVAS_H - H - ENTRY_EXIT_PADDING && sprite->getProgress() >= 1.0F && sprite->isTransitionDone()) {
|
||||
sprite->startGenerate(TRANSITION_DURATION_MS, DissolveDirection::UP);
|
||||
}
|
||||
|
||||
// Checkpoint superior: sprite surt per dalt → dissoldre de dalt a baix
|
||||
if (Y <= static_cast<float>(ENTRY_EXIT_PADDING) && sprite->getProgress() <= 0.0F && sprite->isTransitionDone()) {
|
||||
sprite->startDissolve(TRANSITION_DURATION_MS, DissolveDirection::DOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza los sprites de texto
|
||||
void Ending2::updateTextSprites(float delta) {
|
||||
for (const auto& sprite : sprite_texts_) {
|
||||
sprite->update(delta);
|
||||
|
||||
const float Y = sprite->getPosY();
|
||||
const float H = sprite->getHeight();
|
||||
const auto CANVAS_H = Options::game.height;
|
||||
|
||||
if (Y > static_cast<float>(ENTRY_EXIT_PADDING) && Y <= CANVAS_H - H - ENTRY_EXIT_PADDING && sprite->getProgress() >= 1.0F && sprite->isTransitionDone()) {
|
||||
sprite->startGenerate(TRANSITION_DURATION_MS, DissolveDirection::UP);
|
||||
}
|
||||
|
||||
if (Y <= static_cast<float>(ENTRY_EXIT_PADDING) && sprite->getProgress() <= 0.0F && sprite->isTransitionDone()) {
|
||||
sprite->startDissolve(TRANSITION_DURATION_MS, DissolveDirection::DOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza los sprites de texto del final
|
||||
void Ending2::updateTexts(float delta) {
|
||||
for (const auto& sprite : texts_) {
|
||||
sprite->update(delta);
|
||||
|
||||
const float Y = sprite->getPosY();
|
||||
const float H = sprite->getHeight();
|
||||
const auto CANVAS_H = Options::game.height;
|
||||
|
||||
// Checkpoint inferior: text entra per baix → generar de dalt a baix
|
||||
if (Y > static_cast<float>(ENTRY_EXIT_PADDING) && Y <= CANVAS_H - H - ENTRY_EXIT_PADDING && sprite->getProgress() >= 1.0F && sprite->isTransitionDone()) {
|
||||
sprite->startGenerate(TRANSITION_DURATION_MS, DissolveDirection::UP);
|
||||
}
|
||||
|
||||
// Checkpoint superior: text surt per dalt → dissoldre de dalt a baix
|
||||
if (Y <= static_cast<float>(ENTRY_EXIT_PADDING) && sprite->getProgress() <= 0.0F && sprite->isTransitionDone()) {
|
||||
sprite->startDissolve(TRANSITION_DURATION_MS, DissolveDirection::DOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dibuja los sprites
|
||||
void Ending2::renderSprites() {
|
||||
for (const auto& sprite : sprites_) {
|
||||
const bool A = sprite->getRect().y + sprite->getRect().h > 0;
|
||||
const bool B = sprite->getRect().y < Options::game.height;
|
||||
if (A && B) {
|
||||
sprite->render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dibuja los sprites con el texto
|
||||
void Ending2::renderSpriteTexts() {
|
||||
for (const auto& sprite : sprite_texts_) {
|
||||
const bool A = sprite->getRect().y + sprite->getRect().h > 0;
|
||||
const bool B = sprite->getRect().y < Options::game.height;
|
||||
if (A && B) {
|
||||
sprite->render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dibuja los sprites con el texto del final
|
||||
void Ending2::renderTexts() {
|
||||
for (const auto& sprite : texts_) {
|
||||
const bool A = sprite->getRect().y + sprite->getRect().h > 0;
|
||||
const bool B = sprite->getRect().y < Options::game.height;
|
||||
if (A && B) {
|
||||
sprite->render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Coloca los sprites en su sito
|
||||
void Ending2::placeSprites() const {
|
||||
for (int i = 0; i < static_cast<int>(sprites_.size()); ++i) {
|
||||
const float X = i % 2 == 0 ? FIRST_COL : SECOND_COL;
|
||||
const float Y = ((i / 1) * (sprite_max_height_ + DIST_SPRITE_TEXT + Resource::Cache::get()->getText("smb2")->getCharacterSize() + DIST_SPRITE_SPRITE)) + Options::game.height + INITIAL_Y_OFFSET;
|
||||
const float W = sprites_.at(i)->getWidth();
|
||||
const float H = sprites_.at(i)->getHeight();
|
||||
const float DX = -(W / 2);
|
||||
const float DY = sprite_max_height_ - H;
|
||||
|
||||
sprites_.at(i)->setPos({.x = X + DX, .y = Y + DY, .w = W, .h = H});
|
||||
sprites_.at(i)->setVelY(SPRITE_DESP_SPEED);
|
||||
}
|
||||
|
||||
// Recoloca el sprite del jugador, que es el último de la lista
|
||||
const float X = (Options::game.width - sprites_.back()->getWidth()) / 2;
|
||||
const float Y = sprites_.back()->getPosY() + (sprite_max_height_ * 2);
|
||||
sprites_.back()->setPos(X, Y);
|
||||
sprites_.back()->setCurrentAnimation("default");
|
||||
}
|
||||
|
||||
// Crea los sprites con las texturas con los textos
|
||||
void Ending2::createSpriteTexts() { // NOLINT(readability-convert-member-functions-to-static)
|
||||
// Crea los sprites de texto a partir de la lista
|
||||
for (size_t i = 0; i < sprite_list_.size(); ++i) {
|
||||
auto text = Resource::Cache::get()->getText("smb2");
|
||||
|
||||
// Procesa y ajusta el texto del sprite actual
|
||||
std::string txt = sprite_list_[i];
|
||||
std::ranges::replace(txt, '_', ' '); // Reemplaza '_' por ' '
|
||||
if (txt == "player") {
|
||||
txt = Locale::get()->get("ending2.jaildoctor"); // NOLINT(readability-static-accessed-through-instance) Reemplaza "player" por nombre localizado
|
||||
}
|
||||
|
||||
// Calcula las dimensiones del texto
|
||||
const float W = text->length(txt, 1);
|
||||
const float H = text->getCharacterSize();
|
||||
|
||||
// Determina la columna y la posición X del texto
|
||||
const float X = (i == sprite_list_.size() - 1)
|
||||
? (GameCanvas::CENTER_X - (W / 2))
|
||||
: ((i % 2 == 0 ? FIRST_COL : SECOND_COL) - (W / 2));
|
||||
|
||||
// Calcula la posición Y del texto en base a la posición y altura del sprite
|
||||
const float Y = sprites_.at(i)->getPosY() + sprites_.at(i)->getHeight() + DIST_SPRITE_TEXT;
|
||||
|
||||
// Crea la surface
|
||||
auto surface = std::make_shared<Surface>(W, H);
|
||||
auto previuos_renderer = Screen::get()->getRendererSurface();
|
||||
Screen::get()->setRendererSurface(surface);
|
||||
text->write(0, 0, txt);
|
||||
|
||||
// Crea el sprite
|
||||
SDL_FRect pos = {.x = X, .y = Y, .w = W, .h = H};
|
||||
sprite_texts_.emplace_back(std::make_shared<DissolveSprite>(surface, pos));
|
||||
sprite_texts_.back()->setColorReplace(1, 14);
|
||||
sprite_texts_.back()->setProgress(1.0F); // comença invisible
|
||||
sprite_texts_.back()->setVelY(SPRITE_DESP_SPEED);
|
||||
Screen::get()->setRendererSurface(previuos_renderer);
|
||||
}
|
||||
}
|
||||
|
||||
// Crea los sprites con las texturas con los textos del final
|
||||
void Ending2::createTexts() { // NOLINT(readability-convert-member-functions-to-static)
|
||||
// Crea los primeros textos
|
||||
std::vector<std::string> list;
|
||||
list.emplace_back(Locale::get()->get("ending2.starring"));
|
||||
|
||||
auto text = Resource::Cache::get()->getText("smb2");
|
||||
|
||||
// Crea los sprites de texto a partir de la lista
|
||||
for (size_t i = 0; i < list.size(); ++i) {
|
||||
// Calcula constantes
|
||||
const float W = text->length(list[i], 1);
|
||||
const float H = text->getCharacterSize();
|
||||
const float X = GameCanvas::CENTER_X;
|
||||
const float DX = -(W / 2);
|
||||
const float Y = Options::game.height + (text->getCharacterSize() * (i * TEXT_SPACING_MULTIPLIER));
|
||||
|
||||
// Crea la surface
|
||||
auto surface = std::make_shared<Surface>(W, H);
|
||||
auto previuos_renderer = Screen::get()->getRendererSurface();
|
||||
Screen::get()->setRendererSurface(surface);
|
||||
text->write(0, 0, list[i]);
|
||||
|
||||
// Crea el sprite
|
||||
SDL_FRect pos = {.x = X + DX, .y = Y, .w = W, .h = H};
|
||||
texts_.emplace_back(std::make_shared<DissolveSprite>(surface, pos));
|
||||
texts_.back()->setProgress(1.0F); // comença invisible
|
||||
texts_.back()->setVelY(SPRITE_DESP_SPEED);
|
||||
Screen::get()->setRendererSurface(previuos_renderer);
|
||||
}
|
||||
|
||||
// Crea los últimos textos
|
||||
// El primer texto va a continuación del ultimo spriteText
|
||||
const int START = sprite_texts_.back()->getPosY() + (text->getCharacterSize() * 15);
|
||||
list.clear();
|
||||
list.emplace_back(Locale::get()->get("ending2.thank_you"));
|
||||
list.emplace_back(Locale::get()->get("ending2.for_playing"));
|
||||
|
||||
// Crea los sprites de texto a partir de la lista
|
||||
for (size_t i = 0; i < list.size(); ++i) {
|
||||
// Calcula constantes
|
||||
const float W = text->length(list[i], 1);
|
||||
const float H = text->getCharacterSize();
|
||||
const float X = GameCanvas::CENTER_X;
|
||||
const float DX = -(W / 2);
|
||||
const float Y = START + (text->getCharacterSize() * (i * TEXT_SPACING_MULTIPLIER));
|
||||
|
||||
// Crea la surface
|
||||
auto surface = std::make_shared<Surface>(W, H);
|
||||
auto previuos_renderer = Screen::get()->getRendererSurface();
|
||||
Screen::get()->setRendererSurface(surface);
|
||||
text->write(0, 0, list[i]);
|
||||
|
||||
// Crea el sprite
|
||||
SDL_FRect pos = {.x = X + DX, .y = Y, .w = W, .h = H};
|
||||
texts_.emplace_back(std::make_shared<DissolveSprite>(surface, pos));
|
||||
texts_.back()->setProgress(1.0F); // comença invisible
|
||||
texts_.back()->setVelY(SPRITE_DESP_SPEED);
|
||||
Screen::get()->setRendererSurface(previuos_renderer);
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza el fade final
|
||||
void Ending2::updateFinalFade() {
|
||||
for (const auto& sprite : texts_) {
|
||||
sprite->getSurface()->fadeSubPalette(0);
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <memory> // Para shared_ptr
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "core/rendering/sprite/dissolve_sprite.hpp" // Para SurfaceDissolveSprite
|
||||
#include "utils/defines.hpp" // Para GameCanvas::WIDTH, GameCanvas::FIRST_QUAR...
|
||||
|
||||
class MovingSprite;
|
||||
class DeltaTimer;
|
||||
|
||||
class Ending2 {
|
||||
public:
|
||||
// --- Constructor y Destructor ---
|
||||
Ending2();
|
||||
~Ending2() = default;
|
||||
|
||||
// --- Bucle principal ---
|
||||
void run();
|
||||
|
||||
private:
|
||||
// --- Enumeraciones ---
|
||||
enum class EndingState : int {
|
||||
PRE_CREDITS, // Estado previo a los créditos
|
||||
CREDITS, // Estado de los créditos
|
||||
POST_CREDITS, // Estado posterior a los créditos
|
||||
FADING, // Estado de fundido de los textos a negro
|
||||
};
|
||||
|
||||
// --- Estructuras ---
|
||||
struct State {
|
||||
EndingState state{EndingState::PRE_CREDITS}; // Estado actual
|
||||
float duration{0.0F}; // Duración en segundos para el estado actual
|
||||
};
|
||||
|
||||
// --- Constantes ---
|
||||
static constexpr int FIRST_COL = GameCanvas::FIRST_QUARTER_X + (GameCanvas::WIDTH / 16); // Primera columna por donde desfilan los sprites
|
||||
static constexpr int SECOND_COL = GameCanvas::THIRD_QUARTER_X - (GameCanvas::WIDTH / 16); // Segunda columna por donde desfilan los sprites
|
||||
static constexpr int DIST_SPRITE_TEXT = 8; // Distancia entre el sprite y el texto que lo acompaña
|
||||
static constexpr int DIST_SPRITE_SPRITE = 0; // Distancia entre dos sprites de la misma columna
|
||||
static constexpr int INITIAL_Y_OFFSET = 40; // Offset inicial en Y para posicionar sprites
|
||||
static constexpr int SCREEN_MESH_HEIGHT = 8; // Altura de la malla superior/inferior de la pantalla
|
||||
static constexpr int FADE_H = 24; // Alçada de la zona de dissolució als cantons (files)
|
||||
static constexpr float TRANSITION_DURATION_MS = 500.0F; // ms per canviar d'estat (generar o dissoldre)
|
||||
static constexpr int ENTRY_EXIT_PADDING = 2; // px de padding als bordes per activar dissolució/generació
|
||||
static constexpr int TEXT_SPACING_MULTIPLIER = 2; // Multiplicador para espaciado entre líneas de texto
|
||||
|
||||
// Constantes de tiempo (basadas en tiempo real, no en frames)
|
||||
static constexpr float SPRITE_DESP_SPEED = -12.0F; // Velocidad de desplazamiento en pixels/segundo (era -0.2 px/frame @ 60fps)
|
||||
static constexpr float STATE_PRE_CREDITS_DURATION = 3.0F; // Duración del estado previo a créditos en segundos
|
||||
static constexpr float STATE_POST_CREDITS_DURATION = 5.0F; // Duración del estado posterior a créditos en segundos
|
||||
static constexpr float STATE_FADE_DURATION = 5.0F; // Duración del fade final en segundos
|
||||
static constexpr int MUSIC_FADE_DURATION = 3000; // Duración del fade de música en milisegundos (para Audio API)
|
||||
|
||||
// --- Métodos ---
|
||||
void update(); // Actualiza el objeto
|
||||
void render(); // Dibuja el final en pantalla
|
||||
static void handleEvents(); // Comprueba el manejador de eventos
|
||||
static void handleInput(); // Comprueba las entradas
|
||||
void updateState(float delta_time); // Actualiza el estado
|
||||
void transitionToState(EndingState new_state); // Transición entre estados
|
||||
void iniSpriteList(); // Inicializa la lista de sprites
|
||||
void loadSprites(); // Carga todos los sprites desde una lista
|
||||
void updateSprites(float delta); // Actualiza los sprites
|
||||
void updateTextSprites(float delta); // Actualiza los sprites de texto
|
||||
void updateTexts(float delta); // Actualiza los sprites de texto del final
|
||||
void renderSprites(); // Dibuja los sprites
|
||||
void renderSpriteTexts(); // Dibuja los sprites con el texto
|
||||
void renderTexts(); // Dibuja los sprites con el texto del final
|
||||
void placeSprites() const; // Coloca los sprites en su sitio
|
||||
void createSpriteTexts(); // Crea los sprites con las texturas con los textos
|
||||
void createTexts(); // Crea los sprites con las texturas con los textos del final
|
||||
void updateFinalFade(); // Actualiza el fade final
|
||||
|
||||
// --- Variables miembro ---
|
||||
// Objetos y punteros a recursos
|
||||
std::vector<std::shared_ptr<DissolveSprite>> sprites_; // Vector con todos los sprites a dibujar
|
||||
std::vector<std::shared_ptr<DissolveSprite>> sprite_texts_; // Vector con los sprites de texto de los sprites
|
||||
std::vector<std::shared_ptr<DissolveSprite>> texts_; // Vector con los sprites de texto
|
||||
std::unique_ptr<DeltaTimer> delta_timer_; // Timer para time-based update
|
||||
|
||||
// Variables de estado
|
||||
State state_; // Controla el estado de la clase
|
||||
float state_time_{0.0F}; // Tiempo acumulado en el estado actual
|
||||
|
||||
// Variables auxiliares
|
||||
std::vector<std::string> sprite_list_; // Lista con todos los sprites a dibujar
|
||||
std::vector<Uint8> colors_; // Vector con los colores para el fade
|
||||
float sprite_max_width_{0.0F}; // El valor de ancho del sprite más ancho
|
||||
float sprite_max_height_{0.0F}; // El valor de alto del sprite más alto
|
||||
};
|
||||
@@ -157,7 +157,7 @@ Game::Game(Mode mode)
|
||||
};
|
||||
#endif
|
||||
|
||||
SceneManager::current = (mode_ == Mode::GAME) ? SceneManager::Scene::GAME : SceneManager::Scene::DEMO;
|
||||
SceneManager::current = SceneManager::Scene::GAME;
|
||||
SceneManager::options = SceneManager::Options::NONE;
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ void Game::run() {
|
||||
Audio::get()->pauseMusic();
|
||||
}
|
||||
|
||||
while (SceneManager::current == SceneManager::Scene::GAME || SceneManager::current == SceneManager::Scene::DEMO) {
|
||||
while (SceneManager::current == SceneManager::Scene::GAME) {
|
||||
update();
|
||||
render();
|
||||
}
|
||||
@@ -375,7 +375,7 @@ void Game::updateGameOver(float delta_time) {
|
||||
// Pequeño delay antes de cambiar escena
|
||||
state_time_ += delta_time;
|
||||
if (state_time_ > 0.1F) { // 100ms de delay mínimo
|
||||
SceneManager::current = SceneManager::Scene::GAME_OVER;
|
||||
SceneManager::current = SceneManager::Scene::TITLE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,7 +409,7 @@ void Game::updatePostFadeEnding(float delta_time) {
|
||||
|
||||
// Después del delay, cambiar a la escena de ending
|
||||
if (state_time_ >= POST_FADE_DELAY) {
|
||||
SceneManager::current = SceneManager::Scene::ENDING;
|
||||
SceneManager::current = SceneManager::Scene::TITLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
#include "game/scenes/game_over.hpp"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <algorithm> // Para min, max
|
||||
#include <string> // Para basic_string, operator+, to_string
|
||||
|
||||
#include "core/audio/audio.hpp" // Para Audio
|
||||
#include "core/input/global_inputs.hpp" // Para check
|
||||
#include "core/input/input.hpp" // Para Input
|
||||
#include "core/locale/locale.hpp" // Para Locale
|
||||
#include "core/rendering/screen.hpp" // Para Screen
|
||||
#include "core/rendering/sprite/animated_sprite.hpp" // Para SAnimatedSprite
|
||||
#include "core/rendering/text.hpp" // Para Text::CENTER_FLAG, Text::COLOR_FLAG, Text
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||
#include "core/system/global_events.hpp" // Para check
|
||||
#include "game/options.hpp" // Para Options, options, OptionsStats, Secti...
|
||||
#include "game/scene_manager.hpp" // Para SceneManager
|
||||
#include "utils/defines.hpp" // Para GameCanvas::CENTER_X
|
||||
#include "utils/delta_timer.hpp" // Para DeltaTimer
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
// Constructor
|
||||
GameOver::GameOver()
|
||||
: player_sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData("player_game_over.yaml"))),
|
||||
tv_sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData("tv.yaml"))),
|
||||
delta_timer_(std::make_shared<DeltaTimer>()) {
|
||||
SceneManager::current = SceneManager::Scene::GAME_OVER;
|
||||
SceneManager::options = SceneManager::Options::NONE;
|
||||
|
||||
// Inicializa las posiciones de los sprites usando las constantes
|
||||
player_sprite_->setPosX(GameCanvas::CENTER_X + PLAYER_X_OFFSET);
|
||||
player_sprite_->setPosY(TEXT_Y + SPRITE_Y_OFFSET);
|
||||
tv_sprite_->setPosX(GameCanvas::CENTER_X - tv_sprite_->getWidth() - TV_X_OFFSET);
|
||||
tv_sprite_->setPosY(TEXT_Y + SPRITE_Y_OFFSET);
|
||||
|
||||
Screen::get()->setBorderColor(0);
|
||||
|
||||
// Inicializa el vector de colores (de brillante a oscuro para fade)
|
||||
colors_ = {14, 12, 10, 8, 6, 4, 2, 0};
|
||||
color_ = colors_.back(); // Empieza en black
|
||||
}
|
||||
|
||||
// Actualiza el objeto
|
||||
void GameOver::update() {
|
||||
const float DELTA_TIME = delta_timer_->tick();
|
||||
elapsed_time_ += DELTA_TIME;
|
||||
|
||||
handleEvents(); // Comprueba los eventos
|
||||
handleInput(); // Comprueba las entradas
|
||||
|
||||
updateState(); // Actualiza el estado de la escena
|
||||
updateColor(); // Actualiza el color usado para renderizar los textos e imagenes
|
||||
player_sprite_->update(DELTA_TIME); // Actualiza el sprite
|
||||
tv_sprite_->update(DELTA_TIME); // Actualiza el sprite
|
||||
|
||||
Audio::update(); // Actualiza el objeto Audio
|
||||
Screen::get()->update(DELTA_TIME); // Actualiza el objeto Screen
|
||||
}
|
||||
|
||||
// Dibuja el final en pantalla
|
||||
void GameOver::render() {
|
||||
Screen::get()->start();
|
||||
Screen::get()->clearSurface(0);
|
||||
|
||||
auto text = Resource::Cache::get()->getText("smb2");
|
||||
|
||||
// Escribe el texto de GAME OVER
|
||||
auto* loc = Locale::get();
|
||||
text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y, loc->get("game_over.title"), 1, color_); // NOLINT(readability-static-accessed-through-instance)
|
||||
|
||||
// Dibuja los sprites (ya posicionados en el constructor, solo ajustamos Y)
|
||||
player_sprite_->setPosY(TEXT_Y + SPRITE_Y_OFFSET);
|
||||
tv_sprite_->setPosY(TEXT_Y + SPRITE_Y_OFFSET);
|
||||
renderSprites();
|
||||
|
||||
// Escribe el texto con las habitaciones y los items
|
||||
const std::string ITEMS_TEXT = std::to_string(Options::stats.items / 100) + std::to_string((Options::stats.items % 100) / 10) + std::to_string(Options::stats.items % 10);
|
||||
const std::string ROOMS_TEXT = std::to_string(Options::stats.rooms / 100) + std::to_string((Options::stats.rooms % 100) / 10) + std::to_string(Options::stats.rooms % 10);
|
||||
text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + ITEMS_Y_OFFSET, loc->get("game_over.items") + ITEMS_TEXT, 1, color_); // NOLINT(readability-static-accessed-through-instance)
|
||||
text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + ROOMS_Y_OFFSET, loc->get("game_over.rooms") + ROOMS_TEXT, 1, color_); // NOLINT(readability-static-accessed-through-instance)
|
||||
|
||||
// Vuelca el contenido del renderizador en pantalla
|
||||
Screen::get()->render();
|
||||
}
|
||||
|
||||
// Comprueba el manejador de eventos
|
||||
void GameOver::handleEvents() {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
GlobalEvents::handle(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba las entradas
|
||||
void GameOver::handleInput() {
|
||||
Input::get()->update();
|
||||
GlobalInputs::handle();
|
||||
}
|
||||
|
||||
// Bucle principal
|
||||
void GameOver::run() {
|
||||
while (SceneManager::current == SceneManager::Scene::GAME_OVER) {
|
||||
update();
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza el color usado para renderizar los textos e imagenes
|
||||
void GameOver::updateColor() {
|
||||
// Calcula el color basado en el estado actual
|
||||
switch (state_) {
|
||||
case State::WAITING:
|
||||
// Durante la espera, mantener en black
|
||||
color_ = colors_.back(); // black
|
||||
break;
|
||||
|
||||
case State::FADE_IN: {
|
||||
// Fade in: de black (último color) a white (primer color)
|
||||
// Progreso: 0.0 (black) -> 1.0 (white)
|
||||
const float PROGRESS = std::min(elapsed_time_ / FADE_IN_DURATION, 1.0F);
|
||||
const int INDEX = (colors_.size() - 1) - static_cast<int>((colors_.size() - 1) * PROGRESS);
|
||||
color_ = colors_[std::clamp(INDEX, 0, static_cast<int>(colors_.size() - 1))];
|
||||
break;
|
||||
}
|
||||
|
||||
case State::DISPLAY:
|
||||
// Durante display, mantener el color más brillante
|
||||
color_ = colors_[0]; // white
|
||||
break;
|
||||
|
||||
case State::FADE_OUT: {
|
||||
// Fade out: de white (primer color) a black (último color)
|
||||
// Progreso: 0.0 (white) -> 1.0 (black)
|
||||
const float PROGRESS = std::min(elapsed_time_ / FADE_OUT_DURATION, 1.0F);
|
||||
const int INDEX = static_cast<int>((colors_.size() - 1) * PROGRESS);
|
||||
color_ = colors_[std::clamp(INDEX, 0, static_cast<int>(colors_.size() - 1))];
|
||||
break;
|
||||
}
|
||||
|
||||
case State::ENDING:
|
||||
case State::TRANSITION:
|
||||
// Al final, mantener en black
|
||||
color_ = colors_.back(); // black
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Dibuja los sprites
|
||||
void GameOver::renderSprites() {
|
||||
player_sprite_->render(1, color_);
|
||||
tv_sprite_->render(1, color_);
|
||||
}
|
||||
|
||||
// Actualiza el estado de la escena y gestiona transiciones
|
||||
void GameOver::updateState() {
|
||||
// Máquina de estados basada en tiempo transcurrido
|
||||
switch (state_) {
|
||||
case State::WAITING:
|
||||
// Espera inicial antes de empezar
|
||||
if (elapsed_time_ >= WAITING_DURATION) {
|
||||
state_ = State::FADE_IN;
|
||||
elapsed_time_ = 0.0F;
|
||||
// Hace sonar la música cuando termina la espera
|
||||
Audio::get()->playMusic("574070_KUVO_Farewell_to_school.ogg", 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::FADE_IN:
|
||||
// Fade in de colores desde black
|
||||
if (elapsed_time_ >= FADE_IN_DURATION) {
|
||||
state_ = State::DISPLAY;
|
||||
elapsed_time_ = 0.0F;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::DISPLAY:
|
||||
// Mostrando contenido con color brillante
|
||||
if (elapsed_time_ >= DISPLAY_DURATION) {
|
||||
state_ = State::FADE_OUT;
|
||||
elapsed_time_ = 0.0F;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::FADE_OUT:
|
||||
// Fade out hacia black
|
||||
if (elapsed_time_ >= FADE_OUT_DURATION) {
|
||||
state_ = State::ENDING;
|
||||
elapsed_time_ = 0.0F;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::ENDING:
|
||||
// Pantalla en negro antes de salir
|
||||
if (elapsed_time_ >= ENDING_DURATION) {
|
||||
state_ = State::TRANSITION;
|
||||
elapsed_time_ = 0.0F;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::TRANSITION:
|
||||
// Transición a la escena de logo
|
||||
SceneManager::current = SceneManager::Scene::LOGO;
|
||||
SceneManager::options = SceneManager::Options::LOGO_TO_TITLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <memory> // Para shared_ptr
|
||||
#include <vector> // Para vector
|
||||
class AnimatedSprite; // lines 7-7
|
||||
class DeltaTimer; // Forward declaration
|
||||
|
||||
class GameOver {
|
||||
public:
|
||||
// Constructor y Destructor
|
||||
GameOver();
|
||||
~GameOver() = default;
|
||||
|
||||
// Bucle principal
|
||||
void run();
|
||||
|
||||
private:
|
||||
// --- Enumeraciones ---
|
||||
enum class State {
|
||||
WAITING, // Espera inicial antes de empezar
|
||||
FADE_IN, // Fade in de colores desde black
|
||||
DISPLAY, // Mostrando contenido con color brillante
|
||||
FADE_OUT, // Fade out hacia black
|
||||
ENDING, // Pantalla en negro antes de salir
|
||||
TRANSITION // Cambio a logo
|
||||
};
|
||||
|
||||
// --- Constantes de duración (segundos) ---
|
||||
static constexpr float WAITING_DURATION = 0.8F; // Espera inicial
|
||||
static constexpr float FADE_IN_DURATION = 0.32F; // Duración del fade in
|
||||
static constexpr float DISPLAY_DURATION = 4.64F; // Duración mostrando contenido
|
||||
static constexpr float FADE_OUT_DURATION = 0.32F; // Duración del fade out
|
||||
static constexpr float ENDING_DURATION = 1.12F; // Espera en negro antes de salir
|
||||
|
||||
// --- Constantes de posición ---
|
||||
static constexpr int TEXT_Y = 32; // Posición Y del texto principal
|
||||
static constexpr int SPRITE_Y_OFFSET = 30; // Offset Y para sprites desde TEXT_Y
|
||||
static constexpr int PLAYER_X_OFFSET = 10; // Offset X del jugador desde el centro
|
||||
static constexpr int TV_X_OFFSET = 10; // Offset X del TV desde el centro
|
||||
static constexpr int ITEMS_Y_OFFSET = 80; // Offset Y del texto de items desde TEXT_Y
|
||||
static constexpr int ROOMS_Y_OFFSET = 90; // Offset Y del texto de rooms desde TEXT_Y
|
||||
|
||||
// --- Métodos ---
|
||||
void update(); // Actualiza el objeto
|
||||
void render(); // Dibuja el final en pantalla
|
||||
static void handleEvents(); // Comprueba el manejador de eventos
|
||||
static void handleInput(); // Comprueba las entradas
|
||||
void updateState(); // Actualiza el estado y transiciones
|
||||
void updateColor(); // Actualiza el color usado para renderizar
|
||||
void renderSprites(); // Dibuja los sprites
|
||||
|
||||
// --- Variables miembro ---
|
||||
// Objetos y punteros a recursos
|
||||
std::shared_ptr<AnimatedSprite> player_sprite_; // Sprite con el jugador
|
||||
std::shared_ptr<AnimatedSprite> tv_sprite_; // Sprite con el televisor
|
||||
std::shared_ptr<DeltaTimer> delta_timer_; // Timer para time-based logic
|
||||
|
||||
// Variables de estado de la escena
|
||||
State state_{State::WAITING}; // Estado actual de la escena
|
||||
float elapsed_time_{0.0F}; // Tiempo transcurrido en el estado actual
|
||||
|
||||
// Variables de efectos visuales
|
||||
std::vector<Uint8> colors_; // Vector con los colores para el fade
|
||||
Uint8 color_{0}; // Color actual para texto y sprites
|
||||
};
|
||||
@@ -1,498 +0,0 @@
|
||||
#include "game/scenes/loading_screen.hpp"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <cmath> // Para std::sin
|
||||
#include <cstdlib> // Para rand
|
||||
|
||||
#include "core/audio/audio.hpp" // Para Audio
|
||||
#include "core/input/global_inputs.hpp" // Para check
|
||||
#include "core/input/input.hpp" // Para Input
|
||||
#include "core/rendering/screen.hpp" // Para Screen
|
||||
#include "core/rendering/sprite/sprite.hpp" // Para SSprite
|
||||
#include "core/rendering/surface.hpp" // Para Surface
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||
#include "core/system/global_events.hpp" // Para check
|
||||
#include "game/options.hpp" // Para Options, options, SectionState, Options...
|
||||
#include "game/scene_manager.hpp" // Para SceneManager
|
||||
#include "utils/defines.hpp" // Para GAME_SPEED
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
// Constructor
|
||||
LoadingScreen::LoadingScreen()
|
||||
: mono_loading_screen_surface_(Resource::Cache::get()->getSurface("loading_screen_bn.gif")),
|
||||
color_loading_screen_surface_(Resource::Cache::get()->getSurface("loading_screen_color.gif")),
|
||||
mono_loading_screen_sprite_(std::make_unique<Sprite>(mono_loading_screen_surface_, 0, 0, mono_loading_screen_surface_->getWidth(), mono_loading_screen_surface_->getHeight())),
|
||||
color_loading_screen_sprite_(std::make_unique<Sprite>(color_loading_screen_surface_, 0, 0, color_loading_screen_surface_->getWidth(), color_loading_screen_surface_->getHeight())),
|
||||
program_sprite_(std::make_unique<Sprite>(Resource::Cache::get()->getSurface("program_jaildoc.gif"))),
|
||||
screen_surface_(std::make_shared<Surface>(Options::game.width, Options::game.height)),
|
||||
delta_timer_(std::make_unique<DeltaTimer>()) {
|
||||
// Configura la superficie donde se van a pintar los sprites
|
||||
screen_surface_->clear(14);
|
||||
|
||||
// Inicializa variables
|
||||
SceneManager::current = SceneManager::Scene::LOADING_SCREEN;
|
||||
SceneManager::options = SceneManager::Options::NONE;
|
||||
program_sprite_->setPosition(0.0F, 8.0F);
|
||||
|
||||
// Inicializa el array de índices de líneas
|
||||
initLineIndexArray();
|
||||
|
||||
// Cambia el color del borde
|
||||
Screen::get()->setBorderColor(14);
|
||||
transitionToState(State::SILENT1);
|
||||
}
|
||||
|
||||
// Destructor
|
||||
LoadingScreen::~LoadingScreen() {
|
||||
Audio::get()->stopMusic();
|
||||
}
|
||||
|
||||
// Comprueba el manejador de eventos
|
||||
void LoadingScreen::handleEvents() {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
GlobalEvents::handle(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba las entradas
|
||||
void LoadingScreen::handleInput() {
|
||||
Input::get()->update();
|
||||
GlobalInputs::handle();
|
||||
}
|
||||
|
||||
// Inicializa el array de índices de líneas (imita el direccionamiento de memoria del Spectrum)
|
||||
void LoadingScreen::initLineIndexArray() { // NOLINT(readability-convert-member-functions-to-static)
|
||||
for (int i = 0; i < MONO_TOTAL_LINES; ++i) {
|
||||
if (i < 64) { // Primer bloque de 2K
|
||||
line_index_[i] = ((i % 8) * 8) + (i / 8);
|
||||
} else if (i < 128) { // Segundo bloque de 2K
|
||||
line_index_[i] = 64 + ((i % 8) * 8) + ((i - 64) / 8);
|
||||
} else { // Tercer bloque de 2K
|
||||
line_index_[i] = 128 + ((i % 8) * 8) + ((i - 128) / 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transiciona a un nuevo estado
|
||||
void LoadingScreen::transitionToState(State new_state) {
|
||||
state_ = new_state;
|
||||
state_time_ = 0.0F;
|
||||
|
||||
// Acciones específicas al entrar en cada estado
|
||||
switch (new_state) {
|
||||
case State::SILENT1:
|
||||
case State::SILENT2:
|
||||
current_border_type_ = Border::RED;
|
||||
Audio::get()->stopMusic();
|
||||
break;
|
||||
|
||||
case State::HEADER1:
|
||||
case State::HEADER2:
|
||||
current_border_type_ = Border::RED_AND_CYAN;
|
||||
Audio::get()->playMusic("574071_EA_DTV.ogg", 0);
|
||||
break;
|
||||
|
||||
case State::DATA1:
|
||||
printProgramName();
|
||||
current_border_type_ = Border::YELLOW_AND_BLUE;
|
||||
Audio::get()->playMusic("574071_EA_DTV.ogg", 0);
|
||||
break;
|
||||
case State::DATA2:
|
||||
current_border_type_ = Border::YELLOW_AND_BLUE;
|
||||
Audio::get()->playMusic("574070_KUVO_Farewell_to_school.ogg", 0);
|
||||
break;
|
||||
case State::LOADING_MONO:
|
||||
current_border_type_ = Border::YELLOW_AND_BLUE;
|
||||
Audio::get()->playMusic("574071_EA_DTV.ogg", 0);
|
||||
last_mono_step_ = -1; // Resetear contador de pasos mono
|
||||
break;
|
||||
|
||||
case State::LOADING_COLOR:
|
||||
current_border_type_ = Border::YELLOW_AND_BLUE;
|
||||
Audio::get()->playMusic("574070_KUVO_Farewell_to_school.ogg", 0);
|
||||
last_color_block_ = -1; // Resetear contador de bloques color
|
||||
break;
|
||||
|
||||
case State::COMPLETE:
|
||||
current_border_type_ = Border::BLACK;
|
||||
// Transicionar a la pantalla de título
|
||||
SceneManager::current = SceneManager::Scene::TITLE;
|
||||
SceneManager::options = SceneManager::Options::TITLE_WITH_LOADING_SCREEN;
|
||||
Audio::get()->stopMusic();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza el estado actual
|
||||
void LoadingScreen::updateState(float delta_time) {
|
||||
state_time_ += delta_time;
|
||||
|
||||
// Transiciones automáticas por tiempo para los estados iniciales
|
||||
// LOADING_MONO y LOADING_COLOR transicionan en sus propias funciones
|
||||
switch (state_) {
|
||||
case State::SILENT1:
|
||||
if (state_time_ >= SILENT1_DURATION) {
|
||||
transitionToState(State::HEADER1);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::HEADER1:
|
||||
if (state_time_ >= HEADER1_DURATION) {
|
||||
transitionToState(State::DATA1);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::DATA1:
|
||||
if (state_time_ >= DATA1_DURATION) {
|
||||
transitionToState(State::SILENT2);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::SILENT2:
|
||||
if (state_time_ >= SILENT2_DURATION) {
|
||||
transitionToState(State::HEADER2);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::HEADER2:
|
||||
if (state_time_ >= HEADER2_DURATION) {
|
||||
transitionToState(State::LOADING_MONO);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::DATA2:
|
||||
if (state_time_ >= DATA2_DURATION) {
|
||||
transitionToState(State::COMPLETE);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::LOADING_MONO:
|
||||
case State::LOADING_COLOR:
|
||||
case State::COMPLETE:
|
||||
// Estos estados se gestionan en updateMonoLoad/updateColorLoad
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Gestiona la carga monocromática (time-based simplificado)
|
||||
void LoadingScreen::updateMonoLoad(float delta_time) {
|
||||
// Calcular progreso lineal (0.0 - 1.0)
|
||||
float progress = state_time_ / LOADING_MONO_DURATION;
|
||||
progress = std::min(progress, 1.0F);
|
||||
|
||||
// Calcular paso total actual (0-959)
|
||||
const int TOTAL_STEPS = MONO_TOTAL_LINES * MONO_STEPS_PER_LINE; // 192 * 5 = 960
|
||||
const int CURRENT_STEP = static_cast<int>(progress * TOTAL_STEPS);
|
||||
|
||||
// Verificar si ha completado todas las líneas
|
||||
if (CURRENT_STEP >= TOTAL_STEPS) {
|
||||
transitionToState(State::LOADING_COLOR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Dibujar todos los pasos intermedios desde el último dibujado
|
||||
const float TEXTURE_WIDTH = mono_loading_screen_surface_->getWidth();
|
||||
const float CLIP_WIDTH = TEXTURE_WIDTH / MONO_STEPS_PER_LINE;
|
||||
|
||||
auto previous_renderer = Screen::get()->getRendererSurface();
|
||||
Screen::get()->setRendererSurface(screen_surface_);
|
||||
|
||||
for (int step = last_mono_step_ + 1; step <= CURRENT_STEP; ++step) {
|
||||
// Calcular línea y sub-paso para este paso
|
||||
const int CURRENT_LINE = step / MONO_STEPS_PER_LINE; // 0-191
|
||||
const int CURRENT_SUBSTEP = step % MONO_STEPS_PER_LINE; // 0-4
|
||||
|
||||
// Saltar si excede el total de líneas
|
||||
if (CURRENT_LINE >= MONO_TOTAL_LINES) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Calcular rectángulo de clip para este paso
|
||||
const float CLIP_X = CURRENT_SUBSTEP * CLIP_WIDTH;
|
||||
|
||||
load_rect_.x = CLIP_X;
|
||||
load_rect_.y = static_cast<float>(line_index_[CURRENT_LINE]);
|
||||
load_rect_.w = CLIP_WIDTH;
|
||||
load_rect_.h = 1.0F;
|
||||
|
||||
// Configurar y dibujar sobre screen_surface_
|
||||
mono_loading_screen_sprite_->setClip(load_rect_);
|
||||
mono_loading_screen_sprite_->setPosition(load_rect_);
|
||||
mono_loading_screen_sprite_->render();
|
||||
}
|
||||
|
||||
Screen::get()->setRendererSurface(previous_renderer);
|
||||
|
||||
// Actualizar el último paso dibujado
|
||||
last_mono_step_ = CURRENT_STEP;
|
||||
}
|
||||
|
||||
// Gestiona la carga en color
|
||||
void LoadingScreen::updateColorLoad(float delta_time) {
|
||||
// Calcular progreso lineal (0.0 - 1.0)
|
||||
float progress = state_time_ / LOADING_COLOR_DURATION;
|
||||
progress = std::min(progress, 1.0F);
|
||||
|
||||
// Calcular bloque actual (0-767) - ahora pinta de 1 en 1 en lugar de 2 en 2
|
||||
const int CURRENT_BLOCK = static_cast<int>(progress * COLOR_TOTAL_BLOCKS);
|
||||
|
||||
// Verificar si ha completado todos los bloques
|
||||
if (CURRENT_BLOCK >= COLOR_TOTAL_BLOCKS) {
|
||||
transitionToState(State::DATA2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Dibujar todos los bloques intermedios desde el último dibujado
|
||||
auto previous_renderer = Screen::get()->getRendererSurface();
|
||||
Screen::get()->setRendererSurface(screen_surface_);
|
||||
|
||||
// Iterar desde el último bloque + 1 hasta el bloque actual (de 1 en 1)
|
||||
for (int block = last_color_block_ + 1; block <= CURRENT_BLOCK; ++block) {
|
||||
// Saltar si excede el total de bloques
|
||||
if (block >= COLOR_TOTAL_BLOCKS) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Calcular posición del bloque
|
||||
load_rect_.x = static_cast<float>((block * COLOR_BLOCK_SPACING) % 256);
|
||||
load_rect_.y = static_cast<float>((block / COLOR_BLOCKS_PER_ROW) * COLOR_BLOCK_SPACING);
|
||||
load_rect_.w = static_cast<float>(COLOR_BLOCK_WIDTH);
|
||||
load_rect_.h = static_cast<float>(COLOR_BLOCK_HEIGHT);
|
||||
|
||||
// Configurar y dibujar sobre screen_surface_
|
||||
color_loading_screen_sprite_->setClip(load_rect_);
|
||||
color_loading_screen_sprite_->setPosition(load_rect_);
|
||||
color_loading_screen_sprite_->render();
|
||||
}
|
||||
|
||||
Screen::get()->setRendererSurface(previous_renderer);
|
||||
|
||||
// Actualizar el último bloque dibujado
|
||||
last_color_block_ = CURRENT_BLOCK;
|
||||
}
|
||||
|
||||
// Dibuja el efecto de carga amarillo y azul en el borde
|
||||
void LoadingScreen::renderDataBorder() {
|
||||
// Obtiene la Surface del borde
|
||||
auto border = Screen::get()->getBorderSurface();
|
||||
|
||||
// Pinta el borde de color azul
|
||||
border->clear(2);
|
||||
|
||||
// Añade lineas amarillas
|
||||
const auto COLOR = 12;
|
||||
const int WIDTH = Options::game.width + (Options::video.border.width * 2);
|
||||
const int HEIGHT = Options::game.height + (Options::video.border.height * 2);
|
||||
bool draw_enabled = rand() % 2 == 0;
|
||||
|
||||
int row = 0;
|
||||
while (row < HEIGHT) {
|
||||
const int ROW_HEIGHT = (rand() % 4) + 3;
|
||||
if (draw_enabled) {
|
||||
for (int i = row; i < row + ROW_HEIGHT; ++i) {
|
||||
border->drawLine(0, i, WIDTH, i, COLOR);
|
||||
}
|
||||
}
|
||||
row += ROW_HEIGHT;
|
||||
draw_enabled = !draw_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
// Dibuja el efecto de carga rojo y azul en el borde
|
||||
void LoadingScreen::renderHeaderBorder() const {
|
||||
// Obtiene la Surface del borde
|
||||
auto border = Screen::get()->getBorderSurface();
|
||||
|
||||
// Pinta el borde de color azul o rojo
|
||||
border->clear(carrier_.toggle ? 10 : 4);
|
||||
|
||||
// Añade lineas rojas o azules
|
||||
const auto COLOR = carrier_.toggle ? 4 : 10;
|
||||
const int WIDTH = Options::game.width + (Options::video.border.width * 2);
|
||||
const int HEIGHT = Options::game.height + (Options::video.border.height * 2);
|
||||
|
||||
// Primera linea (con el color y tamaño de la portadora)
|
||||
int row = 0;
|
||||
const int FIRST_ROW_HEIGHT = static_cast<int>(carrier_.offset);
|
||||
for (int i = row; i < row + FIRST_ROW_HEIGHT; ++i) {
|
||||
border->drawLine(0, i, WIDTH, i, COLOR);
|
||||
}
|
||||
row += FIRST_ROW_HEIGHT;
|
||||
|
||||
// Resto de lineas (siguen a la portadora)
|
||||
bool draw_enabled = false;
|
||||
while (row < HEIGHT) {
|
||||
if (draw_enabled) {
|
||||
for (int i = row; i < row + HEADER_DATAROW_HEIGHT; ++i) {
|
||||
border->drawLine(0, i, WIDTH, i, COLOR);
|
||||
}
|
||||
}
|
||||
row += HEADER_DATAROW_HEIGHT;
|
||||
draw_enabled = !draw_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
// Dibuja el borde de color
|
||||
void LoadingScreen::renderColoredBorder(Uint8 color) {
|
||||
// Obtiene la Surface del borde
|
||||
auto border = Screen::get()->getBorderSurface();
|
||||
|
||||
// Pinta el borde del color indicado
|
||||
border->clear(color);
|
||||
}
|
||||
|
||||
// Actualiza las variables
|
||||
void LoadingScreen::update() {
|
||||
const float DELTA_TIME = delta_timer_->tick();
|
||||
|
||||
handleEvents(); // Comprueba los eventos
|
||||
handleInput(); // Comprueba las entradas
|
||||
|
||||
updateState(DELTA_TIME); // Actualiza el estado y gestiona transiciones
|
||||
|
||||
// Actualizar la carga según el estado actual
|
||||
switch (state_) {
|
||||
case State::DATA1:
|
||||
case State::DATA2:
|
||||
// Por ahora no hacen nada específico
|
||||
break;
|
||||
case State::SILENT1:
|
||||
case State::SILENT2:
|
||||
updateSilent(DELTA_TIME);
|
||||
break;
|
||||
case State::HEADER1:
|
||||
case State::HEADER2:
|
||||
updateCarrier(DELTA_TIME);
|
||||
break;
|
||||
|
||||
case State::LOADING_MONO:
|
||||
updateMonoLoad(DELTA_TIME);
|
||||
break;
|
||||
|
||||
case State::LOADING_COLOR:
|
||||
updateColorLoad(DELTA_TIME);
|
||||
break;
|
||||
|
||||
case State::COMPLETE:
|
||||
// No hay más actualizaciones
|
||||
break;
|
||||
}
|
||||
|
||||
Audio::update(); // Actualiza el objeto Audio
|
||||
Screen::get()->update(DELTA_TIME); // Actualiza el objeto Screen
|
||||
}
|
||||
|
||||
// Dibuja en pantalla
|
||||
void LoadingScreen::render() {
|
||||
// Pinta el borde
|
||||
renderBorder();
|
||||
|
||||
// Prepara para empezar a dibujar en la textura de juego
|
||||
Screen::get()->start();
|
||||
Screen::get()->clearSurface(14);
|
||||
|
||||
// Copia la surface a la surface de Screen
|
||||
screen_surface_->render(0, 0);
|
||||
|
||||
// Vuelca el contenido del renderizador en pantalla
|
||||
Screen::get()->render();
|
||||
}
|
||||
|
||||
// Bucle para el logo del juego
|
||||
void LoadingScreen::run() {
|
||||
// Ajusta el volumen
|
||||
Audio::get()->setMusicVolume(50);
|
||||
|
||||
// Limpia la pantalla
|
||||
Screen::get()->start();
|
||||
Screen::get()->clearRenderer();
|
||||
Screen::get()->render();
|
||||
|
||||
while (SceneManager::current == SceneManager::Scene::LOADING_SCREEN) {
|
||||
update();
|
||||
render();
|
||||
}
|
||||
|
||||
Audio::get()->setMusicVolume(100);
|
||||
}
|
||||
|
||||
// Pinta el borde
|
||||
void LoadingScreen::renderBorder() {
|
||||
if (Options::video.border.enabled) {
|
||||
// Dibuja el efecto de carga en el borde según el tipo actual
|
||||
switch (current_border_type_) {
|
||||
case Border::YELLOW_AND_BLUE:
|
||||
renderDataBorder();
|
||||
break;
|
||||
case Border::RED_AND_CYAN:
|
||||
renderHeaderBorder();
|
||||
break;
|
||||
case Border::WHITE:
|
||||
renderColoredBorder(14);
|
||||
break;
|
||||
case Border::BLACK:
|
||||
renderColoredBorder(0);
|
||||
break;
|
||||
case Border::RED:
|
||||
renderColoredBorder(4);
|
||||
break;
|
||||
case Border::CYAN:
|
||||
renderColoredBorder(10);
|
||||
break;
|
||||
case Border::NONE:
|
||||
// No renderizar borde
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Escribe el nombre del programa
|
||||
void LoadingScreen::printProgramName() { // NOLINT(readability-convert-member-functions-to-static)
|
||||
auto previous_renderer = Screen::get()->getRendererSurface();
|
||||
Screen::get()->setRendererSurface(screen_surface_);
|
||||
program_sprite_->render();
|
||||
Screen::get()->setRendererSurface(previous_renderer);
|
||||
}
|
||||
|
||||
// Actualiza la portadora
|
||||
void LoadingScreen::updateCarrier(float delta_time) {
|
||||
constexpr float CARRIER_BASE_SPEED = -250.0F;
|
||||
constexpr float CARRIER_HEIGHT = HEADER_DATAROW_HEIGHT;
|
||||
|
||||
// Oscilación compuesta: mezcla de dos frecuencias para evitar patrón predecible
|
||||
const float MODULATION = std::sin(carrier_.total_time * 1.2F) * std::sin((carrier_.total_time * 0.35F) + 1.0F);
|
||||
const float SPEED = CARRIER_BASE_SPEED * (0.5F + (0.5F * MODULATION)); // rango [-200, 0]
|
||||
|
||||
carrier_.offset += SPEED * delta_time;
|
||||
|
||||
if (carrier_.offset < 0.0F) {
|
||||
carrier_.offset += CARRIER_HEIGHT; // reinicia al rango [0,HEADER_DATAROW_HEIGHT]
|
||||
carrier_.toggle = !carrier_.toggle;
|
||||
}
|
||||
|
||||
carrier_.total_time += delta_time;
|
||||
}
|
||||
|
||||
// Actualiza el ruido durante el tiempo de silencio
|
||||
void LoadingScreen::updateSilent(float delta_time) {
|
||||
constexpr float NOISE_THRESHOLD = 0.35F;
|
||||
|
||||
// Oscilación compuesta para simular picos de ruido
|
||||
const float MODULATION = std::sin(noise_.total_time * 4.2F) * std::sin((noise_.total_time * 1.7F) + 0.5F);
|
||||
noise_.value = std::fabs(MODULATION); // rango [0.0, 1.0]
|
||||
|
||||
// Detecta cruce de umbral solo si venía de abajo
|
||||
if (noise_.value > NOISE_THRESHOLD && !noise_.crossed) {
|
||||
noise_.crossed = true;
|
||||
current_border_type_ = (current_border_type_ == Border::RED) ? Border::CYAN : Border::RED;
|
||||
}
|
||||
|
||||
// Restablece el flag cuando baja del umbral
|
||||
if (noise_.value < NOISE_THRESHOLD) {
|
||||
noise_.crossed = false;
|
||||
}
|
||||
|
||||
noise_.total_time += delta_time;
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <array> // Para std::array
|
||||
#include <memory> // Para shared_ptr
|
||||
|
||||
#include "utils/delta_timer.hpp" // Para DeltaTimer
|
||||
class Sprite; // Forward declaration
|
||||
class Surface; // Forward declaration
|
||||
|
||||
class LoadingScreen {
|
||||
public:
|
||||
// --- Constructor y Destructor ---
|
||||
LoadingScreen();
|
||||
~LoadingScreen();
|
||||
|
||||
// --- Bucle principal ---
|
||||
void run();
|
||||
|
||||
private:
|
||||
// --- Enumeraciones ---
|
||||
// Estados de la secuencia de carga
|
||||
enum class State {
|
||||
SILENT1, // Pausa inicial antes de empezar
|
||||
HEADER1, // Cabecera
|
||||
DATA1, // Datos
|
||||
SILENT2, // Segunda pausa
|
||||
HEADER2, // Cabecera pantalla
|
||||
LOADING_MONO, // Carga de pantalla monocromática (escaneo de líneas)
|
||||
LOADING_COLOR, // Carga de pantalla en color (bloques)
|
||||
DATA2, // Datos
|
||||
COMPLETE // Carga completa
|
||||
};
|
||||
|
||||
// Tipos de borde para la pantalla de carga
|
||||
enum class Border {
|
||||
NONE,
|
||||
YELLOW_AND_BLUE,
|
||||
RED_AND_CYAN,
|
||||
WHITE,
|
||||
BLACK,
|
||||
RED,
|
||||
CYAN
|
||||
};
|
||||
|
||||
// --- Estructuras ---
|
||||
struct Carrier {
|
||||
float offset{0.0F}; // Offset para la carga de cabeceras
|
||||
bool toggle{false}; // Para cambiar el color inicial
|
||||
float total_time{0.0F}; // Tiempo acumulado para modulación de velocidad
|
||||
};
|
||||
|
||||
struct Noise {
|
||||
float value{0.0F}; // Nivel actual de ruido (0.0 a 1.0)
|
||||
float total_time{0.0F}; // Tiempo acumulado para modulación
|
||||
bool crossed{false}; // Flag para detectar cruce de umbral
|
||||
};
|
||||
|
||||
// --- Constantes de tiempo (en segundos) ---
|
||||
static constexpr float SILENT1_DURATION = 2.0F; // Pausa inicial
|
||||
static constexpr float HEADER1_DURATION = 4.0F; // Cabecera
|
||||
static constexpr float DATA1_DURATION = 0.18F; // Datos
|
||||
static constexpr float SILENT2_DURATION = 1.6F; // Segunda pausa
|
||||
static constexpr float HEADER2_DURATION = 2.0F; // Cabecera pantalla
|
||||
static constexpr float LOADING_MONO_DURATION = 16.0F; // Duración total de la carga monocromática
|
||||
static constexpr float LOADING_COLOR_DURATION = 4.0F; // Duración total de la carga en color
|
||||
static constexpr float DATA2_DURATION = 5.0F; // Datos
|
||||
|
||||
// --- Constantes de geometría ---
|
||||
static constexpr int MONO_TOTAL_LINES = 192; // Total de líneas en carga monocromática
|
||||
static constexpr int MONO_STEPS_PER_LINE = 5; // Pasos de animación por línea
|
||||
static constexpr int COLOR_TOTAL_BLOCKS = 768; // Total de bloques en carga color
|
||||
static constexpr int COLOR_BLOCK_WIDTH = 16; // Ancho del bloque de color
|
||||
static constexpr int COLOR_BLOCK_HEIGHT = 8; // Alto del bloque de color
|
||||
static constexpr int COLOR_BLOCKS_PER_ROW = 32; // Bloques por fila (256 / 8)
|
||||
static constexpr int COLOR_BLOCK_SPACING = 8; // Espaciado entre bloques
|
||||
static constexpr int HEADER_DATAROW_HEIGHT = 9.0F; // Alto de las barras del borde de la carga de las cabeceras
|
||||
|
||||
// --- Métodos ---
|
||||
void update(); // Actualiza las variables
|
||||
void render(); // Dibuja en pantalla
|
||||
static void handleEvents(); // Comprueba el manejador de eventos
|
||||
static void handleInput(); // Comprueba las entradas
|
||||
void updateState(float delta_time); // Actualiza el estado actual
|
||||
void transitionToState(State new_state); // Transiciona a un nuevo estado
|
||||
void updateMonoLoad(float delta_time); // Gestiona la carga monocromática (time-based)
|
||||
void updateColorLoad(float delta_time); // Gestiona la carga en color (time-based)
|
||||
void renderBorder(); // Pinta el borde
|
||||
static void renderDataBorder(); // Dibuja el efecto de carga amarillo y azul en el borde
|
||||
void renderHeaderBorder() const; // Dibuja el efecto de carga rojo y azul en el borde
|
||||
static void renderColoredBorder(Uint8 color); // Dibuja el borde de color
|
||||
void initLineIndexArray(); // Inicializa el array de índices de líneas
|
||||
void printProgramName(); // Escribe el nombre del programa
|
||||
void updateCarrier(float delta_time); // Actualiza la portadora
|
||||
void updateSilent(float delta_time); // Actualiza el ruido durante el tiempo de silencio
|
||||
|
||||
// --- Variables miembro ---
|
||||
// Objetos y punteros a recursos
|
||||
std::shared_ptr<Surface> mono_loading_screen_surface_; // Surface con la pantalla de carga en blanco y negro
|
||||
std::shared_ptr<Surface> color_loading_screen_surface_; // Surface con la pantalla de carga en color
|
||||
std::unique_ptr<Sprite> mono_loading_screen_sprite_; // SurfaceSprite para manejar la textura mono_loading_screen_surface_
|
||||
std::unique_ptr<Sprite> color_loading_screen_sprite_; // SurfaceSprite para manejar la textura color_loading_screen_surface_
|
||||
std::unique_ptr<Sprite> program_sprite_; // SurfaceSprite para manejar la textura con el nombre del programa
|
||||
std::shared_ptr<Surface> screen_surface_; // Surface para dibujar la pantalla de carga
|
||||
std::unique_ptr<DeltaTimer> delta_timer_; // Timer para delta time
|
||||
|
||||
// Variables de estado de la secuencia
|
||||
State state_{State::SILENT1}; // Estado actual de la secuencia
|
||||
float state_time_{0.0F}; // Tiempo acumulado en el estado actual
|
||||
Border current_border_type_{Border::NONE}; // Tipo de borde actual
|
||||
|
||||
// Arrays y estructuras auxiliares
|
||||
std::array<int, MONO_TOTAL_LINES> line_index_; // El orden en el que se procesan las 192 líneas de la pantalla de carga
|
||||
SDL_FRect load_rect_{.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 1.0F}; // Rectángulo para dibujar la pantalla de carga
|
||||
Carrier carrier_; // Estructura para los efectos de la carga de cabeceras
|
||||
Noise noise_; // Variaciones de ruido durante los silencios
|
||||
|
||||
// Variables de seguimiento para evitar saltos de pasos/bloques
|
||||
int last_mono_step_{-1}; // Último paso mono dibujado
|
||||
int last_color_block_{-1}; // Último bloque color dibujado
|
||||
};
|
||||
@@ -238,19 +238,7 @@ void Logo::run() {
|
||||
|
||||
// Termina la sección
|
||||
void Logo::endSection() {
|
||||
switch (SceneManager::options) {
|
||||
case SceneManager::Options::LOGO_TO_TITLE:
|
||||
SceneManager::current = SceneManager::Scene::TITLE;
|
||||
break;
|
||||
|
||||
case SceneManager::Options::LOGO_TO_LOADING_SCREEN:
|
||||
SceneManager::current = SceneManager::Scene::LOADING_SCREEN;
|
||||
break;
|
||||
|
||||
default:
|
||||
SceneManager::current = SceneManager::Scene::LOADING_SCREEN;
|
||||
break;
|
||||
}
|
||||
SceneManager::current = SceneManager::Scene::TITLE;
|
||||
}
|
||||
|
||||
// Inicializa el vector de colores
|
||||
|
||||
@@ -26,25 +26,21 @@
|
||||
Title::Title()
|
||||
: game_logo_surface_(Resource::Cache::get()->getSurface("title_logo.gif")),
|
||||
game_logo_sprite_(std::make_unique<Sprite>(game_logo_surface_, 29, 9, game_logo_surface_->getWidth(), game_logo_surface_->getHeight())),
|
||||
loading_screen_surface_(Resource::Cache::get()->getSurface("loading_screen_color.gif")),
|
||||
loading_screen_sprite_(std::make_unique<Sprite>(loading_screen_surface_, 0, 0, loading_screen_surface_->getWidth(), loading_screen_surface_->getHeight())),
|
||||
title_surface_(std::make_shared<Surface>(Options::game.width, Options::game.height)),
|
||||
delta_timer_(std::make_unique<DeltaTimer>()),
|
||||
marquee_text_(Resource::Cache::get()->getText("gauntlet")),
|
||||
menu_text_(Resource::Cache::get()->getText("gauntlet")) {
|
||||
// Inicializa arrays con valores por defecto
|
||||
temp_keys_.fill(SDL_SCANCODE_UNKNOWN);
|
||||
temp_buttons_.fill(-1);
|
||||
|
||||
// Determina el estado inicial basado en opciones
|
||||
state_ = SceneManager::options == SceneManager::Options::TITLE_WITH_LOADING_SCREEN ? State::SHOW_LOADING_SCREEN : State::MAIN_MENU;
|
||||
// Estado inicial: menú principal
|
||||
state_ = State::MAIN_MENU;
|
||||
|
||||
// Establece SceneManager
|
||||
SceneManager::current = SceneManager::Scene::TITLE;
|
||||
SceneManager::options = SceneManager::Options::NONE;
|
||||
|
||||
// Acciones iniciales
|
||||
initMarquee(); // Inicializa la marquesina
|
||||
createCheevosTexture(); // Crea y rellena la textura para mostrar los logros
|
||||
Screen::get()->setBorderColor(0); // Cambia el color del borde
|
||||
Audio::get()->playMusic("574071_EA_DTV.ogg"); // Inicia la musica
|
||||
@@ -52,35 +48,9 @@ Title::Title()
|
||||
|
||||
// Destructor
|
||||
Title::~Title() { // NOLINT(modernize-use-equals-default)
|
||||
loading_screen_surface_->resetSubPalette();
|
||||
title_surface_->resetSubPalette();
|
||||
}
|
||||
|
||||
// Inicializa la marquesina
|
||||
void Title::initMarquee() {
|
||||
letters_.clear();
|
||||
long_text_ = Locale::get()->get("title.marquee");
|
||||
|
||||
// Pre-calcular anchos de caracteres para eficiencia (iteración por codepoints UTF-8)
|
||||
size_t pos = 0;
|
||||
while (pos < long_text_.size()) {
|
||||
uint32_t cp = Text::nextCodepoint(long_text_, pos);
|
||||
Glyph l;
|
||||
l.codepoint = cp;
|
||||
l.clip = marquee_text_->getGlyphClip(cp); // Pre-calcular clip rect (evita búsqueda por frame)
|
||||
l.x = MARQUEE_START_X;
|
||||
l.width = static_cast<float>(marquee_text_->glyphWidth(cp, 0)); // Pre-calcular ancho visual del glifo
|
||||
l.enabled = false;
|
||||
letters_.push_back(l);
|
||||
}
|
||||
|
||||
if (!letters_.empty()) {
|
||||
letters_[0].enabled = true;
|
||||
}
|
||||
first_active_letter_ = 0;
|
||||
last_active_letter_ = 0;
|
||||
}
|
||||
|
||||
// Comprueba el manejador de eventos
|
||||
void Title::handleEvents() {
|
||||
SDL_Event event;
|
||||
@@ -170,12 +140,6 @@ void Title::handleInput(float delta_time) {
|
||||
}
|
||||
|
||||
switch (state_) {
|
||||
case State::SHOW_LOADING_SCREEN:
|
||||
if (Input::get()->checkAction(InputAction::ACCEPT, Input::DO_NOT_ALLOW_REPEAT)) {
|
||||
transitionToState(State::FADE_LOADING_SCREEN);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::CHEEVOS_MENU:
|
||||
if (Input::get()->checkAction(InputAction::ACCEPT, Input::DO_NOT_ALLOW_REPEAT) ||
|
||||
Input::get()->checkAction(InputAction::CANCEL, Input::DO_NOT_ALLOW_REPEAT)) {
|
||||
@@ -191,53 +155,6 @@ void Title::handleInput(float delta_time) {
|
||||
GlobalInputs::handle();
|
||||
}
|
||||
|
||||
// Actualiza la marquesina
|
||||
void Title::updateMarquee(float delta_time) {
|
||||
const float DISPLACEMENT = MARQUEE_SPEED * delta_time;
|
||||
|
||||
// Solo procesar letras en rango activo + 1 para poder activar la siguiente
|
||||
for (int i = first_active_letter_; i <= last_active_letter_ + 1 && i < (int)letters_.size(); ++i) {
|
||||
auto& letter = letters_[i];
|
||||
|
||||
if (letter.enabled) {
|
||||
letter.x -= DISPLACEMENT;
|
||||
|
||||
// Desactivar si sale de pantalla
|
||||
if (letter.x < MARQUEE_EXIT_X) {
|
||||
letter.enabled = false;
|
||||
if (i == first_active_letter_) {
|
||||
first_active_letter_++; // Avanzar inicio del rango
|
||||
}
|
||||
}
|
||||
} else if (i > 0 && letters_[i - 1].x < MARQUEE_START_X && letters_[i - 1].enabled) {
|
||||
// Activar siguiente letra usando ancho pre-calculado
|
||||
letter.enabled = true;
|
||||
letter.x = letters_[i - 1].x + letters_[i - 1].width + MARQUEE_LETTER_SPACING;
|
||||
last_active_letter_ = i; // Expandir fin del rango
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba si ha terminado la marquesina y la reinicia
|
||||
if (letters_[letters_.size() - 1].x < MARQUEE_EXIT_X) {
|
||||
initMarquee();
|
||||
}
|
||||
}
|
||||
|
||||
// Dibuja la marquesina
|
||||
void Title::renderMarquee() const {
|
||||
auto* sprite = marquee_text_->getSprite();
|
||||
sprite->setY(MARQUEE_Y);
|
||||
// Solo renderizar letras activas (optimización: usa cache y rangos)
|
||||
for (int i = first_active_letter_; i <= last_active_letter_ + 1 && i < (int)letters_.size(); ++i) {
|
||||
const auto& letter = letters_[i];
|
||||
if (letter.enabled && letter.clip.w > 0.0F) {
|
||||
sprite->setClip(letter.clip);
|
||||
sprite->setX(letter.x);
|
||||
sprite->render(1, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza las variables
|
||||
void Title::update() {
|
||||
const float DELTA_TIME = delta_timer_->tick();
|
||||
@@ -254,14 +171,6 @@ void Title::update() {
|
||||
// Actualiza el estado actual
|
||||
void Title::updateState(float delta_time) {
|
||||
switch (state_) {
|
||||
case State::SHOW_LOADING_SCREEN:
|
||||
updateShowLoadingScreen(delta_time);
|
||||
break;
|
||||
|
||||
case State::FADE_LOADING_SCREEN:
|
||||
updateFadeLoadingScreen(delta_time);
|
||||
break;
|
||||
|
||||
case State::MAIN_MENU:
|
||||
updateMainMenu(delta_time);
|
||||
break;
|
||||
@@ -290,30 +199,8 @@ void Title::transitionToState(State new_state) {
|
||||
fade_accumulator_ = 0.0F;
|
||||
}
|
||||
|
||||
// Actualiza el estado SHOW_LOADING_SCREEN
|
||||
void Title::updateShowLoadingScreen(float delta_time) {
|
||||
state_time_ += delta_time;
|
||||
if (state_time_ >= SHOW_LOADING_DURATION) {
|
||||
transitionToState(State::FADE_LOADING_SCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza el estado FADE_LOADING_SCREEN
|
||||
void Title::updateFadeLoadingScreen(float delta_time) {
|
||||
fade_accumulator_ += delta_time;
|
||||
if (fade_accumulator_ >= FADE_STEP_INTERVAL) {
|
||||
fade_accumulator_ = 0.0F;
|
||||
if (loading_screen_surface_->fadeSubPalette()) {
|
||||
transitionToState(State::MAIN_MENU);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza el estado MAIN_MENU
|
||||
void Title::updateMainMenu(float delta_time) {
|
||||
// Actualiza la marquesina
|
||||
updateMarquee(delta_time);
|
||||
|
||||
// Si estamos en modo remap, manejar la lógica específica
|
||||
if (is_remapping_keyboard_ || is_remapping_joystick_) {
|
||||
// Decrementar cooldown de ejes si estamos capturando botones de joystick
|
||||
@@ -341,19 +228,11 @@ void Title::updateMainMenu(float delta_time) {
|
||||
} else {
|
||||
// Incrementa el temporizador solo en el menú principal normal
|
||||
state_time_ += delta_time;
|
||||
|
||||
// Si el tiempo alcanza el timeout, va a créditos con fade
|
||||
if (state_time_ >= MAIN_MENU_IDLE_TIMEOUT) {
|
||||
exit_scene_ = SceneManager::Scene::CREDITS;
|
||||
transitionToState(State::FADE_MENU);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza el estado CHEEVOS_MENU
|
||||
void Title::updateCheevosMenu(float delta_time) {
|
||||
// Actualiza la marquesina (sigue visible en fondo)
|
||||
updateMarquee(delta_time);
|
||||
|
||||
// Determina la velocidad objetivo basada en el input
|
||||
float target_velocity = 0.0F;
|
||||
@@ -405,8 +284,6 @@ void Title::updateFadeMenu(float delta_time) {
|
||||
transitionToState(State::POST_FADE_MENU);
|
||||
}
|
||||
}
|
||||
// Actualiza la marquesina (sigue visible en fondo)
|
||||
updateMarquee(delta_time);
|
||||
}
|
||||
|
||||
// Actualiza el estado POST_FADE_MENU
|
||||
@@ -561,19 +438,13 @@ void Title::fillTitleSurface() {
|
||||
case State::FADE_MENU:
|
||||
renderGameLogo();
|
||||
renderMainMenu();
|
||||
renderMarquee();
|
||||
|
||||
break;
|
||||
|
||||
case State::CHEEVOS_MENU:
|
||||
renderGameLogo();
|
||||
renderCheevosMenu();
|
||||
renderMarquee();
|
||||
break;
|
||||
|
||||
case State::SHOW_LOADING_SCREEN:
|
||||
case State::FADE_LOADING_SCREEN:
|
||||
loading_screen_sprite_->render();
|
||||
renderGameLogo();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <array> // Para std::array
|
||||
#include <memory> // Para shared_ptr
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "game/scene_manager.hpp" // Para SceneManager::Scene
|
||||
#include "utils/delta_timer.hpp" // Para DeltaTimer
|
||||
class Sprite; // Forward declaration
|
||||
@@ -24,17 +22,7 @@ class Title {
|
||||
|
||||
private:
|
||||
// --- Estructuras y enumeraciones ---
|
||||
struct Glyph {
|
||||
uint32_t codepoint{0}; // Codepoint Unicode del carácter
|
||||
SDL_FRect clip{}; // Clip rect pre-calculado en el bitmap de fuente
|
||||
float x{0.0F}; // Posición en el eje x (float para precisión con delta time)
|
||||
float width{0.0F}; // Ancho pre-calculado del carácter
|
||||
bool enabled{false}; // Solo se escriben y mueven si estan habilitadas
|
||||
};
|
||||
|
||||
enum class State {
|
||||
SHOW_LOADING_SCREEN,
|
||||
FADE_LOADING_SCREEN,
|
||||
MAIN_MENU,
|
||||
CHEEVOS_MENU,
|
||||
FADE_MENU,
|
||||
@@ -42,22 +30,13 @@ class Title {
|
||||
};
|
||||
|
||||
// --- Constantes de tiempo (en segundos) ---
|
||||
static constexpr float SHOW_LOADING_DURATION = 5.0F; // Tiempo mostrando loading screen (antes 500 frames)
|
||||
static constexpr float FADE_STEP_INTERVAL = 0.05F; // Intervalo entre pasos de fade (antes cada 4 frames)
|
||||
static constexpr float POST_FADE_DELAY = 1.0F; // Delay después del fade (pantalla en negro)
|
||||
static constexpr float MAIN_MENU_IDLE_TIMEOUT = 20.0F; // Timeout para ir a créditos (antes 2200 frames)
|
||||
static constexpr float KEYBOARD_REMAP_DISPLAY_DELAY = 2.0F; // Tiempo mostrando teclas definidas antes de guardar
|
||||
static constexpr float MARQUEE_SPEED = 100.0F; // Velocidad de marquesina (pixels/segundo)
|
||||
static constexpr float CHEEVOS_SCROLL_MAX_SPEED = 180.0F; // Velocidad máxima de scroll de logros (pixels/segundo)
|
||||
static constexpr float CHEEVOS_SCROLL_ACCELERATION = 600.0F; // Aceleración del scroll (pixels/segundo²)
|
||||
static constexpr float CHEEVOS_SCROLL_DECELERATION = 800.0F; // Desaceleración del scroll (pixels/segundo²)
|
||||
|
||||
// --- Constantes de marquesina ---
|
||||
static constexpr float MARQUEE_START_X = 256.0F; // Posición inicial (ancho pantalla)
|
||||
static constexpr float MARQUEE_EXIT_X = -10.0F; // Cuando desaparece de pantalla
|
||||
static constexpr float MARQUEE_Y = 184.0F; // Posición Y
|
||||
static constexpr float MARQUEE_LETTER_SPACING = 1.0F; // Espaciado entre letras
|
||||
|
||||
// --- Métodos ---
|
||||
void update(); // Actualiza las variables
|
||||
void render(); // Dibuja en pantalla
|
||||
@@ -66,15 +45,10 @@ class Title {
|
||||
void handleInput(float delta_time); // Comprueba las entradas
|
||||
void updateState(float delta_time); // Actualiza el estado actual
|
||||
void transitionToState(State new_state); // Transiciona a un nuevo estado
|
||||
void updateShowLoadingScreen(float delta_time); // Actualiza SHOW_LOADING_SCREEN
|
||||
void updateFadeLoadingScreen(float delta_time); // Actualiza FADE_LOADING_SCREEN
|
||||
void updateMainMenu(float delta_time); // Actualiza MAIN_MENU
|
||||
void updateCheevosMenu(float delta_time); // Actualiza CHEEVOS_MENU
|
||||
void updateFadeMenu(float delta_time); // Actualiza FADE_MENU
|
||||
void updatePostFadeMenu(float delta_time); // Actualiza POST_FADE_MENU
|
||||
void initMarquee(); // Inicializa la marquesina
|
||||
void updateMarquee(float delta_time); // Actualiza la marquesina (time-based)
|
||||
void renderMarquee() const; // Dibuja la marquesina
|
||||
void renderGameLogo(); // Dibuja el logo con el titulo del juego
|
||||
void renderMainMenu(); // Dibuja el menu principal
|
||||
void renderCheevosMenu(); // Dibuja el menu de logros
|
||||
@@ -97,21 +71,12 @@ class Title {
|
||||
// Objetos y punteros
|
||||
std::shared_ptr<Surface> game_logo_surface_; // Textura con los graficos
|
||||
std::unique_ptr<Sprite> game_logo_sprite_; // SSprite para manejar la surface
|
||||
std::shared_ptr<Surface> loading_screen_surface_; // Surface con los gráficos de la pantalla de carga
|
||||
std::unique_ptr<Sprite> loading_screen_sprite_; // SSprite con los gráficos de la pantalla de carga
|
||||
std::shared_ptr<Surface> cheevos_surface_; // Textura con la lista de logros
|
||||
std::unique_ptr<Sprite> cheevos_sprite_; // SSprite para manejar la surface con la lista de logros
|
||||
std::shared_ptr<Surface> title_surface_; // Surface donde se dibuja toda la clase
|
||||
std::unique_ptr<DeltaTimer> delta_timer_; // Timer para delta time
|
||||
std::shared_ptr<Text> marquee_text_; // Texto para marquesina
|
||||
std::shared_ptr<Text> menu_text_; // Texto para los menus
|
||||
|
||||
// Variables de estado de marquesina
|
||||
std::string long_text_; // Texto que aparece en la parte inferior del titulo
|
||||
std::vector<Glyph> letters_; // Vector con las letras de la marquesina
|
||||
int first_active_letter_{0}; // Primera letra activa (optimización)
|
||||
int last_active_letter_{0}; // Última letra activa (optimización)
|
||||
|
||||
// Variables de estado del menú de logros
|
||||
SDL_FRect cheevos_surface_view_; // Zona visible de la surface con el listado de logros
|
||||
float cheevos_scroll_velocity_{0.0F}; // Velocidad actual del scroll de logros (pixels/segundo)
|
||||
|
||||
Reference in New Issue
Block a user