forked from jaildesigner-jailgames/jaildoctors_dilemma
413 lines
14 KiB
C++
413 lines
14 KiB
C++
#include "game/scenes/loading_screen.hpp"
|
|
|
|
#include <SDL3/SDL.h>
|
|
|
|
#include <cstdlib> // Para rand
|
|
|
|
#include "core/audio/audio.hpp" // Para Audio
|
|
#include "core/input/global_inputs.hpp" // Para check
|
|
#include "core/rendering/screen.hpp" // Para Screen
|
|
#include "core/rendering/surface.hpp" // Para Surface
|
|
#include "core/rendering/surface_sprite.hpp" // Para SSprite
|
|
#include "core/resources/resource.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" // Para stringToColor, PaletteColor
|
|
|
|
// Constructor
|
|
LoadingScreen::LoadingScreen()
|
|
: mono_loading_screen_surface_(Resource::get()->getSurface("loading_screen_bn.gif")),
|
|
color_loading_screen_surface_(Resource::get()->getSurface("loading_screen_color.gif")),
|
|
mono_loading_screen_sprite_(std::make_shared<SurfaceSprite>(mono_loading_screen_surface_, 0, 0, mono_loading_screen_surface_->getWidth(), mono_loading_screen_surface_->getHeight())),
|
|
color_loading_screen_sprite_(std::make_shared<SurfaceSprite>(color_loading_screen_surface_, 0, 0, color_loading_screen_surface_->getWidth(), color_loading_screen_surface_->getHeight())),
|
|
screen_surface_(std::make_shared<Surface>(Options::game.width, Options::game.height)),
|
|
delta_timer_(std::make_unique<DeltaTimer>()),
|
|
state_(LoadingState::SILENT1),
|
|
state_time_(0.0F),
|
|
current_border_type_(BorderType::NONE),
|
|
load_rect_{0, 0, 0, 1.0F} {
|
|
// Configura la superficie donde se van a pintar los sprites
|
|
screen_surface_->clear(static_cast<Uint8>(PaletteColor::WHITE));
|
|
|
|
// Inicializa variables
|
|
SceneManager::current = SceneManager::Scene::LOADING_SCREEN;
|
|
SceneManager::options = SceneManager::Options::NONE;
|
|
|
|
// Inicializa el array de índices de líneas
|
|
initLineIndexArray();
|
|
|
|
// Cambia el color del borde
|
|
Screen::get()->setBorderColor(stringToColor("white"));
|
|
transitionToState(LoadingState::SILENT1);
|
|
}
|
|
|
|
// Destructor
|
|
LoadingScreen::~LoadingScreen() {
|
|
Audio::get()->stopMusic();
|
|
}
|
|
|
|
// Comprueba el manejador de eventos
|
|
void LoadingScreen::checkEvents() {
|
|
SDL_Event event;
|
|
while (SDL_PollEvent(&event)) {
|
|
GlobalEvents::check(event);
|
|
}
|
|
}
|
|
|
|
// Comprueba las entradas
|
|
void LoadingScreen::checkInput() {
|
|
GlobalInputs::check();
|
|
}
|
|
|
|
// Inicializa el array de índices de líneas (imita el direccionamiento de memoria del Spectrum)
|
|
void LoadingScreen::initLineIndexArray() {
|
|
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(LoadingState new_state) {
|
|
state_ = new_state;
|
|
state_time_ = 0.0F;
|
|
|
|
// Acciones específicas al entrar en cada estado
|
|
switch (new_state) {
|
|
case LoadingState::SILENT1:
|
|
case LoadingState::SILENT2:
|
|
current_border_type_ = BorderType::WHITE;
|
|
Audio::get()->stopMusic();
|
|
break;
|
|
|
|
case LoadingState::HEADER1:
|
|
case LoadingState::HEADER2:
|
|
current_border_type_ = BorderType::RED;
|
|
// Reproducir sonido de cargar el header
|
|
Audio::get()->playMusic("loading_sound1.ogg");
|
|
break;
|
|
|
|
case LoadingState::BYTES1:
|
|
case LoadingState::BYTES2:
|
|
case LoadingState::LOADING_MONO:
|
|
current_border_type_ = BorderType::YELLOW;
|
|
// Reproducir sonido de carga monocromática
|
|
Audio::get()->playMusic("loading_sound2.ogg");
|
|
break;
|
|
|
|
case LoadingState::LOADING_COLOR:
|
|
current_border_type_ = BorderType::YELLOW;
|
|
// Reproducir sonido de carga en color
|
|
Audio::get()->playMusic("loading_sound3.ogg");
|
|
break;
|
|
|
|
case LoadingState::COMPLETE:
|
|
current_border_type_ = BorderType::WHITE;
|
|
// 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 LoadingState::SILENT1:
|
|
if (state_time_ >= SILENT1_DURATION) {
|
|
transitionToState(LoadingState::HEADER1);
|
|
}
|
|
break;
|
|
|
|
case LoadingState::HEADER1:
|
|
if (state_time_ >= HEADER1_DURATION) {
|
|
transitionToState(LoadingState::BYTES1);
|
|
}
|
|
break;
|
|
|
|
case LoadingState::BYTES1:
|
|
if (state_time_ >= BYTES1_DURATION) {
|
|
transitionToState(LoadingState::SILENT2);
|
|
}
|
|
break;
|
|
|
|
case LoadingState::SILENT2:
|
|
if (state_time_ >= SILENT2_DURATION) {
|
|
transitionToState(LoadingState::HEADER2);
|
|
}
|
|
break;
|
|
|
|
case LoadingState::HEADER2:
|
|
if (state_time_ >= HEADER2_DURATION) {
|
|
transitionToState(LoadingState::LOADING_MONO);
|
|
}
|
|
break;
|
|
|
|
case LoadingState::BYTES2:
|
|
if (state_time_ >= BYTES2_DURATION) {
|
|
transitionToState(LoadingState::COMPLETE);
|
|
}
|
|
break;
|
|
|
|
case LoadingState::LOADING_MONO:
|
|
case LoadingState::LOADING_COLOR:
|
|
case LoadingState::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);
|
|
|
|
// Calcular línea y sub-paso
|
|
const int CURRENT_LINE = CURRENT_STEP / MONO_STEPS_PER_LINE; // 0-191
|
|
const int CURRENT_SUBSTEP = CURRENT_STEP % MONO_STEPS_PER_LINE; // 0-4
|
|
|
|
// Verificar si ha completado todas las líneas
|
|
if (CURRENT_LINE >= MONO_TOTAL_LINES) {
|
|
transitionToState(LoadingState::LOADING_COLOR);
|
|
return;
|
|
}
|
|
|
|
// Calcular rectángulo de clip (con floats para mayor precisión)
|
|
const float TEXTURE_WIDTH = mono_loading_screen_surface_->getWidth();
|
|
const float CLIP_WIDTH = TEXTURE_WIDTH / MONO_STEPS_PER_LINE;
|
|
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_);
|
|
|
|
auto previous_renderer = Screen::get()->getRendererSurface();
|
|
Screen::get()->setRendererSurface(screen_surface_);
|
|
mono_loading_screen_sprite_->render(1, stringToColor("black"));
|
|
Screen::get()->setRendererSurface(previous_renderer);
|
|
}
|
|
|
|
// 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 iteración actual (el código original incrementaba de 2 en 2)
|
|
const int TOTAL_ITERATIONS = COLOR_TOTAL_BLOCKS / 2; // 768 / 2 = 384 iteraciones
|
|
const int CURRENT_ITERATION = static_cast<int>(progress * TOTAL_ITERATIONS);
|
|
|
|
// Convertir a bloque (incrementa de 2 en 2, empezando en 0)
|
|
const int CURRENT_BLOCK = CURRENT_ITERATION * 2;
|
|
|
|
// Verificar si ha completado todos los bloques
|
|
if (CURRENT_BLOCK >= COLOR_TOTAL_BLOCKS) {
|
|
transitionToState(LoadingState::BYTES2);
|
|
return;
|
|
}
|
|
|
|
// Calcular posición del bloque
|
|
load_rect_.x = static_cast<float>((CURRENT_BLOCK * COLOR_BLOCK_SPACING) % 256);
|
|
load_rect_.y = static_cast<float>((CURRENT_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_);
|
|
|
|
auto previous_renderer = Screen::get()->getRendererSurface();
|
|
Screen::get()->setRendererSurface(screen_surface_);
|
|
color_loading_screen_sprite_->render();
|
|
Screen::get()->setRendererSurface(previous_renderer);
|
|
}
|
|
|
|
// Dibuja el efecto de carga amarillo y azul en el borde
|
|
void LoadingScreen::renderYellowBorder() {
|
|
// Obtiene la Surface del borde
|
|
auto border = Screen::get()->getBorderSurface();
|
|
|
|
// Pinta el borde de color azul
|
|
border->clear(static_cast<Uint8>(PaletteColor::BLUE));
|
|
|
|
// Añade lineas amarillas
|
|
const auto COLOR = static_cast<Uint8>(PaletteColor::YELLOW);
|
|
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::renderRedBorder() {
|
|
// Obtiene la Surface del borde
|
|
auto border = Screen::get()->getBorderSurface();
|
|
|
|
// Pinta el borde de color azul
|
|
border->clear(static_cast<Uint8>(PaletteColor::CYAN));
|
|
|
|
// Añade lineas rojas
|
|
const auto COLOR = static_cast<Uint8>(PaletteColor::RED);
|
|
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 = true;
|
|
|
|
// Primera linea (para que tenga poca variacion)
|
|
int row = 0;
|
|
const int FIRST_ROW_HEIGHT = (rand() % 4) + 3;
|
|
if (draw_enabled) {
|
|
for (int i = row; i < row + FIRST_ROW_HEIGHT; ++i) {
|
|
border->drawLine(0, i, WIDTH, i, COLOR);
|
|
}
|
|
}
|
|
row += FIRST_ROW_HEIGHT;
|
|
draw_enabled = !draw_enabled;
|
|
|
|
// Resto de lineas
|
|
while (row < HEIGHT) {
|
|
const int ROW_HEIGHT = (rand() % 3) + 8;
|
|
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 borde de color blanco
|
|
void LoadingScreen::renderWhiteBorder() {
|
|
// Obtiene la Surface del borde
|
|
auto border = Screen::get()->getBorderSurface();
|
|
|
|
// Pinta el borde de color azul
|
|
border->clear(static_cast<Uint8>(PaletteColor::WHITE));
|
|
}
|
|
|
|
// Actualiza las variables
|
|
void LoadingScreen::update() {
|
|
// Obtener delta time desde el último frame
|
|
const float DELTA_TIME = delta_timer_->tick();
|
|
|
|
checkInput(); // Comprueba las entradas
|
|
updateState(DELTA_TIME); // Actualiza el estado y gestiona transiciones
|
|
|
|
// Actualizar la carga según el estado actual
|
|
switch (state_) {
|
|
case LoadingState::SILENT1:
|
|
case LoadingState::HEADER1:
|
|
case LoadingState::BYTES1:
|
|
case LoadingState::SILENT2:
|
|
case LoadingState::HEADER2:
|
|
case LoadingState::BYTES2:
|
|
// Por ahora no hacen nada específico
|
|
// Tú definirás la lógica de cada estado aquí
|
|
break;
|
|
|
|
case LoadingState::LOADING_MONO:
|
|
updateMonoLoad(DELTA_TIME);
|
|
break;
|
|
|
|
case LoadingState::LOADING_COLOR:
|
|
updateColorLoad(DELTA_TIME);
|
|
break;
|
|
|
|
case LoadingState::COMPLETE:
|
|
// No hay más actualizaciones
|
|
break;
|
|
}
|
|
|
|
// Singletones
|
|
Audio::update(); // Actualiza el objeto Audio
|
|
Screen::get()->update(); // 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(stringToColor("white"));
|
|
|
|
// 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();
|
|
checkEvents();
|
|
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 BorderType::YELLOW:
|
|
renderYellowBorder();
|
|
break;
|
|
case BorderType::RED:
|
|
renderRedBorder();
|
|
break;
|
|
case BorderType::WHITE:
|
|
renderWhiteBorder();
|
|
break;
|
|
case BorderType::NONE:
|
|
// No renderizar borde
|
|
break;
|
|
}
|
|
}
|
|
} |