#include "director.hpp" #include #include #include #include #include #include "core/audio/audio.hpp" #include "core/audio/audio_cache.hpp" #include "core/defaults.hpp" #include "core/input/input.hpp" #include "core/rendering/sdl_manager.hpp" #include "core/resources/resource_helper.hpp" #include "core/resources/resource_loader.hpp" #include "core/utils/path_utils.hpp" #include "game/escenes/escena_joc.hpp" #include "game/escenes/escena_logo.hpp" #include "game/escenes/escena_titol.hpp" #include "game/options.hpp" #include "context_escenes.hpp" #include "project.h" #ifndef _WIN32 #include #include #endif // Using declarations per simplificar el codi using GestorEscenes::ContextEscenes; using Escena = ContextEscenes::Escena; // Constructor Director::Director(std::vector const& args) { std::cout << "Orni Attack - Inici\n"; // Inicialitzar opcions amb valors per defecte Options::init(); // Comprovar arguments del programa executable_path_ = checkProgramArguments(args); // Inicialitzar sistema de rutes Utils::initializePathSystem(args[0].c_str()); // Obtenir ruta base dels recursos std::string resource_base = Utils::getResourceBasePath(); // Inicialitzar sistema de recursos #ifdef RELEASE_BUILD // Mode release: paquet obligatori, sense fallback std::string pack_path = resource_base + "/resources.pack"; if (!Resource::Helper::initializeResourceSystem(pack_path, false)) { std::cerr << "ERROR FATAL: No es pot carregar " << pack_path << "\n"; std::cerr << "El joc no pot continuar sense els recursos.\n"; std::exit(1); } // Validar integritat del paquet if (!Resource::Loader::get().validatePack()) { std::cerr << "ERROR FATAL: El paquet de recursos està corromput\n"; std::exit(1); } std::cout << "Sistema de recursos inicialitzat (mode release)\n"; #else // Mode desenvolupament: intentar paquet amb fallback a data/ std::string pack_path = resource_base + "/resources.pack"; Resource::Helper::initializeResourceSystem(pack_path, true); if (Resource::Helper::isPackLoaded()) { std::cout << "Sistema de recursos inicialitzat (mode dev amb paquet)\n"; } else { std::cout << "Sistema de recursos inicialitzat (mode dev, fallback a data/)\n"; } // Establir ruta base per al fallback Resource::Loader::get().setBasePath(resource_base); #endif // Crear carpetes del sistema createSystemFolder("jailgames"); createSystemFolder(std::string("jailgames/") + Project::NAME); // Establir ruta del fitxer de configuració Options::setConfigFile(system_folder_ + "/config.yaml"); // Carregar o crear configuració Options::loadFromFile(); // Inicialitzar sistema d'input Input::init("data/gamecontrollerdb.txt"); // Aplicar configuració de controls dels jugadors Input::get()->applyPlayer1BindingsFromOptions(); Input::get()->applyPlayer2BindingsFromOptions(); if (Options::console) { std::cout << "Configuració carregada\n"; std::cout << " Finestra: " << Options::window.width << "×" << Options::window.height << '\n'; std::cout << " Física: rotation=" << Options::physics.rotation_speed << " rad/s\n"; std::cout << " Input: " << Input::get()->getNumGamepads() << " gamepad(s) detectat(s)\n"; } std::cout << '\n'; } Director::~Director() { // Guardar opcions Options::saveToFile(); // Cleanup input Input::destroy(); // Cleanup audio Audio::destroy(); // Cleanup SDL SDL_Quit(); std::cout << "\nAdéu!\n"; } // Comprovar arguments del programa auto Director::checkProgramArguments(std::vector const& args) -> std::string { for (std::size_t i = 1; i < args.size(); ++i) { const std::string& argument = args[i]; if (argument == "--console") { Options::console = true; std::cout << "Mode consola activat\n"; } else if (argument == "--reset-config") { Options::init(); Options::saveToFile(); std::cout << "Configuració restablida als valors per defecte\n"; } } return args[0]; // Retornar ruta de l'executable } // Crear carpeta del sistema (específic per plataforma) void Director::createSystemFolder(const std::string& folder) { #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; // CRÍTIC: Crear ~/.config 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: No es pot crear la carpeta ~/.config\n"); exit(EXIT_FAILURE); } } #endif // Comprovar si la carpeta existeix 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("ERROR: Permisos denegats creant %s\n", system_folder_.c_str()); exit(EXIT_FAILURE); case EEXIST: // La carpeta ja existeix (race condition), continuar break; case ENAMETOOLONG: printf("ERROR: Ruta massa llarga: %s\n", system_folder_.c_str()); exit(EXIT_FAILURE); default: perror("mkdir"); exit(EXIT_FAILURE); } } } if (Options::console) { std::cout << "Carpeta del sistema: " << system_folder_ << '\n'; } } // Bucle principal del joc auto Director::run() -> int { // Calculate initial size from saved zoom_factor int initial_width = static_cast(std::round( Defaults::Window::WIDTH * Options::window.zoom_factor)); int initial_height = static_cast(std::round( Defaults::Window::HEIGHT * Options::window.zoom_factor)); // Crear gestor SDL amb configuració de Options SDLManager sdl(initial_width, initial_height, Options::window.fullscreen); // Inicialitzar sistema d'audio Audio::init(); Audio::get()->setMusicVolume(1.0); Audio::get()->setSoundVolume(0.4); // Precachejar música per evitar lag al començar AudioCache::getMusic("title.ogg"); AudioCache::getMusic("game.ogg"); if (Options::console) { std::cout << "Música precachejada: " << AudioCache::getMusicCacheSize() << " fitxers\n"; } // Crear context d'escenes ContextEscenes context; #ifdef _DEBUG context.canviar_escena(Escena::JOC); #else context.canviar_escena(Escena::LOGO); #endif // Bucle principal de gestió d'escenes while (context.escena_desti() != Escena::EIXIR) { // Sincronitzar GestorEscenes::actual amb context // (altres sistemes encara poden llegir GestorEscenes::actual) GestorEscenes::actual = context.escena_desti(); switch (context.escena_desti()) { case Escena::LOGO: { EscenaLogo logo(sdl, context); logo.executar(); break; } case Escena::TITOL: { EscenaTitol titol(sdl, context); titol.executar(); break; } case Escena::JOC: { EscenaJoc joc(sdl, context); joc.executar(); break; } default: break; } } // Sincronitzar final amb GestorEscenes::actual GestorEscenes::actual = Escena::EIXIR; return 0; }