276 lines
8.2 KiB
C++
276 lines
8.2 KiB
C++
#include "director.hpp"
|
||
|
||
#include <SDL3/SDL.h>
|
||
#include <sys/stat.h>
|
||
|
||
#include <cerrno>
|
||
#include <cstdlib>
|
||
#include <iostream>
|
||
|
||
#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 <pwd.h>
|
||
#include <unistd.h>
|
||
#endif
|
||
|
||
// Using declarations per simplificar el codi
|
||
using GestorEscenes::ContextEscenes;
|
||
using Escena = ContextEscenes::Escena;
|
||
|
||
// Constructor
|
||
Director::Director(std::vector<std::string> 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<std::string> 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<int>(std::round(
|
||
Defaults::Window::WIDTH * Options::window.zoom_factor));
|
||
int initial_height = static_cast<int>(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;
|
||
}
|