segon commit

This commit is contained in:
2026-04-05 21:34:38 +02:00
parent d168ed59f9
commit 20ad7d778f
502 changed files with 178145 additions and 0 deletions

View File

@@ -0,0 +1,428 @@
#include "core/system/director.hpp"
#include <SDL3/SDL.h>
#include <sys/stat.h> // Para mkdir, stat, S_IRWXU
#include <unistd.h> // Para getuid
#include <cerrno> // Para errno, EEXIST, EACCES, ENAMETOO...
#include <cstdio> // Para printf, perror
#include <cstdlib> // Para exit, EXIT_FAILURE, srand
#include <iostream> // Para basic_ostream, operator<<, cout
#include <memory> // Para make_unique, unique_ptr
#include <string> // Para operator+, allocator, char_traits
#include "core/audio/audio.hpp" // Para Audio
#include "core/input/input.hpp" // Para Input, InputAction
#include "core/locale/locale.hpp" // Para Locale
#include "core/rendering/render_info.hpp" // Para RenderInfo
#include "core/rendering/screen.hpp" // Para Screen
#include "core/resources/resource_cache.hpp" // Para Resource
#include "core/resources/resource_helper.hpp" // Para ResourceHelper
#include "core/resources/resource_list.hpp" // Para Asset, AssetType
#include "core/resources/resource_loader.hpp" // Para ResourceLoader
#include "game/gameplay/cheevos.hpp" // Para Cheevos
#include "game/options.hpp" // Para Options, options, OptionsVideo
#include "game/scene_manager.hpp" // Para SceneManager
#include "game/scenes/credits.hpp" // Para Credits
#include "game/scenes/ending.hpp" // Para Ending
#include "game/scenes/ending2.hpp" // Para Ending2
#include "game/scenes/game.hpp" // Para Game, GameMode
#include "game/scenes/game_over.hpp" // Para GameOver
#include "game/scenes/loading_screen.hpp" // Para LoadingScreen
#include "game/scenes/logo.hpp" // Para Logo
#include "game/scenes/title.hpp" // Para Title
#include "game/ui/console.hpp" // Para Console
#include "game/ui/notifier.hpp" // Para Notifier
#include "utils/defines.hpp" // Para WINDOW_CAPTION
#ifdef _DEBUG
#include "core/system/debug.hpp" // Para Debug
#include "game/editor/map_editor.hpp" // Para MapEditor
#endif
#ifndef _WIN32
#include <pwd.h>
#endif
// Constructor
Director::Director() {
std::cout << "Game start" << '\n';
// Obtiene la ruta del ejecutable
std::string base = SDL_GetBasePath();
if (!base.empty() && base.back() == '/') {
base.pop_back();
}
executable_path_ = base;
// Crea la carpeta del sistema donde guardar datos
createSystemFolder("jailgames");
createSystemFolder("jailgames/projecte_2026");
// Crea el subdirectorio shaders/ dentro de system_folder_ sin modificar system_folder_
{
std::string shaders_dir = system_folder_ + "/shaders";
struct stat st = {.st_dev = 0};
if (stat(shaders_dir.c_str(), &st) == -1) {
errno = 0;
#ifdef _WIN32
mkdir(shaders_dir.c_str());
#else
mkdir(shaders_dir.c_str(), S_IRWXU);
#endif
}
}
// Determinar el prefijo de ruta según la plataforma
#ifdef MACOS_BUNDLE
const std::string PREFIX = "/../Resources";
#else
const std::string PREFIX;
#endif
// Preparar ruta al pack (en macOS bundle está en Contents/Resources/)
std::string pack_path = executable_path_ + PREFIX + "/resources.pack";
#ifdef RELEASE_BUILD
// ============================================================
// RELEASE BUILD: Pack-first architecture
// ============================================================
std::cout << "\n** RELEASE MODE: Pack-first initialization\n";
// 1. Initialize resource pack system (required, no fallback)
std::cout << "Initializing resource pack: " << pack_path << '\n';
if (!Resource::Helper::initializeResourceSystem(pack_path, false)) {
std::cerr << "ERROR: Failed to load resources.pack (required in release builds)\n";
exit(EXIT_FAILURE);
}
// 2. Validate pack integrity
std::cout << "Validating pack integrity..." << '\n';
if (!Resource::Loader::get().validatePack()) {
std::cerr << "ERROR: Pack validation failed\n";
exit(EXIT_FAILURE);
}
// 3. Load assets.yaml from pack
std::cout << "Loading assets configuration from pack..." << '\n';
std::string assets_config = Resource::Loader::get().loadAssetsConfig();
if (assets_config.empty()) {
std::cerr << "ERROR: Failed to load assets.yaml from pack\n";
exit(EXIT_FAILURE);
}
// 4. Initialize Asset system with config from pack
// NOTE: In release, don't use executable_path or PREFIX - paths in pack are relative
// Pass empty string to avoid issues when running from different directories
Resource::List::init(""); // Empty executable_path in release
Resource::List::get()->loadFromString(assets_config, "", system_folder_); // Empty PREFIX for pack
std::cout << "Asset system initialized from pack\n";
#else
// ============================================================
// DEVELOPMENT BUILD: Filesystem-first architecture
// ============================================================
std::cout << "\n** DEVELOPMENT MODE: Filesystem-first initialization\n";
// 1. Initialize Asset system from filesystem
Resource::List::init(executable_path_);
// 2. Load asset configuration from disk
// Note: Asset verification happens during Resource::Cache::load()
setFileList();
// 3. Initialize resource pack system (optional, with fallback)
std::cout << "Initializing resource pack (development mode): " << pack_path << '\n';
Resource::Helper::initializeResourceSystem(pack_path, true);
#endif
// Configura la ruta y carga las opciones desde un fichero
Options::setConfigFile(Resource::List::get()->get("config.yaml")); // NOLINT(readability-static-accessed-through-instance)
Options::loadFromFile();
// Configura la ruta y carga los presets de PostFX
Options::setPostFXFile(Resource::List::get()->get("postfx.yaml")); // NOLINT(readability-static-accessed-through-instance)
Options::loadPostFXFromFile();
// Configura la ruta y carga los presets del shader CrtPi
Options::setCrtPiFile(Resource::List::get()->get("crtpi.yaml")); // NOLINT(readability-static-accessed-through-instance)
Options::loadCrtPiFromFile();
// En mode quiosc, forçar pantalla completa independentment de la configuració
if (Options::kiosk.enabled) {
Options::video.fullscreen = true;
}
// Inicializa JailAudio
Audio::init();
// Crea los objetos
Screen::init();
// Initialize resources (works for both release and development)
Resource::Cache::init();
Notifier::init("", "8bithud");
RenderInfo::init();
Console::init("8bithud");
Screen::get()->setNotificationsEnabled(true);
// Special handling for gamecontrollerdb.txt - SDL needs filesystem path
#ifdef RELEASE_BUILD
// In release, construct the path manually (not from Asset which has empty executable_path)
std::string gamecontroller_db = executable_path_ + PREFIX + "/gamecontrollerdb.txt";
Input::init(gamecontroller_db);
#else
// In development, use Asset as normal
Input::init(Resource::List::get()->get("gamecontrollerdb.txt")); // NOLINT(readability-static-accessed-through-instance) Carga configuración de controles
#endif
// Aplica las teclas y botones del gamepad configurados desde Options
Input::get()->applyKeyboardBindingsFromOptions();
Input::get()->applyGamepadBindingsFromOptions();
#ifdef _DEBUG
Debug::init();
Debug::get()->setDebugFile(Resource::List::get()->get("debug.yaml"));
Debug::get()->loadFromFile();
SceneManager::current = Debug::get()->getInitialScene();
MapEditor::init();
#endif
std::cout << "\n"; // Fin de inicialización de sistemas
// Inicializa el sistema de localización (antes de Cheevos que usa textos traducidos)
#ifdef RELEASE_BUILD
{
// En release el locale está en el pack, no en el filesystem
std::string locale_key = Resource::List::get()->get(Options::language + ".yaml"); // NOLINT(readability-static-accessed-through-instance)
auto locale_bytes = Resource::Helper::loadFile(locale_key);
std::string locale_content(locale_bytes.begin(), locale_bytes.end());
Locale::initFromContent(locale_content);
}
#else
Locale::init(Resource::List::get()->get(Options::language + ".yaml")); // NOLINT(readability-static-accessed-through-instance)
#endif
// Special handling for cheevos.bin - also needs filesystem path
#ifdef RELEASE_BUILD
std::string cheevos_path = system_folder_ + "/cheevos.bin";
Cheevos::init(cheevos_path);
#else
Cheevos::init(Resource::List::get()->get("cheevos.bin"));
#endif
}
Director::~Director() {
// Guarda las opciones a un fichero
Options::saveToFile();
// Destruye los singletones
Cheevos::destroy();
Locale::destroy();
#ifdef _DEBUG
MapEditor::destroy();
Debug::destroy();
#endif
Input::destroy();
Console::destroy();
RenderInfo::destroy();
Notifier::destroy();
Resource::Cache::destroy();
Resource::Helper::shutdownResourceSystem(); // Shutdown resource pack system
Audio::destroy();
Screen::destroy();
Resource::List::destroy();
SDL_Quit();
std::cout << "\nBye!" << '\n';
}
// Crea la carpeta del sistema donde guardar datos
void Director::createSystemFolder(const std::string& folder) { // NOLINT(readability-convert-member-functions-to-static)
#ifdef _WIN32
system_folder_ = std::string(getenv("APPDATA")) + "/" + folder;
#elif __APPLE__
struct passwd* pw = getpwuid(getuid());
const char* homedir = pw->pw_dir;
system_folder_ = std::string(homedir) + "/Library/Application Support" + "/" + folder;
#elif __linux__
struct passwd* pw = getpwuid(getuid());
const char* homedir = pw->pw_dir;
system_folder_ = std::string(homedir) + "/.config/" + folder;
{
// Intenta crear ".config", per si no existeix
std::string config_base_folder = std::string(homedir) + "/.config";
int ret = mkdir(config_base_folder.c_str(), S_IRWXU);
if (ret == -1 && errno != EEXIST) {
printf("ERROR CREATING CONFIG BASE FOLDER.");
exit(EXIT_FAILURE);
}
}
#endif
struct stat st = {.st_dev = 0};
if (stat(system_folder_.c_str(), &st) == -1) {
errno = 0;
#ifdef _WIN32
int ret = mkdir(system_folder_.c_str());
#else
int ret = mkdir(system_folder_.c_str(), S_IRWXU);
#endif
if (ret == -1) {
switch (errno) {
case EACCES:
printf("the parent directory does not allow write");
exit(EXIT_FAILURE);
case EEXIST:
printf("pathname already exists");
exit(EXIT_FAILURE);
case ENAMETOOLONG:
printf("pathname is too long");
exit(EXIT_FAILURE);
default:
perror("mkdir");
exit(EXIT_FAILURE);
}
}
}
}
// Carga la configuración de assets desde assets.yaml
void Director::setFileList() { // NOLINT(readability-convert-member-functions-to-static)
// Determinar el prefijo de ruta según la plataforma
#ifdef MACOS_BUNDLE
const std::string PREFIX = "/../Resources";
#else
const std::string PREFIX;
#endif
// Construir ruta al archivo de configuración de assets
std::string config_path = executable_path_ + PREFIX + "/config/assets.yaml";
// Cargar todos los assets desde el archivo de configuración
// La verificación de existencia de archivos se realiza durante Resource::Cache::load()
Resource::List::get()->loadFromFile(config_path, PREFIX, system_folder_);
}
// Ejecuta la seccion de juego con el logo
void Director::runLogo() {
auto logo = std::make_unique<Logo>();
logo->run();
}
// Ejecuta la seccion de juego de la pantalla de carga
void Director::runLoadingScreen() {
auto loading_screen = std::make_unique<LoadingScreen>();
loading_screen->run();
}
// Ejecuta la seccion de juego con el titulo y los menus
void Director::runTitle() {
auto title = std::make_unique<Title>();
title->run();
}
// Ejecuta la seccion de los creditos del juego
void Director::runCredits() {
auto credits = std::make_unique<Credits>();
credits->run();
}
// Ejecuta la seccion de la demo, donde se ven pantallas del juego
void Director::runDemo() {
auto game = std::make_unique<Game>(Game::Mode::DEMO);
game->run();
}
// Ejecuta la seccion del final del juego
void Director::runEnding() {
auto ending = std::make_unique<Ending>();
ending->run();
}
// Ejecuta la seccion del final del juego
void Director::runEnding2() {
auto ending2 = std::make_unique<Ending2>();
ending2->run();
}
// Ejecuta la seccion del final de la partida
void Director::runGameOver() {
auto game_over = std::make_unique<GameOver>();
game_over->run();
}
// Ejecuta la seccion de juego donde se juega
void Director::runGame() {
Audio::get()->stopMusic();
auto game = std::make_unique<Game>(Game::Mode::GAME);
game->run();
}
auto Director::run() -> int {
// Bucle principal
while (SceneManager::current != SceneManager::Scene::QUIT) {
const SceneManager::Scene ACTIVE = SceneManager::current;
switch (SceneManager::current) {
case SceneManager::Scene::LOGO:
runLogo();
break;
case SceneManager::Scene::LOADING_SCREEN:
runLoadingScreen();
break;
case SceneManager::Scene::TITLE:
runTitle();
break;
case SceneManager::Scene::CREDITS:
runCredits();
break;
case SceneManager::Scene::DEMO:
runDemo();
break;
case SceneManager::Scene::GAME:
runGame();
break;
case SceneManager::Scene::GAME_OVER:
runGameOver();
break;
case SceneManager::Scene::ENDING:
runEnding();
break;
case SceneManager::Scene::ENDING2:
runEnding2();
break;
case SceneManager::Scene::RESTART_CURRENT:
// La escena salió por RESTART_CURRENT → relanzar la escena guardada
SceneManager::current = SceneManager::scene_before_restart;
break;
default:
break;
}
// Si la escena que acaba de correr dejó RESTART_CURRENT pendiente,
// restaurar la escena que estaba activa para relanzarla en la próxima iteración
if (SceneManager::current == SceneManager::Scene::RESTART_CURRENT) {
SceneManager::current = ACTIVE;
}
}
return 0;
}