// IWYU pragma: no_include #include "director.h" #include // Para SDL_Init, SDL_Quit, SDL_INIT_EV... #include // Para AUDIO_S16 #include // Para SDL_BLENDMODE_BLEND #include // Para SDL_GetError #include // Para SDL_CONTROLLER_BUTTON_B, SDL_CO... #include // Para SDL_SetHint, SDL_HINT_RENDER_DR... #include // Para SDL_SCANCODE_0, SDL_SCANCODE_DOWN #include // Para SDL_bool, Uint32 #include // Para duration, system_clock #include // Para errno, EEXIST, EACCES, ENAMETOO... #include // Para printf, perror #include // Para mkdir, stat, S_IRWXU #include // Para getuid #include // Para min #include // Para exit, EXIT_FAILURE, size_t, rand #include // Para basic_ostream, operator<<, basi... #include // Para make_unique, unique_ptr #include // Para runtime_error #include // Para operator+, char_traits, allocator #include // Para vector #include "asset.h" // Para Asset, AssetType #include "credits.h" // Para Credits #include "dbgtxt.h" // Para dbg_init #include "game.h" // Para Game, GAME_MODE_DEMO_OFF, GAME_... #include "global_inputs.h" // Para init #include "hiscore_table.h" // Para HiScoreTable #include "input.h" // Para Input, InputType #include "instructions.h" // Para Instructions #include "intro.h" // Para Intro #include "jail_audio.h" // Para JA_SetMusicVolume, JA_SetSoundV... #include "lang.h" // Para Code, loadFromFile #include "logo.h" // Para Logo #include "manage_hiscore_table.h" // Para ManageHiScoreTable #include "notifier.h" // Para Notifier #include "on_screen_help.h" // Para OnScreenHelp #include "options.h" // Para Options, OptionsController, opt... #include "param.h" // Para Param, ParamGame, param, loadPa... #include "resource.h" // Para Resource #include "screen.h" // Para Screen #include "section.h" // Para Name, Options, name, options #include "title.h" // Para Title #include "utils.h" // Para Overrides, overrides #ifndef _WIN32 #include // para getpwuid, passwd #endif // Constructor Director::Director(int argc, const char *argv[]) { #ifdef RECORDING section::name = section::Name::GAME; section::options = section::Options::GAME_PLAY_1P; #elif DEBUG section::name = section::Name::CREDITS; #else // NORMAL GAME section::name = section::Name::LOGO; section::attract_mode = section::AttractMode::TITLE_TO_DEMO; #endif #ifndef VERBOSE // Deshabilita todos los std::cout std::ostream null_stream(nullptr); orig_buf = std::cout.rdbuf(null_stream.rdbuf()); #endif std::cout << "Game start" << std::endl; // Inicia la semilla aleatoria unsigned int seed = static_cast(std::chrono::system_clock::now().time_since_epoch().count()); std::srand(seed); // Comprueba los parametros del programa checkProgramArguments(argc, argv); // Crea la carpeta del sistema donde guardar los datos persistentes createSystemFolder("jailgames"); createSystemFolder("jailgames/coffee_crisis_arcade_edition"); init(); } Director::~Director() { close(); std::cout << "\nBye!" << std::endl; } // Inicializa todo void Director::init() { Asset::init(executable_path_); // Crea el objeto que controla los ficheros de recursos setFileList(); // Crea el indice de ficheros loadOptionsFile(Asset::get()->get("config.txt")); // Carga el fichero de configuración loadParams(); // Carga los parametros loadScoreFile(); // Carga el fichero de puntuaciones // Inicializa y crea el resto de objetos initSDL(); initJailAudio(); dbg_init(renderer_); lang::loadFromFile(getLangFile(static_cast(options.game.language))); Screen::init(window_, renderer_); Resource::init(); Input::init(Asset::get()->get("gamecontrollerdb.txt")); bindInputs(); Notifier::init(std::string(), Resource::get()->getText("8bithud")); OnScreenHelp::init(); } // Cierra todo void Director::close() { saveOptionsFile(Asset::get()->get("config.txt")); Asset::destroy(); Resource::destroy(); Input::destroy(); Screen::destroy(); Notifier::destroy(); OnScreenHelp::destroy(); JA_Quit(); SDL_DestroyRenderer(renderer_); SDL_DestroyWindow(window_); SDL_Quit(); } // Carga los parametros void Director::loadParams() { // Carga los parametros para configurar el juego #ifdef ANBERNIC const std::string paramFilePath = asset->get("param_320x240.txt"); #else const std::string paramFilePath = overrides.param_file == "--320x240" ? Asset::get()->get("param_320x240.txt") : Asset::get()->get("param_320x256.txt"); #endif loadParamsFromFile(paramFilePath); } // Carga el fichero de puntuaciones void Director::loadScoreFile() { auto manager = std::make_unique(options.game.hi_score_table); if (overrides.clear_hi_score_table) { manager->clear(); } else { manager->loadFromFile(Asset::get()->get("score.bin")); } } // Asigna los botones y teclas al objeto Input void Director::bindInputs() { // Teclado - Movimiento del jugador Input::get()->bindKey(InputType::UP, SDL_SCANCODE_UP); Input::get()->bindKey(InputType::DOWN, SDL_SCANCODE_DOWN); Input::get()->bindKey(InputType::LEFT, SDL_SCANCODE_LEFT); Input::get()->bindKey(InputType::RIGHT, SDL_SCANCODE_RIGHT); Input::get()->bindKey(InputType::FIRE_LEFT, SDL_SCANCODE_Q); Input::get()->bindKey(InputType::FIRE_CENTER, SDL_SCANCODE_W); Input::get()->bindKey(InputType::FIRE_RIGHT, SDL_SCANCODE_E); Input::get()->bindKey(InputType::START, SDL_SCANCODE_RETURN); // Teclado - Control del programa Input::get()->bindKey(InputType::SERVICE, SDL_SCANCODE_0); Input::get()->bindKey(InputType::EXIT, SDL_SCANCODE_ESCAPE); Input::get()->bindKey(InputType::PAUSE, SDL_SCANCODE_P); Input::get()->bindKey(InputType::WINDOW_DEC_SIZE, SDL_SCANCODE_F1); Input::get()->bindKey(InputType::WINDOW_INC_SIZE, SDL_SCANCODE_F2); Input::get()->bindKey(InputType::WINDOW_FULLSCREEN, SDL_SCANCODE_F3); Input::get()->bindKey(InputType::VIDEO_SHADERS, SDL_SCANCODE_F4); Input::get()->bindKey(InputType::MUTE, SDL_SCANCODE_F5); Input::get()->bindKey(InputType::AUTO_FIRE, SDL_SCANCODE_F6); Input::get()->bindKey(InputType::CHANGE_LANG, SDL_SCANCODE_F7); Input::get()->bindKey(InputType::SHOWINFO, SDL_SCANCODE_F8); Input::get()->bindKey(InputType::RESET, SDL_SCANCODE_F10); // Asigna botones a inputs const int num_gamepads = Input::get()->getNumControllers(); for (int i = 0; i < num_gamepads; ++i) { // Mando - Movimiento del jugador Input::get()->bindGameControllerButton(i, InputType::UP, SDL_CONTROLLER_BUTTON_DPAD_UP); Input::get()->bindGameControllerButton(i, InputType::DOWN, SDL_CONTROLLER_BUTTON_DPAD_DOWN); Input::get()->bindGameControllerButton(i, InputType::LEFT, SDL_CONTROLLER_BUTTON_DPAD_LEFT); Input::get()->bindGameControllerButton(i, InputType::RIGHT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); Input::get()->bindGameControllerButton(i, InputType::FIRE_LEFT, SDL_CONTROLLER_BUTTON_X); Input::get()->bindGameControllerButton(i, InputType::FIRE_CENTER, SDL_CONTROLLER_BUTTON_Y); Input::get()->bindGameControllerButton(i, InputType::FIRE_RIGHT, SDL_CONTROLLER_BUTTON_B); Input::get()->bindGameControllerButton(i, InputType::START, SDL_CONTROLLER_BUTTON_START); // Mando - Control del programa Input::get()->bindGameControllerButton(i, InputType::SERVICE, SDL_CONTROLLER_BUTTON_BACK); } // Mapea las asignaciones a los botones desde el archivo de configuración, si se da el caso const size_t max_controllers = std::min(2, num_gamepads); for (size_t i = 0; i < max_controllers; ++i) { for (auto &controller : options.controllers) { if (Input::get()->getControllerName(i) == controller.name) { for (size_t j = 0; j < controller.inputs.size(); ++j) { Input::get()->bindGameControllerButton(i, controller.inputs.at(j), controller.buttons.at(j)); } } } } // Asigna botones a inputs desde otros inputs for (int i = 0; i < num_gamepads; ++i) { Input::get()->bindGameControllerButton(i, InputType::EXIT, InputType::START); Input::get()->bindGameControllerButton(i, InputType::RESET, InputType::FIRE_CENTER); Input::get()->bindGameControllerButton(i, InputType::PAUSE, InputType::FIRE_RIGHT); Input::get()->bindGameControllerButton(i, InputType::VIDEO_SHADERS, InputType::FIRE_LEFT); Input::get()->bindGameControllerButton(i, InputType::MUTE, InputType::LEFT); Input::get()->bindGameControllerButton(i, InputType::SHOWINFO, InputType::RIGHT); Input::get()->bindGameControllerButton(i, InputType::CONFIG, InputType::DOWN); Input::get()->bindGameControllerButton(i, InputType::SWAP_CONTROLLERS, InputType::UP); } // Guarda las asignaciones de botones en las opciones de los dos primeros mandos for (size_t i = 0; i < max_controllers; ++i) { // Variables asociadas al mando options.controllers.at(i).index = i; options.controllers.at(i).name = Input::get()->getControllerName(i); options.controllers.at(i).plugged = true; // Asignaciones de botones for (size_t j = 0; j < options.controllers.at(i).inputs.size(); ++j) { options.controllers.at(i).buttons.at(j) = Input::get()->getControllerBinding(i, options.controllers.at(i).inputs.at(j)); } } // Asegura que algún jugador tenga el teclado asignado if (getPlayerWhoUsesKeyboard() == 0) { setKeyboardToPlayer(1); } } // Inicializa JailAudio void Director::initJailAudio() { JA_Init(48000, AUDIO_S16, 2); if (options.audio.enabled) { JA_SetMusicVolume(to_JA_volume(options.audio.music.volume)); JA_SetSoundVolume(to_JA_volume(options.audio.sound.volume)); } else { JA_SetMusicVolume(0); JA_SetSoundVolume(0); } } // Arranca SDL y crea la ventana bool Director::initSDL() { // Indicador de éxito auto success = true; // Inicializa SDL if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { std::cout << "SDL could not initialize!\nSDL Error: " << SDL_GetError() << std::endl; success = false; } else { /* // Muestra información de la pantalla std::cout << "\nDisplay modes list:" << std::endl; for (int i = 0; i < SDL_GetNumDisplayModes(0); ++i) { SDL_DisplayMode DM; SDL_GetDisplayMode(0,i,&DM); std::cout << " - " << DM.w << "x" << DM.h << " @ " << DM.refresh_rate << "Hz" << std::endl; } */ // Obtiene información sobre la pantalla SDL_DisplayMode DM; SDL_GetCurrentDisplayMode(0, &DM); // Calcula el máximo factor de zoom que se puede aplicar a la pantalla options.video.window.max_size = std::min(DM.w / param.game.width, DM.h / param.game.height); options.video.window.size = std::min(options.video.window.size, options.video.window.max_size); // Muestra información sobre el tamaño de la pantalla y de la ventana de juego std::cout << "\nCurrent display mode: " << DM.w << "x" << DM.h << " @ " << DM.refresh_rate << "Hz" << std::endl; std::cout << "Window resolution : " << param.game.width << "x" << param.game.height << " x" << options.video.window.size << std::endl; // Establece el filtro de la textura if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, std::to_string(static_cast(options.video.filter)).c_str())) { std::cout << "Warning: texture filtering not enabled!\n"; } #ifndef NO_SHADERS if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) { std::cout << "Warning: opengl not enabled!\n"; } #endif // Crea la ventana window_ = SDL_CreateWindow(WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, param.game.width * options.video.window.size, param.game.height * options.video.window.size, SDL_WINDOW_HIDDEN); if (!window_) { std::cout << "Window could not be created!\nSDL Error: " << SDL_GetError() << std::endl; success = false; } else { // Crea un renderizador para la ventana. El vsync se activa en funcion de las opciones Uint32 flags = 0; if (options.video.v_sync) { flags = SDL_RENDERER_PRESENTVSYNC; } #ifndef NO_SHADERS // La aceleración se activa según el define flags = flags | SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE; #endif renderer_ = SDL_CreateRenderer(window_, -1, flags); if (!renderer_) { std::cout << "Renderer could not be created!\nSDL Error: " << SDL_GetError() << std::endl; success = false; } else { SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF); SDL_RenderSetLogicalSize(renderer_, param.game.width, param.game.height); SDL_RenderSetIntegerScale(renderer_, static_cast(options.video.integer_scale)); SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND); } } } std::cout << std::endl; return success; } // Crea el indice de ficheros void Director::setFileList() { #ifdef MACOS_BUNDLE const std::string prefix = "/../Resources"; #else const std::string prefix; #endif // Ficheros de configuración Asset::get()->add(system_folder_ + "/config.txt", AssetType::DATA, false, true); Asset::get()->add(system_folder_ + "/score.bin", AssetType::DATA, false, true); Asset::get()->add(prefix + "/data/config/param_320x240.txt", AssetType::DATA); Asset::get()->add(prefix + "/data/config/param_320x256.txt", AssetType::DATA); Asset::get()->add(prefix + "/data/config/demo1.bin", AssetType::DATA); Asset::get()->add(prefix + "/data/config/demo2.bin", AssetType::DATA); Asset::get()->add(prefix + "/data/config/gamecontrollerdb.txt", AssetType::DATA); // Musicas Asset::get()->add(prefix + "/data/music/intro.ogg", AssetType::MUSIC); Asset::get()->add(prefix + "/data/music/playing.ogg", AssetType::MUSIC); Asset::get()->add(prefix + "/data/music/title.ogg", AssetType::MUSIC); Asset::get()->add(prefix + "/data/music/credits.ogg", AssetType::MUSIC); // Sonidos Asset::get()->add(prefix + "/data/sound/balloon.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/bubble1.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/bubble2.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/bubble3.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/bubble4.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/bullet.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/clock.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/coffeeout.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/continue_clock.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/game_start.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/hiscore.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/itemdrop.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/itempickup.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/logo.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/notify.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/player_collision.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/powerball.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/stage_change.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/tabe.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/title.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/voice_coffee.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/voice_get_ready.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/voice_no.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/voice_power_up.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/walk.wav", AssetType::SOUND); // Shaders Asset::get()->add(prefix + "/data/shaders/crtpi_256.glsl", AssetType::DATA); Asset::get()->add(prefix + "/data/shaders/crtpi_240.glsl", AssetType::DATA); // Texturas { // Controllers Asset::get()->add(prefix + "/data/gfx/controllers/controllers.png", AssetType::BITMAP); } { // Balloons Asset::get()->add(prefix + "/data/gfx/balloon/balloon1.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/balloon/balloon1.ani", AssetType::ANIMATION); Asset::get()->add(prefix + "/data/gfx/balloon/balloon2.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/balloon/balloon2.ani", AssetType::ANIMATION); Asset::get()->add(prefix + "/data/gfx/balloon/balloon3.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/balloon/balloon3.ani", AssetType::ANIMATION); Asset::get()->add(prefix + "/data/gfx/balloon/balloon4.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/balloon/balloon4.ani", AssetType::ANIMATION); } { // Explosions Asset::get()->add(prefix + "/data/gfx/balloon/explosion1.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/balloon/explosion1.ani", AssetType::ANIMATION); Asset::get()->add(prefix + "/data/gfx/balloon/explosion2.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/balloon/explosion2.ani", AssetType::ANIMATION); Asset::get()->add(prefix + "/data/gfx/balloon/explosion3.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/balloon/explosion3.ani", AssetType::ANIMATION); Asset::get()->add(prefix + "/data/gfx/balloon/explosion4.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/balloon/explosion4.ani", AssetType::ANIMATION); } { // Power Ball Asset::get()->add(prefix + "/data/gfx/balloon/powerball.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/balloon/powerball.ani", AssetType::ANIMATION); } { // Bala Asset::get()->add(prefix + "/data/gfx/bullet/bullet.png", AssetType::BITMAP); } { // Tabe Asset::get()->add(prefix + "/data/gfx/tabe/tabe.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/tabe/tabe.ani", AssetType::ANIMATION); } { // Juego Asset::get()->add(prefix + "/data/gfx/game/game_buildings.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/game/game_clouds1.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/game/game_clouds2.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/game/game_grass.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/game/game_power_meter.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/game/game_sky_colors.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/game/game_sun.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/game/game_moon.png", AssetType::BITMAP); } { // Intro Asset::get()->add(prefix + "/data/gfx/intro/intro.png", AssetType::BITMAP); } { // Logo Asset::get()->add(prefix + "/data/gfx/logo/logo_jailgames.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/logo/logo_jailgames_mini.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/logo/logo_since_1998.png", AssetType::BITMAP); } { // Items Asset::get()->add(prefix + "/data/gfx/item/item_points1_disk.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/item/item_points1_disk.ani", AssetType::ANIMATION); Asset::get()->add(prefix + "/data/gfx/item/item_points2_gavina.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/item/item_points2_gavina.ani", AssetType::ANIMATION); Asset::get()->add(prefix + "/data/gfx/item/item_points3_pacmar.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/item/item_points3_pacmar.ani", AssetType::ANIMATION); Asset::get()->add(prefix + "/data/gfx/item/item_clock.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/item/item_clock.ani", AssetType::ANIMATION); Asset::get()->add(prefix + "/data/gfx/item/item_coffee.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/item/item_coffee.ani", AssetType::ANIMATION); Asset::get()->add(prefix + "/data/gfx/item/item_coffee_machine.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/item/item_coffee_machine.ani", AssetType::ANIMATION); } { // Titulo Asset::get()->add(prefix + "/data/gfx/title/title_bg_tile.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/title/title_coffee.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/title/title_crisis.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/title/title_arcade_edition.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/title/title_dust.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/title/title_dust.ani", AssetType::ANIMATION); } { // Jugador 1 Asset::get()->add(prefix + "/data/gfx/player/player1.gif", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/player/player1_1_coffee_palette.gif", AssetType::PALETTE); Asset::get()->add(prefix + "/data/gfx/player/player1_2_coffee_palette.gif", AssetType::PALETTE); Asset::get()->add(prefix + "/data/gfx/player/player1_invencible_palette.gif", AssetType::PALETTE); Asset::get()->add(prefix + "/data/gfx/player/player1_power.png", AssetType::BITMAP); } { // Jugador 2 Asset::get()->add(prefix + "/data/gfx/player/player2.gif", AssetType::BITMAP); Asset::get()->add(prefix + "/data/gfx/player/player2_1_coffee_palette.gif", AssetType::PALETTE); Asset::get()->add(prefix + "/data/gfx/player/player2_2_coffee_palette.gif", AssetType::PALETTE); Asset::get()->add(prefix + "/data/gfx/player/player2_invencible_palette.gif", AssetType::PALETTE); Asset::get()->add(prefix + "/data/gfx/player/player2_power.png", AssetType::BITMAP); } { // Animaciones del jugador Asset::get()->add(prefix + "/data/gfx/player/player.ani", AssetType::ANIMATION); Asset::get()->add(prefix + "/data/gfx/player/player_power.ani", AssetType::ANIMATION); } // Fuentes de texto Asset::get()->add(prefix + "/data/font/8bithud.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/font/8bithud.txt", AssetType::FONT); Asset::get()->add(prefix + "/data/font/nokia.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/font/nokia.txt", AssetType::FONT); Asset::get()->add(prefix + "/data/font/smb2.gif", AssetType::BITMAP); Asset::get()->add(prefix + "/data/font/smb2_palette1.pal", AssetType::PALETTE); Asset::get()->add(prefix + "/data/font/smb2.txt", AssetType::FONT); Asset::get()->add(prefix + "/data/font/04b_25.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/font/04b_25.txt", AssetType::FONT); Asset::get()->add(prefix + "/data/font/04b_25_2x.png", AssetType::BITMAP); Asset::get()->add(prefix + "/data/font/04b_25_2x.txt", AssetType::FONT); // Textos Asset::get()->add(prefix + "/data/lang/es_ES.txt", AssetType::LANG); Asset::get()->add(prefix + "/data/lang/en_UK.txt", AssetType::LANG); Asset::get()->add(prefix + "/data/lang/ba_BA.txt", AssetType::LANG); // Si falta algun fichero, sale del programa if (!Asset::get()->check()) throw std::runtime_error("Falta algun fichero"); } // Comprueba los parametros del programa void Director::checkProgramArguments(int argc, const char *argv[]) { // Establece la ruta del programa executable_path_ = argv[0]; // Comprueba el resto de parámetros for (int i = 1; i < argc; ++i) { std::string arg = argv[i]; if (arg == "--320x240") { overrides.param_file = arg; } else if (arg == "--clear_score") { overrides.clear_hi_score_table = true; } } } // Crea la carpeta del sistema donde guardar datos 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; { // 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 = {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); } } } } // Ejecuta la sección con el logo void Director::runLogo() { auto logo = std::make_unique(); logo->run(); } // Ejecuta la sección con la secuencia de introducción void Director::runIntro() { auto intro = std::make_unique(); intro->run(); } // Ejecuta la sección con el título del juego void Director::runTitle() { auto title = std::make_unique(); title->run(); } // Ejecuta la sección donde se juega al juego void Director::runGame() { const auto player_id = section::options == section::Options::GAME_PLAY_1P ? 1 : 2; #ifdef DEBUG constexpr auto current_stage = 0; #else constexpr auto current_stage = 0; #endif auto game = std::make_unique<Game>(player_id, current_stage, GAME_MODE_DEMO_OFF); game->run(); } // Ejecuta la sección donde se muestran las instrucciones void Director::runInstructions() { auto instructions = std::make_unique<Instructions>(); instructions->run(); } // Ejecuta la sección donde se muestran los creditos del programa void Director::runCredits() { auto credits = std::make_unique<Credits>(); credits->run(); } // Ejecuta la sección donde se muestra la tabla de puntuaciones void Director::runHiScoreTable() { auto hi_score_table = std::make_unique<HiScoreTable>(); hi_score_table->run(); } // Ejecuta el juego en modo demo void Director::runDemoGame() { const auto player_id = (rand() % 2) + 1; constexpr auto current_stage = 0; auto game = std::make_unique<Game>(player_id, current_stage, GAME_MODE_DEMO_ON); game->run(); } // Ejecuta la sección init void Director::runInit() { if (section::options == section::Options::RELOAD) { Resource::get()->reload(); } section::name = section::Name::LOGO; } int Director::run() { // Bucle principal while (section::name != section::Name::QUIT) { switch (section::name) { case section::Name::INIT: runInit(); break; case section::Name::LOGO: runLogo(); break; case section::Name::INTRO: runIntro(); break; case section::Name::TITLE: runTitle(); break; case section::Name::GAME: runGame(); break; case section::Name::HI_SCORE_TABLE: runHiScoreTable(); break; case section::Name::GAME_DEMO: runDemoGame(); break; case section::Name::INSTRUCTIONS: runInstructions(); break; case section::Name::CREDITS: runCredits(); break; default: break; } } std::string return_code; switch (section::options) { case section::Options::QUIT_WITH_KEYBOARD: return_code = "with keyboard"; break; case section::Options::QUIT_WITH_CONTROLLER: return_code = "with controller"; break; default: return_code = "from event"; break; } std::cout << "\nGame end " << return_code << std::endl; #ifndef VERBOSE // Habilita de nuevo los std::cout std::cout.rdbuf(orig_buf); #endif #ifdef ARCADE // Comprueba si ha de apagar el sistema if (section::options == section::Options::QUIT_WITH_CONTROLLER) shutdownSystem(); #endif return (section::options == section::Options::QUIT_WITH_CONTROLLER) ? 1 : 0; } // Obtiene una fichero a partir de un lang::Code std::string Director::getLangFile(lang::Code code) { switch (code) { case lang::Code::ba_BA: return Asset::get()->get("ba_BA.txt"); break; case lang::Code::es_ES: return Asset::get()->get("es_ES.txt"); break; case lang::Code::en_UK: return Asset::get()->get("en_UK.txt"); break; default: break; } return Asset::get()->get("en_UK.txt"); } #ifdef ARCADE // Apaga el sistema void Director::shutdownSystem() { #ifdef _WIN32 // Apaga el sistema en Windows system("shutdown /s /t 0"); #elif __APPLE__ // Apaga el sistema en macOS system("sudo shutdown -h now"); #elif __linux__ // Apaga el sistema en Linux system("shutdown -h now"); #else // Sistema operativo no compatible #error "Sistema operativo no soportado" #endif } #endif // ARCADE