Compare commits
4 Commits
1a2298963d
...
8706b2c7fb
| Author | SHA1 | Date | |
|---|---|---|---|
| 8706b2c7fb | |||
| d493ebf4f0 | |||
| 3e795998d1 | |||
| c694781f38 |
@@ -104,6 +104,7 @@ set(APP_SOURCES
|
|||||||
source/game/scenes/instructions.cpp
|
source/game/scenes/instructions.cpp
|
||||||
source/game/scenes/intro.cpp
|
source/game/scenes/intro.cpp
|
||||||
source/game/scenes/logo.cpp
|
source/game/scenes/logo.cpp
|
||||||
|
source/game/scenes/preload.cpp
|
||||||
source/game/scenes/title.cpp
|
source/game/scenes/title.cpp
|
||||||
|
|
||||||
# --- game/ui ---
|
# --- game/ui ---
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -26,12 +26,12 @@ DIR_PACK_TOOL := $(DIR_TOOLS)pack_resources
|
|||||||
SHADER_SCRIPT := $(DIR_ROOT)tools/shaders/compile_spirv.sh
|
SHADER_SCRIPT := $(DIR_ROOT)tools/shaders/compile_spirv.sh
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# VERSION (fecha actual)
|
# VERSION (extraída de defines.hpp)
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
VERSION := $(shell powershell -Command "Get-Date -Format 'yyyy-MM-dd'")
|
VERSION := $(shell powershell -Command "(Select-String -Path 'source/utils/defines.hpp' -Pattern 'constexpr const char\* VERSION = \"(.+?)\"').Matches.Groups[1].Value")
|
||||||
else
|
else
|
||||||
VERSION := $(shell date +%Y-%m-%d)
|
VERSION := $(shell grep 'constexpr const char\* VERSION' source/utils/defines.hpp | sed -E 's/.*VERSION = "([^"]+)".*/\1/')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
"[NOTIFICATIONS] DISCONNECTED": "desconectat",
|
"[NOTIFICATIONS] DISCONNECTED": "desconectat",
|
||||||
|
|
||||||
"[RESOURCE] LOADING": "Carregant",
|
"[RESOURCE] LOADING": "Carregant",
|
||||||
|
"[RESOURCE] PRESS_TO_CONTINUE": "Prem una tecla per continuar",
|
||||||
|
|
||||||
"[SERVICE_MENU] TITLE": "Menu de servei",
|
"[SERVICE_MENU] TITLE": "Menu de servei",
|
||||||
"[SERVICE_MENU] RESET": "Reiniciar",
|
"[SERVICE_MENU] RESET": "Reiniciar",
|
||||||
|
|||||||
@@ -71,6 +71,7 @@
|
|||||||
"[NOTIFICATIONS] DISCONNECTED": "disconnected",
|
"[NOTIFICATIONS] DISCONNECTED": "disconnected",
|
||||||
|
|
||||||
"[RESOURCE] LOADING": "Loading",
|
"[RESOURCE] LOADING": "Loading",
|
||||||
|
"[RESOURCE] PRESS_TO_CONTINUE": "Press any key to continue",
|
||||||
|
|
||||||
"[SERVICE_MENU] TITLE": "Service Menu",
|
"[SERVICE_MENU] TITLE": "Service Menu",
|
||||||
"[SERVICE_MENU] RESET": "Reset",
|
"[SERVICE_MENU] RESET": "Reset",
|
||||||
|
|||||||
@@ -71,6 +71,7 @@
|
|||||||
"[NOTIFICATIONS] DISCONNECTED": "desconectado",
|
"[NOTIFICATIONS] DISCONNECTED": "desconectado",
|
||||||
|
|
||||||
"[RESOURCE] LOADING": "Cargando",
|
"[RESOURCE] LOADING": "Cargando",
|
||||||
|
"[RESOURCE] PRESS_TO_CONTINUE": "Pulsa una tecla para continuar",
|
||||||
|
|
||||||
"[SERVICE_MENU] TITLE": "Menu de servicio",
|
"[SERVICE_MENU] TITLE": "Menu de servicio",
|
||||||
"[SERVICE_MENU] RESET": "Reiniciar",
|
"[SERVICE_MENU] RESET": "Reiniciar",
|
||||||
|
|||||||
@@ -303,20 +303,6 @@ void Input::addGamepadMappingsFromFile() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Input::discoverGamepads() {
|
|
||||||
// Enumera los gamepads ya conectados sin drenar la cola de eventos de SDL
|
|
||||||
// (necesario con SDL_MAIN_USE_CALLBACKS, que entrega los eventos por SDL_AppEvent).
|
|
||||||
int count = 0;
|
|
||||||
SDL_JoystickID* joysticks = SDL_GetGamepads(&count);
|
|
||||||
if (joysticks == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < count; ++i) {
|
|
||||||
addGamepad(joysticks[i]);
|
|
||||||
}
|
|
||||||
SDL_free(joysticks);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Input::initSDLGamePad() {
|
void Input::initSDLGamePad() {
|
||||||
if (SDL_WasInit(SDL_INIT_GAMEPAD) != 1) {
|
if (SDL_WasInit(SDL_INIT_GAMEPAD) != 1) {
|
||||||
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) {
|
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) {
|
||||||
@@ -324,7 +310,9 @@ void Input::initSDLGamePad() {
|
|||||||
} else {
|
} else {
|
||||||
addGamepadMappingsFromFile();
|
addGamepadMappingsFromFile();
|
||||||
loadGamepadConfigs();
|
loadGamepadConfigs();
|
||||||
discoverGamepads();
|
// Los mandos ya conectados llegan como SDL_EVENT_GAMEPAD_ADDED en el
|
||||||
|
// primer pase del pump de eventos (antes del primer SDL_AppIterate),
|
||||||
|
// por lo que no hace falta enumerarlos aquí a mano.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -444,6 +432,27 @@ void Input::applyGamepadConfig(std::shared_ptr<Gamepad> gamepad) {
|
|||||||
return config.path == gamepad->path;
|
return config.path == gamepad->path;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fallback por nombre: si el mismo dispositivo se enchufa a otro puerto, su
|
||||||
|
// path cambia pero el nombre suele mantenerse. Recuperamos su configuración
|
||||||
|
// y actualizamos el path guardado al actual. Solo se acepta un match cuyo
|
||||||
|
// path guardado NO corresponda a otro mando ya conectado, para no arruinar
|
||||||
|
// setups con varios mandos idénticos en puertos distintos (cabinet arcade).
|
||||||
|
if (config_it == gamepad_configs_.end() && !gamepad->name.empty()) {
|
||||||
|
auto is_path_active = [this](const std::string& query_path) -> bool {
|
||||||
|
return std::ranges::any_of(gamepads_, [&query_path](const std::shared_ptr<Gamepad>& g) -> bool {
|
||||||
|
return g && g->path == query_path;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
config_it = std::ranges::find_if(gamepad_configs_, [&gamepad, &is_path_active](const GamepadConfig& config) -> bool {
|
||||||
|
return config.name == gamepad->name && !is_path_active(config.path);
|
||||||
|
});
|
||||||
|
if (config_it != gamepad_configs_.end()) {
|
||||||
|
std::cout << "Gamepad '" << gamepad->name << "' found by name, refreshing path to: " << gamepad->path << '\n';
|
||||||
|
config_it->path = gamepad->path;
|
||||||
|
saveGamepadConfigs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (config_it != gamepad_configs_.end()) {
|
if (config_it != gamepad_configs_.end()) {
|
||||||
// Se encontró una configuración específica para este puerto/dispositivo. La aplicamos.
|
// Se encontró una configuración específica para este puerto/dispositivo. La aplicamos.
|
||||||
std::cout << "Applying custom config for gamepad at path: " << gamepad->path << '\n';
|
std::cout << "Applying custom config for gamepad at path: " << gamepad->path << '\n';
|
||||||
@@ -453,7 +462,6 @@ void Input::applyGamepadConfig(std::shared_ptr<Gamepad> gamepad) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Opcional: Podrías añadir un fallback para buscar por nombre si no se encuentra por ruta.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Input::saveGamepadConfigFromGamepad(std::shared_ptr<Gamepad> gamepad) {
|
void Input::saveGamepadConfigFromGamepad(std::shared_ptr<Gamepad> gamepad) {
|
||||||
|
|||||||
@@ -206,7 +206,6 @@ class Input {
|
|||||||
auto addGamepad(int device_index) -> std::string;
|
auto addGamepad(int device_index) -> std::string;
|
||||||
auto removeGamepad(SDL_JoystickID id) -> std::string;
|
auto removeGamepad(SDL_JoystickID id) -> std::string;
|
||||||
void addGamepadMappingsFromFile();
|
void addGamepadMappingsFromFile();
|
||||||
void discoverGamepads();
|
|
||||||
|
|
||||||
// --- Métodos para integración con GamepadConfigManager ---
|
// --- Métodos para integración con GamepadConfigManager ---
|
||||||
void loadGamepadConfigs();
|
void loadGamepadConfigs();
|
||||||
|
|||||||
@@ -18,7 +18,9 @@
|
|||||||
#include "core/rendering/text.hpp" // Para Text
|
#include "core/rendering/text.hpp" // Para Text
|
||||||
#include "core/resources/asset.hpp" // Para Asset
|
#include "core/resources/asset.hpp" // Para Asset
|
||||||
#include "core/resources/resource_helper.hpp" // Para loadFile
|
#include "core/resources/resource_helper.hpp" // Para loadFile
|
||||||
|
#include "game/options.hpp" // Para Options::loading
|
||||||
#include "utils/color.hpp" // Para Color, NO_COLOR_MOD
|
#include "utils/color.hpp" // Para Color, NO_COLOR_MOD
|
||||||
|
#include "utils/defines.hpp" // Para Texts::VERSION
|
||||||
#include "utils/param.hpp" // Para Param, param, ParamPlayer, ParamResource, ParamGame
|
#include "utils/param.hpp" // Para Param, param, ParamPlayer, ParamResource, ParamGame
|
||||||
#include "utils/utils.hpp" // Para getFileName
|
#include "utils/utils.hpp" // Para getFileName
|
||||||
#include "version.h" // Para APP_NAME, GIT_HASH
|
#include "version.h" // Para APP_NAME, GIT_HASH
|
||||||
@@ -65,7 +67,8 @@ Resource::Resource(LoadingMode mode)
|
|||||||
Screen::get()->show();
|
Screen::get()->show();
|
||||||
if (loading_mode_ == LoadingMode::PRELOAD) {
|
if (loading_mode_ == LoadingMode::PRELOAD) {
|
||||||
loading_text_ = Screen::get()->getText();
|
loading_text_ = Screen::get()->getText();
|
||||||
load();
|
// Ya NO llamamos load() aquí: Director bombea beginLoad() + loadStep()
|
||||||
|
// desde iterate() para mantener vivo el bucle SDL3 durante la carga.
|
||||||
} else {
|
} else {
|
||||||
// En modo lazy, cargamos lo mínimo indispensable
|
// En modo lazy, cargamos lo mínimo indispensable
|
||||||
initResourceLists();
|
initResourceLists();
|
||||||
@@ -411,31 +414,128 @@ void Resource::clear() {
|
|||||||
demos_.clear();
|
demos_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga todos los recursos del juego y muestra el progreso de carga
|
// Carga síncrona completa: usado por Resource::reload() (hot-reload en debug).
|
||||||
|
// En arranque normal la carga la bombea Director::iterate() vía loadStep().
|
||||||
void Resource::load() {
|
void Resource::load() {
|
||||||
// Prepara la gestión del progreso de carga
|
beginLoad();
|
||||||
|
while (!loadStep(INT_MAX)) {
|
||||||
|
// Presupuesto infinito: una sola pasada carga todo
|
||||||
|
}
|
||||||
|
Screen::get()->setVSync(saved_vsync_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepara el estado del cargador incremental. No carga nada todavía.
|
||||||
|
void Resource::beginLoad() {
|
||||||
calculateTotalResources();
|
calculateTotalResources();
|
||||||
initProgressBar();
|
initProgressBar();
|
||||||
|
|
||||||
// Muerstra la ventana y desactiva el sincronismo vertical
|
saved_vsync_ = Screen::getVSync();
|
||||||
auto* screen = Screen::get();
|
Screen::get()->setVSync(false); // Maximiza FPS durante el preload
|
||||||
auto vsync = Screen::getVSync();
|
|
||||||
screen->setVSync(false);
|
|
||||||
|
|
||||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** LOADING RESOURCES");
|
stage_ = LoadStage::SOUNDS;
|
||||||
loadSounds(); // Carga sonidos
|
stage_index_ = 0;
|
||||||
loadMusics(); // Carga músicas
|
}
|
||||||
loadTextures(); // Carga texturas
|
|
||||||
loadTextFiles(); // Carga ficheros de texto
|
|
||||||
loadAnimations(); // Carga animaciones
|
|
||||||
loadDemoData(); // Carga datos de demo
|
|
||||||
createText(); // Crea objetos de texto
|
|
||||||
createTextTextures(); // Crea texturas a partir de texto
|
|
||||||
createPlayerTextures(); // Crea las texturas de jugadores con todas sus variantes de paleta
|
|
||||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** RESOURCES LOADED");
|
|
||||||
|
|
||||||
// Restablece el sincronismo vertical a su valor original
|
auto Resource::isLoadDone() const -> bool {
|
||||||
screen->setVSync(vsync);
|
return stage_ == LoadStage::DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bombea la máquina de etapas hasta agotar el presupuesto de tiempo o completar la carga.
|
||||||
|
// Devuelve true cuando ya no queda nada por cargar.
|
||||||
|
auto Resource::loadStep(int budget_ms) -> bool {
|
||||||
|
if (stage_ == LoadStage::DONE) { return true; }
|
||||||
|
|
||||||
|
const Uint64 start_ns = SDL_GetTicksNS();
|
||||||
|
const Uint64 budget_ns = static_cast<Uint64>(budget_ms) * 1'000'000ULL;
|
||||||
|
|
||||||
|
while (stage_ != LoadStage::DONE) {
|
||||||
|
switch (stage_) {
|
||||||
|
case LoadStage::SOUNDS: {
|
||||||
|
auto list = Asset::get()->getListByType(Asset::Type::SOUND);
|
||||||
|
if (stage_index_ == 0) { sounds_.clear(); }
|
||||||
|
if (stage_index_ >= list.size()) {
|
||||||
|
stage_ = LoadStage::MUSICS;
|
||||||
|
stage_index_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loadOneSound(stage_index_++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LoadStage::MUSICS: {
|
||||||
|
auto list = Asset::get()->getListByType(Asset::Type::MUSIC);
|
||||||
|
if (stage_index_ == 0) { musics_.clear(); }
|
||||||
|
if (stage_index_ >= list.size()) {
|
||||||
|
stage_ = LoadStage::TEXTURES;
|
||||||
|
stage_index_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loadOneMusic(stage_index_++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LoadStage::TEXTURES: {
|
||||||
|
auto list = Asset::get()->getListByType(Asset::Type::BITMAP);
|
||||||
|
if (stage_index_ == 0) { textures_.clear(); }
|
||||||
|
if (stage_index_ >= list.size()) {
|
||||||
|
stage_ = LoadStage::TEXT_FILES;
|
||||||
|
stage_index_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loadOneTexture(stage_index_++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LoadStage::TEXT_FILES: {
|
||||||
|
auto list = Asset::get()->getListByType(Asset::Type::FONT);
|
||||||
|
if (stage_index_ == 0) { text_files_.clear(); }
|
||||||
|
if (stage_index_ >= list.size()) {
|
||||||
|
stage_ = LoadStage::ANIMATIONS;
|
||||||
|
stage_index_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loadOneTextFile(stage_index_++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LoadStage::ANIMATIONS: {
|
||||||
|
auto list = Asset::get()->getListByType(Asset::Type::ANIMATION);
|
||||||
|
if (stage_index_ == 0) { animations_.clear(); }
|
||||||
|
if (stage_index_ >= list.size()) {
|
||||||
|
stage_ = LoadStage::DEMO_DATA;
|
||||||
|
stage_index_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loadOneAnimation(stage_index_++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LoadStage::DEMO_DATA: {
|
||||||
|
auto list = Asset::get()->getListByType(Asset::Type::DEMODATA);
|
||||||
|
if (stage_index_ == 0) { demos_.clear(); }
|
||||||
|
if (stage_index_ >= list.size()) {
|
||||||
|
stage_ = LoadStage::CREATE_TEXT;
|
||||||
|
stage_index_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loadOneDemoData(stage_index_++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LoadStage::CREATE_TEXT:
|
||||||
|
createText();
|
||||||
|
stage_ = LoadStage::CREATE_TEXT_TEXTURES;
|
||||||
|
break;
|
||||||
|
case LoadStage::CREATE_TEXT_TEXTURES:
|
||||||
|
createTextTextures();
|
||||||
|
stage_ = LoadStage::CREATE_PLAYER_TEXTURES;
|
||||||
|
break;
|
||||||
|
case LoadStage::CREATE_PLAYER_TEXTURES:
|
||||||
|
createPlayerTextures();
|
||||||
|
stage_ = LoadStage::DONE;
|
||||||
|
break;
|
||||||
|
case LoadStage::DONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((SDL_GetTicksNS() - start_ns) >= budget_ns) { break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return stage_ == LoadStage::DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recarga todos los recursos (limpia y vuelve a cargar)
|
// Recarga todos los recursos (limpia y vuelve a cargar)
|
||||||
@@ -448,96 +548,78 @@ void Resource::reload() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga los sonidos del juego
|
// Carga un sonido concreto desde la lista de assets
|
||||||
void Resource::loadSounds() {
|
void Resource::loadOneSound(size_t idx) {
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::SOUND);
|
auto list = Asset::get()->getListByType(Asset::Type::SOUND);
|
||||||
sounds_.clear();
|
const auto& path = list[idx];
|
||||||
|
auto name = getFileName(path);
|
||||||
for (const auto& l : list) {
|
|
||||||
auto name = getFileName(l);
|
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
auto audio_data = loadAudioData(l);
|
auto audio_data = loadAudioData(path);
|
||||||
JA_Sound_t* sound = nullptr;
|
JA_Sound_t* sound = nullptr;
|
||||||
if (!audio_data.data.empty()) {
|
if (!audio_data.data.empty()) {
|
||||||
sound = JA_LoadSound(audio_data.data.data(), audio_data.data.size());
|
sound = JA_LoadSound(audio_data.data.data(), audio_data.data.size());
|
||||||
} else {
|
} else {
|
||||||
sound = JA_LoadSound(l.c_str());
|
sound = JA_LoadSound(path.c_str());
|
||||||
}
|
}
|
||||||
if (sound == nullptr) {
|
if (sound == nullptr) {
|
||||||
std::cout << "Sound load failed: " << name << '\n';
|
std::cout << "Sound load failed: " << name << '\n';
|
||||||
}
|
}
|
||||||
sounds_.emplace_back(name, sound);
|
sounds_.emplace_back(name, sound);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga las músicas del juego
|
// Carga una música concreta desde la lista de assets
|
||||||
void Resource::loadMusics() {
|
void Resource::loadOneMusic(size_t idx) {
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::MUSIC);
|
auto list = Asset::get()->getListByType(Asset::Type::MUSIC);
|
||||||
musics_.clear();
|
const auto& path = list[idx];
|
||||||
|
auto name = getFileName(path);
|
||||||
for (const auto& l : list) {
|
|
||||||
auto name = getFileName(l);
|
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
auto audio_data = loadAudioData(l);
|
auto audio_data = loadAudioData(path);
|
||||||
JA_Music_t* music = nullptr;
|
JA_Music_t* music = nullptr;
|
||||||
if (!audio_data.data.empty()) {
|
if (!audio_data.data.empty()) {
|
||||||
music = JA_LoadMusic(audio_data.data.data(), audio_data.data.size());
|
music = JA_LoadMusic(audio_data.data.data(), audio_data.data.size());
|
||||||
} else {
|
} else {
|
||||||
music = JA_LoadMusic(l.c_str());
|
music = JA_LoadMusic(path.c_str());
|
||||||
}
|
}
|
||||||
if (music == nullptr) {
|
if (music == nullptr) {
|
||||||
std::cout << "Music load failed: " << name << '\n';
|
std::cout << "Music load failed: " << name << '\n';
|
||||||
}
|
}
|
||||||
musics_.emplace_back(name, music);
|
musics_.emplace_back(name, music);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga las texturas del juego
|
// Carga una textura concreta desde la lista de assets
|
||||||
void Resource::loadTextures() {
|
void Resource::loadOneTexture(size_t idx) {
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::BITMAP);
|
auto list = Asset::get()->getListByType(Asset::Type::BITMAP);
|
||||||
textures_.clear();
|
const auto& path = list[idx];
|
||||||
|
auto name = getFileName(path);
|
||||||
for (const auto& l : list) {
|
|
||||||
auto name = getFileName(l);
|
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
textures_.emplace_back(name, std::make_shared<Texture>(Screen::get()->getRenderer(), l));
|
textures_.emplace_back(name, std::make_shared<Texture>(Screen::get()->getRenderer(), path));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga los ficheros de texto del juego
|
// Carga un fichero de texto concreto desde la lista de assets
|
||||||
void Resource::loadTextFiles() {
|
void Resource::loadOneTextFile(size_t idx) {
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::FONT);
|
auto list = Asset::get()->getListByType(Asset::Type::FONT);
|
||||||
text_files_.clear();
|
const auto& path = list[idx];
|
||||||
|
auto name = getFileName(path);
|
||||||
for (const auto& l : list) {
|
|
||||||
auto name = getFileName(l);
|
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
text_files_.emplace_back(name, Text::loadFile(l));
|
text_files_.emplace_back(name, Text::loadFile(path));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga las animaciones del juego
|
// Carga una animación concreta desde la lista de assets
|
||||||
void Resource::loadAnimations() {
|
void Resource::loadOneAnimation(size_t idx) {
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::ANIMATION);
|
auto list = Asset::get()->getListByType(Asset::Type::ANIMATION);
|
||||||
animations_.clear();
|
const auto& path = list[idx];
|
||||||
|
auto name = getFileName(path);
|
||||||
for (const auto& l : list) {
|
|
||||||
auto name = getFileName(l);
|
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
animations_.emplace_back(name, loadAnimationsFromFile(l));
|
animations_.emplace_back(name, loadAnimationsFromFile(path));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga los datos para el modo demostración
|
// Carga un fichero de datos de demo concreto desde la lista de assets
|
||||||
void Resource::loadDemoData() {
|
void Resource::loadOneDemoData(size_t idx) {
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::DEMODATA);
|
auto list = Asset::get()->getListByType(Asset::Type::DEMODATA);
|
||||||
demos_.clear();
|
const auto& path = list[idx];
|
||||||
|
auto name = getFileName(path);
|
||||||
for (const auto& l : list) {
|
|
||||||
auto name = getFileName(l);
|
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
demos_.emplace_back(loadDemoDataFromFile(l));
|
demos_.emplace_back(loadDemoDataFromFile(path));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crea las texturas de jugadores con todas sus variantes de paleta
|
// Crea las texturas de jugadores con todas sus variantes de paleta
|
||||||
@@ -754,6 +836,16 @@ void Resource::renderProgress() {
|
|||||||
screen->start();
|
screen->start();
|
||||||
screen->clean();
|
screen->clean();
|
||||||
|
|
||||||
|
// Si la pantalla de carga está desactivada, dejamos todo en negro.
|
||||||
|
// wait_for_input solo tiene efecto cuando la pantalla está visible.
|
||||||
|
if (!Options::loading.show) {
|
||||||
|
screen->coreRender();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estamos en la fase de espera explícita al usuario tras terminar la carga
|
||||||
|
const bool WAITING_FOR_INPUT = isLoadDone() && Options::loading.wait_for_input;
|
||||||
|
|
||||||
auto text_color = param.resource.color;
|
auto text_color = param.resource.color;
|
||||||
auto bar_color = param.resource.color.DARKEN(100);
|
auto bar_color = param.resource.color.DARKEN(100);
|
||||||
const auto TEXT_HEIGHT = loading_text_->getCharacterSize();
|
const auto TEXT_HEIGHT = loading_text_->getCharacterSize();
|
||||||
@@ -766,14 +858,20 @@ void Resource::renderProgress() {
|
|||||||
SDL_SetRenderDrawColor(renderer, bar_color.r, bar_color.g, bar_color.b, bar_color.a);
|
SDL_SetRenderDrawColor(renderer, bar_color.r, bar_color.g, bar_color.b, bar_color.a);
|
||||||
SDL_RenderRect(renderer, &loading_wired_rect_);
|
SDL_RenderRect(renderer, &loading_wired_rect_);
|
||||||
|
|
||||||
// Escribe el texto de carga encima de la barra
|
// Texto centrado sobre la barra: mientras carga, el nombre del recurso;
|
||||||
/*
|
// al terminar en modo wait_for_input, el prompt traducido.
|
||||||
loading_text_->writeColored(
|
const std::string OVER_BAR_TEXT = WAITING_FOR_INPUT
|
||||||
loading_wired_rect_.x,
|
? Lang::getText("[RESOURCE] PRESS_TO_CONTINUE")
|
||||||
loading_wired_rect_.y - 9,
|
: loading_resource_name_;
|
||||||
Lang::getText("[RESOURCE] LOADING") + " : " + loading_resource_name_,
|
if ((Options::loading.show_resource_name || WAITING_FOR_INPUT) && !OVER_BAR_TEXT.empty()) {
|
||||||
|
loading_text_->writeDX(
|
||||||
|
Text::CENTER | Text::COLOR,
|
||||||
|
loading_wired_rect_.x + (loading_wired_rect_.w / 2),
|
||||||
|
loading_wired_rect_.y - TEXT_HEIGHT - 2,
|
||||||
|
OVER_BAR_TEXT,
|
||||||
|
1,
|
||||||
text_color);
|
text_color);
|
||||||
*/
|
}
|
||||||
|
|
||||||
// Muestra nombre de la aplicación
|
// Muestra nombre de la aplicación
|
||||||
loading_text_->writeDX(
|
loading_text_->writeDX(
|
||||||
@@ -784,32 +882,15 @@ void Resource::renderProgress() {
|
|||||||
1,
|
1,
|
||||||
text_color);
|
text_color);
|
||||||
|
|
||||||
// Muestra la versión
|
// Muestra la versión y el hash del commit
|
||||||
loading_text_->writeDX(
|
loading_text_->writeDX(
|
||||||
Text::CENTER | Text::COLOR,
|
Text::CENTER | Text::COLOR,
|
||||||
param.game.game_area.center_x,
|
param.game.game_area.center_x,
|
||||||
param.game.game_area.center_y + TEXT_HEIGHT,
|
param.game.game_area.center_y + TEXT_HEIGHT,
|
||||||
"(" + std::string(Version::GIT_HASH) + ")",
|
"ver. " + std::string(Texts::VERSION) + " (" + std::string(Version::GIT_HASH) + ")",
|
||||||
1,
|
1,
|
||||||
text_color);
|
text_color);
|
||||||
|
|
||||||
// Muestra información del monitor desplazada hacia abajo
|
|
||||||
/*loading_text_->writeColored(
|
|
||||||
X_PADDING,
|
|
||||||
Y_PADDING + 18,
|
|
||||||
screen->getDisplayMonitorName(),
|
|
||||||
text_color);
|
|
||||||
loading_text_->writeColored(
|
|
||||||
X_PADDING,
|
|
||||||
Y_PADDING + 27,
|
|
||||||
std::to_string(screen->getDisplayMonitorWidth()) + "x" + std::to_string(screen->getDisplayMonitorHeight()),
|
|
||||||
text_color);
|
|
||||||
loading_text_->writeColored(
|
|
||||||
X_PADDING,
|
|
||||||
Y_PADDING + 36,
|
|
||||||
std::to_string(screen->getDisplayMonitorRefreshRate()) + "Hz",
|
|
||||||
text_color);*/
|
|
||||||
|
|
||||||
// Renderiza el frame en pantalla
|
// Renderiza el frame en pantalla
|
||||||
screen->coreRender();
|
screen->coreRender();
|
||||||
}
|
}
|
||||||
@@ -826,21 +907,22 @@ void Resource::loadDemoDataQuiet() {
|
|||||||
|
|
||||||
// Inicializa los rectangulos que definen la barra de progreso
|
// Inicializa los rectangulos que definen la barra de progreso
|
||||||
void Resource::initProgressBar() {
|
void Resource::initProgressBar() {
|
||||||
const float BAR_Y_POSITION = param.game.height - BAR_HEIGHT - Y_PADDING;
|
const float WIRED_BAR_WIDTH = param.game.width * BAR_WIDTH_RATIO;
|
||||||
|
const float BAR_X_POSITION = (param.game.width - WIRED_BAR_WIDTH) / 2.0F;
|
||||||
|
const float BAR_Y_POSITION = (param.game.height * BAR_Y_RATIO) - (BAR_HEIGHT / 2.0F);
|
||||||
|
|
||||||
const float WIRED_BAR_WIDTH = param.game.width - (X_PADDING * 2);
|
loading_wired_rect_ = {.x = BAR_X_POSITION, .y = BAR_Y_POSITION, .w = WIRED_BAR_WIDTH, .h = BAR_HEIGHT};
|
||||||
loading_wired_rect_ = {.x = X_PADDING, .y = BAR_Y_POSITION, .w = WIRED_BAR_WIDTH, .h = BAR_HEIGHT};
|
|
||||||
|
|
||||||
const float FULL_BAR_WIDTH = WIRED_BAR_WIDTH * loading_count_.getPercentage();
|
const float FULL_BAR_WIDTH = WIRED_BAR_WIDTH * loading_count_.getPercentage();
|
||||||
loading_full_rect_ = {.x = X_PADDING, .y = BAR_Y_POSITION, .w = FULL_BAR_WIDTH, .h = BAR_HEIGHT};
|
loading_full_rect_ = {.x = BAR_X_POSITION, .y = BAR_Y_POSITION, .w = FULL_BAR_WIDTH, .h = BAR_HEIGHT};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza el progreso de carga y muestra la barra
|
// Actualiza el estado del progreso. No renderiza: el repintado lo hace
|
||||||
|
// Preload::iterate una vez por frame llamando a renderProgress().
|
||||||
void Resource::updateLoadingProgress(std::string name) {
|
void Resource::updateLoadingProgress(std::string name) {
|
||||||
loading_resource_name_ = std::move(name);
|
loading_resource_name_ = std::move(name);
|
||||||
loading_count_.increase();
|
loading_count_.increase();
|
||||||
updateProgressBar();
|
updateProgressBar();
|
||||||
renderProgress();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza la barra de estado
|
// Actualiza la barra de estado
|
||||||
|
|||||||
@@ -42,6 +42,15 @@ class Resource {
|
|||||||
// --- Métodos de recarga de recursos ---
|
// --- Métodos de recarga de recursos ---
|
||||||
void reload(); // Recarga todos los recursos
|
void reload(); // Recarga todos los recursos
|
||||||
|
|
||||||
|
// --- Cargador incremental ---
|
||||||
|
// beginLoad prepara el estado; loadStep carga recursos hasta agotar el presupuesto;
|
||||||
|
// devuelve true cuando ya no queda nada. renderProgress se llama una vez por frame
|
||||||
|
// desde la escena Preload.
|
||||||
|
void beginLoad();
|
||||||
|
auto loadStep(int budget_ms) -> bool;
|
||||||
|
[[nodiscard]] auto isLoadDone() const -> bool;
|
||||||
|
void renderProgress();
|
||||||
|
|
||||||
// --- Método para obtener el modo de carga actual ---
|
// --- Método para obtener el modo de carga actual ---
|
||||||
[[nodiscard]] auto getLoadingMode() const -> LoadingMode { return loading_mode_; }
|
[[nodiscard]] auto getLoadingMode() const -> LoadingMode { return loading_mode_; }
|
||||||
|
|
||||||
@@ -117,12 +126,15 @@ class Resource {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Constantes para la pantalla de carga ---
|
// --- Constantes para la barra de progreso de la pantalla de carga ---
|
||||||
static constexpr float X_PADDING = 60.0F;
|
// BAR_WIDTH_RATIO: fracción del ancho de la pantalla ocupada por la barra (centrada)
|
||||||
static constexpr float Y_PADDING = 20.0F;
|
// 1.0 = ancho completo, 0.5 = la mitad del ancho
|
||||||
|
// BAR_Y_RATIO: posición vertical del CENTRO de la barra en fracción de la altura
|
||||||
|
// 0.0 = centro en el borde superior, 1.0 = centro en el borde inferior
|
||||||
|
// BAR_HEIGHT: grosor de la barra en píxeles
|
||||||
|
static constexpr float BAR_WIDTH_RATIO = 0.5F;
|
||||||
|
static constexpr float BAR_Y_RATIO = 0.85F;
|
||||||
static constexpr float BAR_HEIGHT = 5.0F;
|
static constexpr float BAR_HEIGHT = 5.0F;
|
||||||
static constexpr Color BAR_COLOR = Color(128, 128, 128);
|
|
||||||
static constexpr Color TEXT_COLOR = Color(255, 255, 255);
|
|
||||||
|
|
||||||
// --- Modo de carga ---
|
// --- Modo de carga ---
|
||||||
LoadingMode loading_mode_;
|
LoadingMode loading_mode_;
|
||||||
@@ -143,13 +155,24 @@ class Resource {
|
|||||||
SDL_FRect loading_wired_rect_;
|
SDL_FRect loading_wired_rect_;
|
||||||
SDL_FRect loading_full_rect_;
|
SDL_FRect loading_full_rect_;
|
||||||
|
|
||||||
|
// --- Estado del cargador incremental ---
|
||||||
|
enum class LoadStage {
|
||||||
|
SOUNDS,
|
||||||
|
MUSICS,
|
||||||
|
TEXTURES,
|
||||||
|
TEXT_FILES,
|
||||||
|
ANIMATIONS,
|
||||||
|
DEMO_DATA,
|
||||||
|
CREATE_TEXT,
|
||||||
|
CREATE_TEXT_TEXTURES,
|
||||||
|
CREATE_PLAYER_TEXTURES,
|
||||||
|
DONE
|
||||||
|
};
|
||||||
|
LoadStage stage_{LoadStage::DONE};
|
||||||
|
size_t stage_index_{0};
|
||||||
|
bool saved_vsync_{false}; // Vsync previo a beginLoad, restaurado por finishBoot/load
|
||||||
|
|
||||||
// --- Métodos internos de carga y gestión ---
|
// --- Métodos internos de carga y gestión ---
|
||||||
void loadSounds(); // Carga los sonidos
|
|
||||||
void loadMusics(); // Carga las músicas
|
|
||||||
void loadTextures(); // Carga las texturas
|
|
||||||
void loadTextFiles(); // Carga los ficheros de texto
|
|
||||||
void loadAnimations(); // Carga las animaciones
|
|
||||||
void loadDemoData(); // Carga los datos para el modo demostración
|
|
||||||
void loadDemoDataQuiet(); // Carga los datos de demo sin mostrar progreso (para modo lazy)
|
void loadDemoDataQuiet(); // Carga los datos de demo sin mostrar progreso (para modo lazy)
|
||||||
void loadEssentialResources(); // Carga recursos esenciales en modo lazy
|
void loadEssentialResources(); // Carga recursos esenciales en modo lazy
|
||||||
void loadEssentialTextures(); // Carga solo las texturas esenciales (fuentes)
|
void loadEssentialTextures(); // Carga solo las texturas esenciales (fuentes)
|
||||||
@@ -173,11 +196,18 @@ class Resource {
|
|||||||
|
|
||||||
// --- Métodos internos para gestionar el progreso ---
|
// --- Métodos internos para gestionar el progreso ---
|
||||||
void calculateTotalResources(); // Calcula el número de recursos para cargar
|
void calculateTotalResources(); // Calcula el número de recursos para cargar
|
||||||
void renderProgress(); // Muestra el progreso de carga
|
|
||||||
void updateLoadingProgress(std::string name); // Actualiza el progreso de carga
|
void updateLoadingProgress(std::string name); // Actualiza el progreso de carga
|
||||||
void initProgressBar(); // Inicializa los rectangulos que definen la barra de progreso
|
void initProgressBar(); // Inicializa los rectangulos que definen la barra de progreso
|
||||||
void updateProgressBar(); // Actualiza la barra de estado
|
void updateProgressBar(); // Actualiza la barra de estado
|
||||||
|
|
||||||
|
// --- Helpers del cargador incremental (cargan un único recurso) ---
|
||||||
|
void loadOneSound(size_t idx);
|
||||||
|
void loadOneMusic(size_t idx);
|
||||||
|
void loadOneTexture(size_t idx);
|
||||||
|
void loadOneTextFile(size_t idx);
|
||||||
|
void loadOneAnimation(size_t idx);
|
||||||
|
void loadOneDemoData(size_t idx);
|
||||||
|
|
||||||
// --- Constructores y destructor privados (singleton) ---
|
// --- Constructores y destructor privados (singleton) ---
|
||||||
explicit Resource(LoadingMode mode); // Constructor privado con modo de carga
|
explicit Resource(LoadingMode mode); // Constructor privado con modo de carga
|
||||||
~Resource(); // Destructor privado
|
~Resource(); // Destructor privado
|
||||||
|
|||||||
@@ -213,3 +213,9 @@ namespace Defaults::Settings {
|
|||||||
constexpr bool SHUTDOWN_ENABLED = false;
|
constexpr bool SHUTDOWN_ENABLED = false;
|
||||||
constexpr const char* PARAMS_FILE = "param_320x256.txt";
|
constexpr const char* PARAMS_FILE = "param_320x256.txt";
|
||||||
} // namespace Defaults::Settings
|
} // namespace Defaults::Settings
|
||||||
|
|
||||||
|
namespace Defaults::Loading {
|
||||||
|
constexpr bool SHOW = false;
|
||||||
|
constexpr bool SHOW_RESOURCE_NAME = true;
|
||||||
|
constexpr bool WAIT_FOR_INPUT = false;
|
||||||
|
} // namespace Defaults::Loading
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include "game/scenes/instructions.hpp" // Para Instructions
|
#include "game/scenes/instructions.hpp" // Para Instructions
|
||||||
#include "game/scenes/intro.hpp" // Para Intro
|
#include "game/scenes/intro.hpp" // Para Intro
|
||||||
#include "game/scenes/logo.hpp" // Para Logo
|
#include "game/scenes/logo.hpp" // Para Logo
|
||||||
|
#include "game/scenes/preload.hpp" // Para Preload
|
||||||
#include "game/scenes/title.hpp" // Para Title
|
#include "game/scenes/title.hpp" // Para Title
|
||||||
#include "game/ui/notifier.hpp" // Para Notifier
|
#include "game/ui/notifier.hpp" // Para Notifier
|
||||||
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
||||||
@@ -140,9 +141,40 @@ void Director::init() {
|
|||||||
#else
|
#else
|
||||||
Resource::init(Resource::LoadingMode::PRELOAD);
|
Resource::init(Resource::LoadingMode::PRELOAD);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (Resource::get()->getLoadingMode() == Resource::LoadingMode::PRELOAD) {
|
||||||
|
// Guarda la sección destino (la que fijó loadDebugConfig o el default)
|
||||||
|
// y redirige el arranque a la escena PRELOAD hasta que loadStep termine.
|
||||||
|
Section::post_preload = Section::name;
|
||||||
|
Section::name = Section::Name::PRELOAD;
|
||||||
|
Resource::get()->beginLoad();
|
||||||
|
} else {
|
||||||
|
// LAZY_LOAD: el constructor de Resource ya cargó lo esencial síncronamente;
|
||||||
|
// no hay fase de preload, pasamos directamente a post-boot.
|
||||||
|
finishBoot();
|
||||||
|
boot_loading_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceMenu/Notifier/getSingletons se mueven a finishBoot() — dependen
|
||||||
|
// de Resource y se inicializan al terminar la carga incremental.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicializaciones que dependen del Resource cargado. Se llama desde iterate()
|
||||||
|
// cuando Resource::loadStep() devuelve true, con la ventana y el bucle vivos.
|
||||||
|
void Director::finishBoot() {
|
||||||
ServiceMenu::init(); // Inicializa el menú de servicio
|
ServiceMenu::init(); // Inicializa el menú de servicio
|
||||||
Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones
|
Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones
|
||||||
Screen::get()->getSingletons(); // Obtiene los punteros al resto de singletones
|
Screen::get()->getSingletons(); // Obtiene los punteros al resto de singletones
|
||||||
|
|
||||||
|
// Restaura el vsync a la preferencia del usuario (beginLoad lo había puesto a false)
|
||||||
|
Screen::get()->setVSync(Options::video.vsync);
|
||||||
|
|
||||||
|
// Si NO estamos en modo "wait for input", transiciona ya al destino.
|
||||||
|
// Si wait_for_input está activo (y la pantalla es visible), nos quedamos
|
||||||
|
// en PRELOAD hasta que el usuario pulse tecla/botón.
|
||||||
|
if (!(Options::loading.show && Options::loading.wait_for_input)) {
|
||||||
|
Section::name = Section::post_preload;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cierra todo y libera recursos del sistema y de los singletons
|
// Cierra todo y libera recursos del sistema y de los singletons
|
||||||
@@ -327,6 +359,7 @@ void Director::createSystemFolder(const std::string& folder) {
|
|||||||
|
|
||||||
// Libera todos los unique_ptr de sección (solo uno tiene propiedad a la vez)
|
// Libera todos los unique_ptr de sección (solo uno tiene propiedad a la vez)
|
||||||
void Director::resetActiveSection() {
|
void Director::resetActiveSection() {
|
||||||
|
preload_.reset();
|
||||||
logo_.reset();
|
logo_.reset();
|
||||||
intro_.reset();
|
intro_.reset();
|
||||||
title_.reset();
|
title_.reset();
|
||||||
@@ -353,6 +386,10 @@ void Director::handleSectionTransition() {
|
|||||||
|
|
||||||
// Construye la nueva
|
// Construye la nueva
|
||||||
switch (Section::name) {
|
switch (Section::name) {
|
||||||
|
case Section::Name::PRELOAD:
|
||||||
|
preload_ = std::make_unique<Preload>();
|
||||||
|
break;
|
||||||
|
|
||||||
case Section::Name::LOGO:
|
case Section::Name::LOGO:
|
||||||
logo_ = std::make_unique<Logo>();
|
logo_ = std::make_unique<Logo>();
|
||||||
break;
|
break;
|
||||||
@@ -435,11 +472,32 @@ auto Director::iterate() -> SDL_AppResult {
|
|||||||
return SDL_APP_SUCCESS;
|
return SDL_APP_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fase de boot: carga incremental frame a frame con presupuesto de 50ms.
|
||||||
|
// Durante esta fase la escena activa es Preload (una barra de progreso).
|
||||||
|
if (boot_loading_) {
|
||||||
|
try {
|
||||||
|
if (Resource::get()->loadStep(50 /*ms*/)) {
|
||||||
|
finishBoot();
|
||||||
|
boot_loading_ = false;
|
||||||
|
// Los SDL_EVENT_GAMEPAD_ADDED iniciales ya los ha drenado la rama
|
||||||
|
// durante la carga: marcamos startup completo ahora para que los
|
||||||
|
// ADDED/REMOVED posteriores sí generen notificación.
|
||||||
|
GlobalEvents::markStartupComplete();
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Fatal error during resource load: " << e.what() << '\n';
|
||||||
|
Section::name = Section::Name::QUIT;
|
||||||
|
return SDL_APP_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Gestiona las transiciones entre secciones (destruye la anterior y construye la nueva)
|
// Gestiona las transiciones entre secciones (destruye la anterior y construye la nueva)
|
||||||
handleSectionTransition();
|
handleSectionTransition();
|
||||||
|
|
||||||
// Ejecuta un frame de la sección activa
|
// Ejecuta un frame de la sección activa
|
||||||
if (logo_) {
|
if (preload_) {
|
||||||
|
preload_->iterate();
|
||||||
|
} else if (logo_) {
|
||||||
logo_->iterate();
|
logo_->iterate();
|
||||||
} else if (intro_) {
|
} else if (intro_) {
|
||||||
intro_->iterate();
|
intro_->iterate();
|
||||||
@@ -464,7 +522,9 @@ auto Director::handleEvent(SDL_Event& event) -> SDL_AppResult {
|
|||||||
GlobalEvents::handle(event);
|
GlobalEvents::handle(event);
|
||||||
|
|
||||||
// Reenvía a la sección activa
|
// Reenvía a la sección activa
|
||||||
if (logo_) {
|
if (preload_) {
|
||||||
|
preload_->handleEvent(event);
|
||||||
|
} else if (logo_) {
|
||||||
logo_->handleEvent(event);
|
logo_->handleEvent(event);
|
||||||
} else if (intro_) {
|
} else if (intro_) {
|
||||||
intro_->handleEvent(event);
|
intro_->handleEvent(event);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace Lang {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Declaraciones adelantadas de las secciones
|
// Declaraciones adelantadas de las secciones
|
||||||
|
class Preload;
|
||||||
class Logo;
|
class Logo;
|
||||||
class Intro;
|
class Intro;
|
||||||
class Title;
|
class Title;
|
||||||
@@ -54,6 +55,7 @@ class Director {
|
|||||||
std::string system_folder_; // Carpeta del sistema para almacenar datos
|
std::string system_folder_; // Carpeta del sistema para almacenar datos
|
||||||
|
|
||||||
// --- Sección activa (una y sólo una viva en cada momento) ---
|
// --- Sección activa (una y sólo una viva en cada momento) ---
|
||||||
|
std::unique_ptr<Preload> preload_;
|
||||||
std::unique_ptr<Logo> logo_;
|
std::unique_ptr<Logo> logo_;
|
||||||
std::unique_ptr<Intro> intro_;
|
std::unique_ptr<Intro> intro_;
|
||||||
std::unique_ptr<Title> title_;
|
std::unique_ptr<Title> title_;
|
||||||
@@ -63,8 +65,12 @@ class Director {
|
|||||||
std::unique_ptr<Credits> credits_;
|
std::unique_ptr<Credits> credits_;
|
||||||
Section::Name last_built_section_name_ = Section::Name::RESET;
|
Section::Name last_built_section_name_ = Section::Name::RESET;
|
||||||
|
|
||||||
|
// --- Fase de arranque no bloqueante ---
|
||||||
|
bool boot_loading_ = true; // True mientras Resource::loadStep está cargando incremental
|
||||||
|
|
||||||
// --- Inicialización y cierre del sistema ---
|
// --- Inicialización y cierre del sistema ---
|
||||||
void init(); // Inicializa la aplicación
|
void init(); // Inicializa la aplicación (pre-boot)
|
||||||
|
void finishBoot(); // Post-boot: inicializa lo que depende de recursos cargados
|
||||||
static void close(); // Cierra y libera recursos
|
static void close(); // Cierra y libera recursos
|
||||||
|
|
||||||
// --- Configuración inicial ---
|
// --- Configuración inicial ---
|
||||||
|
|||||||
@@ -17,12 +17,32 @@
|
|||||||
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
||||||
|
|
||||||
namespace GlobalEvents {
|
namespace GlobalEvents {
|
||||||
|
namespace {
|
||||||
|
// Durante el arranque se drenan los SDL_EVENT_GAMEPAD_ADDED de los mandos
|
||||||
|
// ya conectados. Esos eventos sí deben reasignar mandos a jugadores, pero
|
||||||
|
// no deben mostrar notificación: no son hotplug, son detección inicial.
|
||||||
|
bool startup_in_progress = true;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// Comprueba los eventos de Input y muestra notificaciones
|
// Comprueba los eventos de Input y muestra notificaciones
|
||||||
void handleInputEvents(const SDL_Event& event) {
|
void handleInputEvents(const SDL_Event& event) {
|
||||||
|
if (event.type != SDL_EVENT_GAMEPAD_ADDED && event.type != SDL_EVENT_GAMEPAD_REMOVED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static auto* input_ = Input::get();
|
static auto* input_ = Input::get();
|
||||||
auto message = input_->handleEvent(event);
|
auto message = input_->handleEvent(event);
|
||||||
|
|
||||||
if (message.empty()) {
|
// Reasignar siempre: tanto en arranque como en hotplug en caliente.
|
||||||
|
Options::gamepad_manager.assignAndLinkGamepads();
|
||||||
|
Options::gamepad_manager.resyncGamepadsWithPlayers();
|
||||||
|
|
||||||
|
// Durante el preload ServiceMenu aún no existe: solo refresca si está vivo.
|
||||||
|
if (ServiceMenu::get() != nullptr) {
|
||||||
|
ServiceMenu::get()->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startup_in_progress || message.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,10 +55,15 @@ namespace GlobalEvents {
|
|||||||
message.replace(pos, std::string(" DISCONNECTED").length(), " " + Lang::getText("[NOTIFICATIONS] DISCONNECTED"));
|
message.replace(pos, std::string(" DISCONNECTED").length(), " " + Lang::getText("[NOTIFICATIONS] DISCONNECTED"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Options::gamepad_manager.assignAndLinkGamepads();
|
// Notifier también puede no existir todavía si la notificación se
|
||||||
Options::gamepad_manager.resyncGamepadsWithPlayers();
|
// disparase antes de finishBoot(). Protegido por si acaso.
|
||||||
|
if (Notifier::get() != nullptr) {
|
||||||
Notifier::get()->show({message});
|
Notifier::get()->show({message});
|
||||||
ServiceMenu::get()->refresh();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void markStartupComplete() {
|
||||||
|
startup_in_progress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba los eventos que se pueden producir en cualquier sección del juego
|
// Comprueba los eventos que se pueden producir en cualquier sección del juego
|
||||||
@@ -62,7 +87,10 @@ namespace GlobalEvents {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Durante el preload ServiceMenu aún no existe
|
||||||
|
if (ServiceMenu::get() != nullptr) {
|
||||||
ServiceMenu::get()->handleEvent(event);
|
ServiceMenu::get()->handleEvent(event);
|
||||||
|
}
|
||||||
Mouse::handleEvent(event);
|
Mouse::handleEvent(event);
|
||||||
handleInputEvents(event);
|
handleInputEvents(event);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,10 @@
|
|||||||
namespace GlobalEvents {
|
namespace GlobalEvents {
|
||||||
// --- Funciones ---
|
// --- Funciones ---
|
||||||
void handle(const SDL_Event& event); // Comprueba los eventos que se pueden producir en cualquier sección del juego
|
void handle(const SDL_Event& event); // Comprueba los eventos que se pueden producir en cualquier sección del juego
|
||||||
|
|
||||||
|
// Marca el fin del arranque: a partir de aquí, los eventos de mando
|
||||||
|
// generan notificaciones en pantalla. Se debe llamar desde Director::iterate
|
||||||
|
// en el primer fotograma, después de que SDL haya drenado los
|
||||||
|
// SDL_EVENT_GAMEPAD_ADDED de los mandos ya conectados al iniciar.
|
||||||
|
void markStartupComplete();
|
||||||
} // namespace GlobalEvents
|
} // namespace GlobalEvents
|
||||||
@@ -10,6 +10,7 @@ namespace Section {
|
|||||||
// --- Enumeraciones de secciones del programa ---
|
// --- Enumeraciones de secciones del programa ---
|
||||||
enum class Name {
|
enum class Name {
|
||||||
RESET, // Inicialización
|
RESET, // Inicialización
|
||||||
|
PRELOAD, // Carga incremental de recursos
|
||||||
LOGO, // Pantalla de logo
|
LOGO, // Pantalla de logo
|
||||||
INTRO, // Introducción
|
INTRO, // Introducción
|
||||||
TITLE, // Pantalla de título/menú principal
|
TITLE, // Pantalla de título/menú principal
|
||||||
@@ -43,6 +44,7 @@ namespace Section {
|
|||||||
|
|
||||||
// --- Variables globales de estado ---
|
// --- Variables globales de estado ---
|
||||||
inline Name name = Name::RESET;
|
inline Name name = Name::RESET;
|
||||||
|
inline Name post_preload = Name::LOGO; // Sección a la que transiciona PRELOAD al terminar
|
||||||
inline Options options = Options::NONE;
|
inline Options options = Options::NONE;
|
||||||
inline AttractMode attract_mode = AttractMode::TITLE_TO_DEMO;
|
inline AttractMode attract_mode = AttractMode::TITLE_TO_DEMO;
|
||||||
} // namespace Section
|
} // namespace Section
|
||||||
@@ -21,6 +21,7 @@ namespace Options {
|
|||||||
Settings settings; // Opciones del juego
|
Settings settings; // Opciones del juego
|
||||||
Video video; // Opciones de vídeo
|
Video video; // Opciones de vídeo
|
||||||
Audio audio; // Opciones de audio
|
Audio audio; // Opciones de audio
|
||||||
|
Loading loading; // Opciones de la pantalla de carga
|
||||||
GamepadManager gamepad_manager; // Opciones de mando para cada jugador
|
GamepadManager gamepad_manager; // Opciones de mando para cada jugador
|
||||||
Keyboard keyboard; // Opciones para el teclado
|
Keyboard keyboard; // Opciones para el teclado
|
||||||
PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
||||||
@@ -489,6 +490,14 @@ namespace Options {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadLoadingFromYaml(const fkyaml::node& yaml) {
|
||||||
|
if (!yaml.contains("loading")) { return; }
|
||||||
|
const auto& ld = yaml["loading"];
|
||||||
|
parseBoolField(ld, "show", loading.show);
|
||||||
|
parseBoolField(ld, "show_resource_name", loading.show_resource_name);
|
||||||
|
parseBoolField(ld, "wait_for_input", loading.wait_for_input);
|
||||||
|
}
|
||||||
|
|
||||||
void loadGameFromYaml(const fkyaml::node& yaml) {
|
void loadGameFromYaml(const fkyaml::node& yaml) {
|
||||||
if (!yaml.contains("game")) { return; }
|
if (!yaml.contains("game")) { return; }
|
||||||
const auto& game = yaml["game"];
|
const auto& game = yaml["game"];
|
||||||
@@ -601,6 +610,7 @@ namespace Options {
|
|||||||
loadWindowFromYaml(yaml);
|
loadWindowFromYaml(yaml);
|
||||||
loadVideoFromYaml(yaml);
|
loadVideoFromYaml(yaml);
|
||||||
loadAudioFromYaml(yaml);
|
loadAudioFromYaml(yaml);
|
||||||
|
loadLoadingFromYaml(yaml);
|
||||||
loadGameFromYaml(yaml);
|
loadGameFromYaml(yaml);
|
||||||
loadControllersFromYaml(yaml);
|
loadControllersFromYaml(yaml);
|
||||||
loadKeyboardFromYaml(yaml);
|
loadKeyboardFromYaml(yaml);
|
||||||
@@ -682,6 +692,14 @@ namespace Options {
|
|||||||
file << " volume: " << audio.sound.volume << "\n";
|
file << " volume: " << audio.sound.volume << "\n";
|
||||||
file << "\n";
|
file << "\n";
|
||||||
|
|
||||||
|
// LOADING
|
||||||
|
file << "# LOADING SCREEN\n";
|
||||||
|
file << "loading:\n";
|
||||||
|
file << " show: " << boolToString(loading.show) << "\n";
|
||||||
|
file << " show_resource_name: " << boolToString(loading.show_resource_name) << "\n";
|
||||||
|
file << " wait_for_input: " << boolToString(loading.wait_for_input) << "\n";
|
||||||
|
file << "\n";
|
||||||
|
|
||||||
// GAME
|
// GAME
|
||||||
file << "# GAME\n";
|
file << "# GAME\n";
|
||||||
file << "game:\n";
|
file << "game:\n";
|
||||||
@@ -763,18 +781,30 @@ namespace Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Asigna los mandos físicos basándose en la configuración actual.
|
// Asigna los mandos físicos basándose en la configuración actual.
|
||||||
|
//
|
||||||
|
// Tres pases en orden decreciente de prioridad:
|
||||||
|
// 1) Match por path exacto: resuelve el caso arcade (dos sticks idénticos
|
||||||
|
// enchufados siempre a los mismos puertos USB).
|
||||||
|
// 2) Match por nombre cuando el path no coincidió: recoloca un mando
|
||||||
|
// conocido que se ha reenchufado a otro puerto (ej. DualSense). En ese
|
||||||
|
// caso se refresca el path guardado al nuevo.
|
||||||
|
// 3) Reparto en orden de los mandos físicos sobrantes a los slots que
|
||||||
|
// sigan libres.
|
||||||
void GamepadManager::assignAndLinkGamepads() {
|
void GamepadManager::assignAndLinkGamepads() {
|
||||||
auto physical_gamepads = Input::get()->getGamepads();
|
auto physical_gamepads = Input::get()->getGamepads();
|
||||||
|
|
||||||
std::array<std::string, MAX_PLAYERS> desired_paths;
|
std::array<std::string, MAX_PLAYERS> desired_paths;
|
||||||
|
std::array<std::string, MAX_PLAYERS> desired_names;
|
||||||
for (size_t i = 0; i < MAX_PLAYERS; ++i) {
|
for (size_t i = 0; i < MAX_PLAYERS; ++i) {
|
||||||
desired_paths[i] = gamepads_[i].path;
|
desired_paths[i] = gamepads_[i].path;
|
||||||
|
desired_names[i] = gamepads_[i].name;
|
||||||
gamepads_[i].instance = nullptr;
|
gamepads_[i].instance = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Input::Gamepad>> assigned_instances;
|
std::vector<std::shared_ptr<Input::Gamepad>> assigned_instances;
|
||||||
|
|
||||||
assignGamepadsByPath(desired_paths, physical_gamepads, assigned_instances);
|
assignGamepadsByPath(desired_paths, physical_gamepads, assigned_instances);
|
||||||
|
assignGamepadsByName(desired_names, physical_gamepads, assigned_instances);
|
||||||
assignRemainingGamepads(physical_gamepads, assigned_instances);
|
assignRemainingGamepads(physical_gamepads, assigned_instances);
|
||||||
clearUnassignedGamepadSlots();
|
clearUnassignedGamepadSlots();
|
||||||
}
|
}
|
||||||
@@ -802,7 +832,37 @@ namespace Options {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- SEGUNDA PASADA: Asigna los mandos físicos restantes a los jugadores libres ---
|
// --- SEGUNDA PASADA: Match por nombre para slots cuyo path guardado no casó ---
|
||||||
|
// Si un slot tiene nombre guardado pero el path no ha coincidido (mando reenchufado
|
||||||
|
// a otro puerto, o path del sistema cambiado), lo enlazamos por nombre y
|
||||||
|
// refrescamos el path guardado al del dispositivo físico actual.
|
||||||
|
void GamepadManager::assignGamepadsByName(
|
||||||
|
const std::array<std::string, MAX_PLAYERS>& desired_names,
|
||||||
|
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-named-parameter)
|
||||||
|
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances) {
|
||||||
|
for (size_t i = 0; i < MAX_PLAYERS; ++i) {
|
||||||
|
if (gamepads_[i].instance != nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const std::string& desired_name = desired_names[i];
|
||||||
|
if (desired_name.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& physical_gamepad : physical_gamepads) {
|
||||||
|
if (physical_gamepad->name == desired_name && !isGamepadAssigned(physical_gamepad, assigned_instances)) {
|
||||||
|
gamepads_[i].instance = physical_gamepad;
|
||||||
|
gamepads_[i].name = physical_gamepad->name;
|
||||||
|
gamepads_[i].path = physical_gamepad->path;
|
||||||
|
|
||||||
|
assigned_instances.push_back(physical_gamepad);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- TERCERA PASADA: Asigna los mandos físicos restantes a los jugadores libres ---
|
||||||
void GamepadManager::assignRemainingGamepads(
|
void GamepadManager::assignRemainingGamepads(
|
||||||
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-named-parameter)
|
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-named-parameter)
|
||||||
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances) {
|
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances) {
|
||||||
@@ -824,7 +884,7 @@ namespace Options {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- TERCERA PASADA: Limpia la información "fantasma" de los slots no asignados ---
|
// --- LIMPIEZA FINAL: Limpia la información "fantasma" de los slots no asignados ---
|
||||||
void GamepadManager::clearUnassignedGamepadSlots() {
|
void GamepadManager::clearUnassignedGamepadSlots() {
|
||||||
for (auto& gamepad_config : gamepads_) {
|
for (auto& gamepad_config : gamepads_) {
|
||||||
if (gamepad_config.instance == nullptr) {
|
if (gamepad_config.instance == nullptr) {
|
||||||
|
|||||||
@@ -109,6 +109,12 @@ namespace Options {
|
|||||||
int volume = Defaults::Audio::VOLUME; // Volumen general del audio
|
int volume = Defaults::Audio::VOLUME; // Volumen general del audio
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Loading {
|
||||||
|
bool show = Defaults::Loading::SHOW; // Muestra la pantalla de carga (si no, pantalla en negro)
|
||||||
|
bool show_resource_name = Defaults::Loading::SHOW_RESOURCE_NAME; // Muestra el nombre del recurso en curso sobre la barra de progreso
|
||||||
|
bool wait_for_input = Defaults::Loading::WAIT_FOR_INPUT; // Al terminar la carga, espera a que el usuario pulse una tecla/botón (solo si show=true)
|
||||||
|
};
|
||||||
|
|
||||||
struct Settings {
|
struct Settings {
|
||||||
static constexpr int CURRENT_CONFIG_VERSION = 3; // Versión esperada del fichero
|
static constexpr int CURRENT_CONFIG_VERSION = 3; // Versión esperada del fichero
|
||||||
int config_version = CURRENT_CONFIG_VERSION; // Versión del archivo de configuración
|
int config_version = CURRENT_CONFIG_VERSION; // Versión del archivo de configuración
|
||||||
@@ -293,6 +299,10 @@ namespace Options {
|
|||||||
const std::array<std::string, MAX_PLAYERS>& desired_paths,
|
const std::array<std::string, MAX_PLAYERS>& desired_paths,
|
||||||
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-avoid-const-params-in-decls)
|
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-avoid-const-params-in-decls)
|
||||||
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances);
|
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances);
|
||||||
|
void assignGamepadsByName(
|
||||||
|
const std::array<std::string, MAX_PLAYERS>& desired_names,
|
||||||
|
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-avoid-const-params-in-decls)
|
||||||
|
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances);
|
||||||
void assignRemainingGamepads(
|
void assignRemainingGamepads(
|
||||||
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-avoid-const-params-in-decls)
|
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, // NOLINT(readability-avoid-const-params-in-decls)
|
||||||
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances);
|
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances);
|
||||||
@@ -329,6 +339,7 @@ namespace Options {
|
|||||||
extern Settings settings; // Opciones del juego
|
extern Settings settings; // Opciones del juego
|
||||||
extern Video video; // Opciones de vídeo
|
extern Video video; // Opciones de vídeo
|
||||||
extern Audio audio; // Opciones de audio
|
extern Audio audio; // Opciones de audio
|
||||||
|
extern Loading loading; // Opciones de la pantalla de carga
|
||||||
extern GamepadManager gamepad_manager; // Manager de mandos para cada jugador
|
extern GamepadManager gamepad_manager; // Manager de mandos para cada jugador
|
||||||
extern Keyboard keyboard; // Opciones para el teclado
|
extern Keyboard keyboard; // Opciones para el teclado
|
||||||
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
||||||
|
|||||||
26
source/game/scenes/preload.cpp
Normal file
26
source/game/scenes/preload.cpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#include "game/scenes/preload.hpp"
|
||||||
|
|
||||||
|
#include "core/resources/resource.hpp" // Para Resource
|
||||||
|
#include "core/system/section.hpp" // Para Section
|
||||||
|
#include "game/options.hpp" // Para Options::loading
|
||||||
|
|
||||||
|
void Preload::iterate() {
|
||||||
|
// El repintado es independiente del avance de la carga: Director::iterate
|
||||||
|
// ya ha bombeado Resource::loadStep() antes de llamar aquí.
|
||||||
|
Resource::get()->renderProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preload::handleEvent(const SDL_Event& event) {
|
||||||
|
// Solo aceptamos input si la carga ha terminado y estamos en modo
|
||||||
|
// wait_for_input (que además exige que la pantalla de carga sea visible).
|
||||||
|
if (!Resource::get()->isLoadDone() || !Options::loading.wait_for_input || !Options::loading.show) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pulsación limpia: ignoramos auto-repeat y eventos de hotplug de mando.
|
||||||
|
const bool IS_KEY = event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat;
|
||||||
|
const bool IS_BUTTON = event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN;
|
||||||
|
if (IS_KEY || IS_BUTTON) {
|
||||||
|
Section::name = Section::post_preload;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
source/game/scenes/preload.hpp
Normal file
19
source/game/scenes/preload.hpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h> // Para SDL_Event
|
||||||
|
|
||||||
|
// --- Clase Preload: escena mínima durante la carga incremental de recursos ---
|
||||||
|
//
|
||||||
|
// No avanza la carga — de eso se encarga Director::iterate() llamando a
|
||||||
|
// Resource::loadStep(budget_ms) antes de despachar la escena. Aquí solo se
|
||||||
|
// repinta la barra de progreso y, si Options::loading.wait_for_input está
|
||||||
|
// activo, se detecta la pulsación que transiciona a la siguiente sección.
|
||||||
|
class Preload {
|
||||||
|
public:
|
||||||
|
Preload() = default;
|
||||||
|
~Preload() = default;
|
||||||
|
|
||||||
|
// --- Callbacks para el bucle SDL_MAIN_USE_CALLBACKS ---
|
||||||
|
void iterate(); // Repinta la barra de progreso
|
||||||
|
void handleEvent(const SDL_Event& event); // Detecta pulsación en modo wait_for_input
|
||||||
|
};
|
||||||
5
source/utils/defines.hpp
Normal file
5
source/utils/defines.hpp
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Texts {
|
||||||
|
constexpr const char* VERSION = "2026-04-14"; // Versión del juego (también usada por el Makefile)
|
||||||
|
} // namespace Texts
|
||||||
Reference in New Issue
Block a user