Files
coffee_crisis_arcade_edition/source/director.cpp

659 lines
30 KiB
C++

// IWYU pragma: no_include <bits/chrono.h>
#include "director.h"
#include <SDL3/SDL.h> // Para SDL_Scancode, SDL_GamepadButton, SDL_LogCategory, SDL_LogInfo, SDL_SetLogPriority, SDL_LogPriority, SDL_Quit
#include <sys/stat.h> // Para mkdir, stat, S_IRWXU
#include <unistd.h> // Para getuid
#include <algorithm> // Para min
#include <cerrno> // Para errno, EEXIST, EACCES, ENAMETOOLONG
#include <cstdio> // Para printf, perror
#include <cstdlib> // Para exit, EXIT_FAILURE, size_t, srand, rand, system
#include <ctime> // Para time
#include <memory> // Para make_unique, unique_ptr
#include <span> // Para span
#include <stdexcept> // Para runtime_error
#include <string> // Para operator+, allocator, char_traits, operator==, string, basic_string
#include <vector> // Para vector
#include "asset.h" // Para Asset, AssetType
#include "audio.h" // Para Audio
#include "input.h" // Para Input, InputAction
#include "lang.h" // Para setLanguage
#include "manage_hiscore_table.h" // Para ManageHiScoreTable
#include "options.h" // Para GamepadOptions, controllers, loadFromFile, saveToFile, SettingsOptions, settings, getPlayerWhoUsesKeyboard, setKeyboardToPlayer
#include "param.h" // Para loadParamsFromFile
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "section.hpp" // Para Name, Options, name, options, AttractMode, attract_mode
#include "sections/credits.h" // Para Credits
#include "sections/game.h" // Para Game, GAME_MODE_DEMO_OFF, GAME_MODE_DEMO_ON
#include "sections/hiscore_table.h" // Para HiScoreTable
#include "sections/instructions.h" // Para Instructions
#include "sections/intro.h" // Para Intro
#include "sections/logo.h" // Para Logo
#include "sections/title.h" // Para Title
#include "ui/notifier.h" // Para Notifier
#include "ui/service_menu.h" // Para ServiceMenu
#include "utils.h" // Para Overrides, overrides, getPath
#ifndef _WIN32
#include <pwd.h> // Para getpwuid, passwd
#endif
// Constructor
Director::Director(int argc, std::span<char *> argv) {
#ifdef RECORDING
Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P;
#elif _DEBUG
Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P;
#else // NORMAL GAME
Section::name = Section::Name::LOGO;
Section::options = Section::Options::NONE;
#endif
Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO;
// Establece el nivel de prioridad de la categoría de registro
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
SDL_SetLogPriority(SDL_LOG_CATEGORY_TEST, SDL_LOG_PRIORITY_ERROR);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Game start");
// Inicia la semilla aleatoria usando el tiempo actual en segundos
std::srand(static_cast<unsigned int>(std::time(nullptr)));
// 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();
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nBye!");
}
// Inicializa todo
void Director::init() {
// Configuración inicial de recursos
Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos
setFileList(); // Crea el índice de archivos
Options::setFile(Asset::get()->get("config.txt")); // Establece el fichero de configuración
Options::loadFromFile(); // Carga el archivo de configuración
loadParams(); // Carga los parámetros del programa
loadScoreFile(); // Carga el archivo de puntuaciones
// Inicialización de subsistemas principales
Lang::setLanguage(Options::settings.language); // Carga el archivo de idioma
Screen::init(); // Inicializa la pantalla y el sistema de renderizado
Audio::init(); // Activa el sistema de audio
Resource::init(); // Inicializa el sistema de gestión de recursos
Input::init(Asset::get()->get("gamecontrollerdb.txt")); // Carga configuración de controles
bindInputs(); // Asigna los controles a la entrada del sistema
ServiceMenu::init(); // Inicializa el menú de servicio
Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones
Screen::get()->getSingletons(); // Obtiene los punteros al resto de singletones
}
// Cierra todo y libera recursos del sistema y de los singletons
void Director::close() {
// Guarda las opciones actuales en el archivo de configuración
Options::saveToFile();
// Libera los singletons y recursos en orden inverso al de inicialización
Notifier::destroy(); // Libera el sistema de notificaciones
ServiceMenu::destroy(); // Libera el sistema de menú de servicio
Input::destroy(); // Libera el sistema de entrada
Resource::destroy(); // Libera el sistema de recursos gráficos y de texto
Audio::destroy(); // Libera el sistema de audio
Screen::destroy(); // Libera el sistema de pantalla y renderizado
Asset::destroy(); // Libera el gestor de archivos
// Libera todos los recursos de SDL
SDL_Quit();
// Apaga el sistema
shutdownSystem(Section::options == Section::Options::SHUTDOWN);
}
// 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 PARAM_FILE_PATH = overrides.param_file == "--320x240" ? Asset::get()->get("param_320x240.txt") : Asset::get()->get("param_320x256.txt");
#endif
loadParamsFromFile(PARAM_FILE_PATH);
}
// Carga el fichero de puntuaciones
void Director::loadScoreFile() {
auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table);
#ifdef _DEBUG
manager->clear();
#else
if (overrides.clear_hi_score_table) {
manager->clear();
} else {
manager->loadFromFile(Asset::get()->get("score.bin"));
}
#endif
}
// Asigna los botones y teclas al objeto Input
void Director::bindInputs() {
// Teclado - Movimiento del jugador
Input::get()->bindKey(InputAction::UP, SDL_SCANCODE_UP);
Input::get()->bindKey(InputAction::DOWN, SDL_SCANCODE_DOWN);
Input::get()->bindKey(InputAction::LEFT, SDL_SCANCODE_LEFT);
Input::get()->bindKey(InputAction::RIGHT, SDL_SCANCODE_RIGHT);
// Teclado - Disparo del jugador
Input::get()->bindKey(InputAction::FIRE_LEFT, SDL_SCANCODE_Q);
Input::get()->bindKey(InputAction::FIRE_CENTER, SDL_SCANCODE_W);
Input::get()->bindKey(InputAction::FIRE_RIGHT, SDL_SCANCODE_E);
// Teclado - Interfaz
Input::get()->bindKey(InputAction::START, SDL_SCANCODE_RETURN);
// Teclado - Menu de servicio
Input::get()->bindKey(InputAction::SERVICE, SDL_SCANCODE_0);
Input::get()->bindKey(InputAction::SM_SELECT, SDL_SCANCODE_RETURN);
Input::get()->bindKey(InputAction::SM_BACK, SDL_SCANCODE_BACKSPACE);
// Teclado - Control del programa
Input::get()->bindKey(InputAction::EXIT, SDL_SCANCODE_ESCAPE);
Input::get()->bindKey(InputAction::PAUSE, SDL_SCANCODE_P);
Input::get()->bindKey(InputAction::BACK, SDL_SCANCODE_BACKSPACE);
Input::get()->bindKey(InputAction::WINDOW_DEC_SIZE, SDL_SCANCODE_F1);
Input::get()->bindKey(InputAction::WINDOW_INC_SIZE, SDL_SCANCODE_F2);
Input::get()->bindKey(InputAction::WINDOW_FULLSCREEN, SDL_SCANCODE_F3);
Input::get()->bindKey(InputAction::TOGGLE_VIDEO_SHADERS, SDL_SCANCODE_F4);
Input::get()->bindKey(InputAction::TOGGLE_VIDEO_INTEGER_SCALE, SDL_SCANCODE_F5);
Input::get()->bindKey(InputAction::TOGGLE_VIDEO_VSYNC, SDL_SCANCODE_F6);
Input::get()->bindKey(InputAction::TOGGLE_AUDIO, SDL_SCANCODE_F7);
Input::get()->bindKey(InputAction::TOGGLE_AUTO_FIRE, SDL_SCANCODE_F8);
Input::get()->bindKey(InputAction::CHANGE_LANG, SDL_SCANCODE_F9);
Input::get()->bindKey(InputAction::RESET, SDL_SCANCODE_F10);
Input::get()->bindKey(InputAction::SHOW_INFO, SDL_SCANCODE_F12);
// 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, InputAction::UP, SDL_GAMEPAD_BUTTON_DPAD_UP);
Input::get()->bindGameControllerButton(i, InputAction::DOWN, SDL_GAMEPAD_BUTTON_DPAD_DOWN);
Input::get()->bindGameControllerButton(i, InputAction::LEFT, SDL_GAMEPAD_BUTTON_DPAD_LEFT);
Input::get()->bindGameControllerButton(i, InputAction::RIGHT, SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
// Mando - Disparo del jugador
Input::get()->bindGameControllerButton(i, InputAction::FIRE_LEFT, SDL_GAMEPAD_BUTTON_WEST);
Input::get()->bindGameControllerButton(i, InputAction::FIRE_CENTER, SDL_GAMEPAD_BUTTON_NORTH);
Input::get()->bindGameControllerButton(i, InputAction::FIRE_RIGHT, SDL_GAMEPAD_BUTTON_EAST);
// Mando - Interfaz
Input::get()->bindGameControllerButton(i, InputAction::START, SDL_GAMEPAD_BUTTON_START);
Input::get()->bindGameControllerButton(i, InputAction::SERVICE, SDL_GAMEPAD_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) {
// Mando - Menu de servicio
Input::get()->bindGameControllerButton(i, InputAction::SM_SELECT, InputAction::FIRE_LEFT);
Input::get()->bindGameControllerButton(i, InputAction::SM_BACK, InputAction::FIRE_CENTER);
}
// 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 (Options::getPlayerWhoUsesKeyboard() == 0) {
Options::setKeyboardToPlayer(1);
}
}
// 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::DEMODATA);
Asset::get()->add(PREFIX + "/data/config/demo2.bin", AssetType::DEMODATA);
Asset::get()->add(PREFIX + "/data/config/gamecontrollerdb.txt", AssetType::DATA);
Asset::get()->add(PREFIX + "/data/config/formations.txt", AssetType::DATA);
Asset::get()->add(PREFIX + "/data/config/pools.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_pop0.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/balloon_pop1.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/balloon_pop2.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/balloon_pop3.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/balloon_bounce0.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/balloon_bounce1.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/balloon_bounce2.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/balloon_bounce3.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/coffee_out.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/continue_clock.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/debian_drop.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/debian_pickup.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/hi_score_achieved.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/item_drop.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/item_pickup.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/jump.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/power_ball_explosion.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/service_menu_adjust.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/service_menu_move.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/service_menu_select.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/stage_change.wav", AssetType::SOUND);
Asset::get()->add(PREFIX + "/data/sound/tabe_hit.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_aw_aw_aw.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/voice_thankyou.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 - Balloons
Asset::get()->add(PREFIX + "/data/gfx/balloon/balloon0.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/gfx/balloon/balloon0.ani", AssetType::ANIMATION);
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);
// Texturas - Explosiones
Asset::get()->add(PREFIX + "/data/gfx/balloon/explosion0.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/gfx/balloon/explosion0.ani", AssetType::ANIMATION);
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);
// Texturas - Power Ball
Asset::get()->add(PREFIX + "/data/gfx/balloon/powerball.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/gfx/balloon/powerball.ani", AssetType::ANIMATION);
// Texturas - Bala
Asset::get()->add(PREFIX + "/data/gfx/bullet/bullet.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/gfx/bullet/bullet.ani", AssetType::ANIMATION);
// Texturas - Tabe
Asset::get()->add(PREFIX + "/data/gfx/tabe/tabe.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/gfx/tabe/tabe.ani", AssetType::ANIMATION);
// Texturas - 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);
// Texturas - Intro
Asset::get()->add(PREFIX + "/data/gfx/intro/intro1.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/gfx/intro/intro2.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/gfx/intro/intro3.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/gfx/intro/intro4.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/gfx/intro/intro5.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/gfx/intro/intro6.png", AssetType::BITMAP);
// Texturas - 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);
// Texturas - 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_debian.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/gfx/item/item_debian.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);
// Texturas - 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);
// Texturas - Jugador 1
Asset::get()->add(PREFIX + "/data/gfx/player/player1.gif", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/gfx/player/player1_coffee1.pal", AssetType::PALETTE);
Asset::get()->add(PREFIX + "/data/gfx/player/player1_coffee2.pal", AssetType::PALETTE);
Asset::get()->add(PREFIX + "/data/gfx/player/player1_invencible.pal", AssetType::PALETTE);
Asset::get()->add(PREFIX + "/data/gfx/player/player1_power.png", AssetType::BITMAP);
// Texturas - Jugador 2
Asset::get()->add(PREFIX + "/data/gfx/player/player2.gif", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/gfx/player/player2_coffee1.pal", AssetType::PALETTE);
Asset::get()->add(PREFIX + "/data/gfx/player/player2_coffee2.pal", AssetType::PALETTE);
Asset::get()->add(PREFIX + "/data/gfx/player/player2_invencible.pal", 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/aseprite.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/font/aseprite.txt", AssetType::FONT);
Asset::get()->add(PREFIX + "/data/font/smb2.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/font/smb2_grad.png", AssetType::BITMAP);
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);
Asset::get()->add(PREFIX + "/data/font/04b_25_metal.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/font/04b_25_grey.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/font/04b_25_flat.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/font/04b_25_reversed.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/font/04b_25_flat_2x.png", AssetType::BITMAP);
Asset::get()->add(PREFIX + "/data/font/04b_25_reversed_2x.png", AssetType::BITMAP);
// Textos
Asset::get()->add(PREFIX + "/data/lang/es_ES.json", AssetType::LANG);
Asset::get()->add(PREFIX + "/data/lang/en_UK.json", AssetType::LANG);
Asset::get()->add(PREFIX + "/data/lang/ba_BA.json", 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, std::span<char *> argv) {
// Establece la ruta del programa
executable_path_ = getPath(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>();
logo->run();
}
// Ejecuta la sección con la secuencia de introducción
void Director::runIntro() {
auto intro = std::make_unique<Intro>();
intro->run();
}
// Ejecuta la sección con el título del juego
void Director::runTitle() {
auto title = std::make_unique<Title>();
title->run();
}
// Ejecuta la sección donde se juega al juego
void Director::runGame() {
int player_id = 1;
switch (Section::options) {
case Section::Options::GAME_PLAY_1P:
player_id = 1;
break;
case Section::Options::GAME_PLAY_2P:
player_id = 2;
break;
case Section::Options::GAME_PLAY_BOTH:
player_id = 0;
break;
default:
player_id = 1;
break;
}
#ifdef _DEBUG
constexpr int CURRENT_STAGE = 0;
#else
constexpr int 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();
}
// Reinicia objetos y vuelve a la sección inicial
void Director::reset() {
Options::saveToFile();
Options::loadFromFile();
Lang::setLanguage(Options::settings.language);
Audio::get()->stopMusic();
Audio::get()->stopAllSounds();
{
Resource::get()->reload();
}
Input::get()->discoverGameControllers();
bindInputs();
ServiceMenu::get()->reset();
Section::name = Section::Name::LOGO;
}
auto Director::run() -> int {
// Bucle principal
while (Section::name != Section::Name::QUIT) {
switch (Section::name) {
case Section::Name::RESET:
reset();
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;
}
}
return 0;
}
// Apaga el sistema
void Director::shutdownSystem(bool should_shutdown) {
if (should_shutdown) {
#ifdef _WIN32
// Apaga el sistema en Windows
system("shutdown /s /t 5");
#elif __APPLE__
// Apaga el sistema en macOS
system("sudo shutdown -h +0.1");
#elif __linux__
// Apaga el sistema en Linux
system("sleep 5; shutdown -h now");
#else
// Sistema operativo no compatible
#error "Sistema operativo no soportado"
#endif
}
}