Files
coffee_crisis/source/director.cpp

821 lines
28 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "director.h"
#include <SDL3/SDL.h>
#include <errno.h> // for errno, EEXIST, EACCES, ENAMETOO...
#include <stdio.h> // for printf, perror
#include <string.h> // for strcmp
#ifndef __EMSCRIPTEN__
#include <sys/stat.h> // for mkdir, stat, S_IRWXU
#include <unistd.h> // for getuid
#endif
#include <cstdlib> // for exit, EXIT_FAILURE, srand
#include <fstream> // for basic_ostream, operator<<, basi...
#include <iostream> // for cout
#include <memory>
#include <string> // for basic_string, operator+, char_t...
#include <vector> // for vector
#include "asset.h" // for Asset, assetType
#include "const.h" // for SECTION_PROG_LOGO, GAMECANVAS_H...
#include "game.h" // for Game
#include "input.h" // for Input, inputs_e, INPUT_USE_GAME...
#include "intro.h" // for Intro
#include "jail_audio.hpp" // for JA_Init
#include "lang.h" // for Lang, MAX_LANGUAGES, ba_BA, en_UK
#include "logo.h" // for Logo
#include "mouse.hpp" // for Mouse::handleEvent, Mouse::upda...
#include "screen.h" // for FILTER_NEAREST, Screen, FILTER_...
#include "texture.h" // for Texture
#include "title.h" // for Title
#include "utils.h" // for options_t, input_t, boolToString
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
#include <pwd.h>
#endif
// Constructor
Director::Director(int argc, const char *argv[]) {
std::cout << "Game start" << std::endl;
// Inicializa variables
section = new section_t();
section->name = SECTION_PROG_LOGO;
// Inicializa las opciones del programa
initOptions();
// Comprueba los parametros del programa
checkProgramArguments(argc, argv);
// Crea la carpeta del sistema donde guardar datos
createSystemFolder("jailgames");
#ifndef DEBUG
createSystemFolder("jailgames/coffee_crisis");
#else
createSystemFolder("jailgames/coffee_crisis_debug");
#endif
// Crea el objeto que controla los ficheros de recursos
asset = new Asset(executablePath);
asset->setVerbose(options->console);
// Si falta algún fichero no inicia el programa
if (!setFileList()) {
exit(EXIT_FAILURE);
}
// Carga el fichero de configuración
loadConfigFile();
// Inicializa SDL
initSDL();
// Inicializa JailAudio
initJailAudio();
// Establece el modo de escalado de texturas
Texture::setGlobalScaleMode(options->filter == FILTER_NEAREST ? SDL_SCALEMODE_NEAREST : SDL_SCALEMODE_LINEAR);
// Crea los objetos
lang = new Lang(asset);
lang->setLang(options->language);
input = new Input(asset->get("gamecontrollerdb.txt"));
initInput();
screen = new Screen(window, renderer, asset, options);
activeSection = ActiveSection::None;
}
Director::~Director() {
saveConfigFile();
// Libera las secciones primero: sus destructores tocan audio/render SDL
// (p.ej. Intro::~Intro llama a JA_DeleteMusic) y deben ejecutarse antes
// de SDL_Quit().
logo.reset();
intro.reset();
title.reset();
game.reset();
delete asset;
delete input;
delete screen;
delete lang;
delete options;
delete section;
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
std::cout << "\nBye!" << std::endl;
}
// Inicializa el objeto input
void Director::initInput() {
// Establece si ha de mostrar mensajes
input->setVerbose(options->console);
// Busca si hay un mando conectado
input->discoverGameController();
// Teclado - Movimiento del jugador
input->bindKey(input_up, SDL_SCANCODE_UP);
input->bindKey(input_down, SDL_SCANCODE_DOWN);
input->bindKey(input_left, SDL_SCANCODE_LEFT);
input->bindKey(input_right, SDL_SCANCODE_RIGHT);
input->bindKey(input_fire_left, SDL_SCANCODE_Q);
input->bindKey(input_fire_center, SDL_SCANCODE_W);
input->bindKey(input_fire_right, SDL_SCANCODE_E);
// Teclado - Otros
input->bindKey(input_accept, SDL_SCANCODE_RETURN);
input->bindKey(input_cancel, SDL_SCANCODE_ESCAPE);
input->bindKey(input_pause, SDL_SCANCODE_ESCAPE);
input->bindKey(input_exit, SDL_SCANCODE_ESCAPE);
input->bindKey(input_window_dec_size, SDL_SCANCODE_F1);
input->bindKey(input_window_inc_size, SDL_SCANCODE_F2);
input->bindKey(input_window_fullscreen, SDL_SCANCODE_F3);
// Mando - Movimiento del jugador
input->bindGameControllerButton(input_up, SDL_GAMEPAD_BUTTON_DPAD_UP);
input->bindGameControllerButton(input_down, SDL_GAMEPAD_BUTTON_DPAD_DOWN);
input->bindGameControllerButton(input_left, SDL_GAMEPAD_BUTTON_DPAD_LEFT);
input->bindGameControllerButton(input_right, SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
input->bindGameControllerButton(input_fire_left, SDL_GAMEPAD_BUTTON_WEST);
input->bindGameControllerButton(input_fire_center, SDL_GAMEPAD_BUTTON_NORTH);
input->bindGameControllerButton(input_fire_right, SDL_GAMEPAD_BUTTON_EAST);
// Mando - Otros
// SOUTH queda sin asignar para evitar salidas accidentales: pausa/cancel se hace con START/BACK.
input->bindGameControllerButton(input_accept, SDL_GAMEPAD_BUTTON_EAST);
#ifdef GAME_CONSOLE
input->bindGameControllerButton(input_pause, SDL_GAMEPAD_BUTTON_BACK);
input->bindGameControllerButton(input_exit, SDL_GAMEPAD_BUTTON_START);
#else
input->bindGameControllerButton(input_pause, SDL_GAMEPAD_BUTTON_START);
input->bindGameControllerButton(input_exit, SDL_GAMEPAD_BUTTON_BACK);
#endif
}
// Inicializa JailAudio
void Director::initJailAudio() {
JA_Init(48000, SDL_AUDIO_S16, 2);
}
// Arranca SDL y crea la ventana
bool Director::initSDL() {
// Indicador de éxito
bool success = true;
// Inicializa SDL
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD)) {
if (options->console) {
std::cout << "SDL could not initialize!\nSDL Error: " << SDL_GetError() << std::endl;
}
success = false;
} else {
// Inicia el generador de numeros aleatorios
std::srand(static_cast<unsigned int>(SDL_GetTicks()));
// Crea la ventana
int incW = 0;
int incH = 0;
if (options->borderEnabled) {
incW = options->borderWidth * 2;
incH = options->borderHeight * 2;
}
window = SDL_CreateWindow(WINDOW_CAPTION, (options->gameWidth + incW) * options->windowSize, (options->gameHeight + incH) * options->windowSize, 0);
if (window == nullptr) {
if (options->console) {
std::cout << "Window could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
}
success = false;
} else {
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
// Crea un renderizador para la ventana
renderer = SDL_CreateRenderer(window, NULL);
if (renderer == nullptr) {
if (options->console) {
std::cout << "Renderer could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
}
success = false;
} else {
// Activa vsync si es necesario
if (options->vSync) {
SDL_SetRenderVSync(renderer, 1);
}
// Inicializa el color de renderizado
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);
// Establece el tamaño del buffer de renderizado
SDL_SetRenderLogicalPresentation(renderer, options->gameWidth, options->gameHeight, SDL_LOGICAL_PRESENTATION_LETTERBOX);
// Establece el modo de mezcla
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
}
}
}
if (options->console) {
std::cout << std::endl;
}
return success;
}
// Crea el indice de ficheros
bool Director::setFileList() {
#ifdef MACOS_BUNDLE
const std::string prefix = "/../Resources";
#else
const std::string prefix = "";
#endif
// Ficheros de configuración
asset->add(systemFolder + "/config.txt", t_data, false, true);
asset->add(systemFolder + "/score.bin", t_data, false, true);
asset->add(prefix + "/data/config/demo.bin", t_data);
asset->add(prefix + "/data/config/gamecontrollerdb.txt", t_data);
// Musicas
asset->add(prefix + "/data/music/intro.ogg", t_music);
asset->add(prefix + "/data/music/playing.ogg", t_music);
asset->add(prefix + "/data/music/title.ogg", t_music);
// Sonidos
asset->add(prefix + "/data/sound/balloon.wav", t_sound);
asset->add(prefix + "/data/sound/bubble1.wav", t_sound);
asset->add(prefix + "/data/sound/bubble2.wav", t_sound);
asset->add(prefix + "/data/sound/bubble3.wav", t_sound);
asset->add(prefix + "/data/sound/bubble4.wav", t_sound);
asset->add(prefix + "/data/sound/bullet.wav", t_sound);
asset->add(prefix + "/data/sound/coffeeout.wav", t_sound);
asset->add(prefix + "/data/sound/hiscore.wav", t_sound);
asset->add(prefix + "/data/sound/itemdrop.wav", t_sound);
asset->add(prefix + "/data/sound/itempickup.wav", t_sound);
asset->add(prefix + "/data/sound/menu_move.wav", t_sound);
asset->add(prefix + "/data/sound/menu_select.wav", t_sound);
asset->add(prefix + "/data/sound/player_collision.wav", t_sound);
asset->add(prefix + "/data/sound/stage_change.wav", t_sound);
asset->add(prefix + "/data/sound/title.wav", t_sound);
asset->add(prefix + "/data/sound/clock.wav", t_sound);
asset->add(prefix + "/data/sound/powerball.wav", t_sound);
// Texturas
asset->add(prefix + "/data/gfx/balloon1.png", t_bitmap);
asset->add(prefix + "/data/gfx/balloon1.ani", t_data);
asset->add(prefix + "/data/gfx/balloon2.png", t_bitmap);
asset->add(prefix + "/data/gfx/balloon2.ani", t_data);
asset->add(prefix + "/data/gfx/balloon3.png", t_bitmap);
asset->add(prefix + "/data/gfx/balloon3.ani", t_data);
asset->add(prefix + "/data/gfx/balloon4.png", t_bitmap);
asset->add(prefix + "/data/gfx/balloon4.ani", t_data);
asset->add(prefix + "/data/gfx/bullet.png", t_bitmap);
asset->add(prefix + "/data/gfx/game_buildings.png", t_bitmap);
asset->add(prefix + "/data/gfx/game_clouds.png", t_bitmap);
asset->add(prefix + "/data/gfx/game_grass.png", t_bitmap);
asset->add(prefix + "/data/gfx/game_power_meter.png", t_bitmap);
asset->add(prefix + "/data/gfx/game_sky_colors.png", t_bitmap);
asset->add(prefix + "/data/gfx/game_text.png", t_bitmap);
asset->add(prefix + "/data/gfx/intro.png", t_bitmap);
asset->add(prefix + "/data/gfx/logo.png", t_bitmap);
asset->add(prefix + "/data/gfx/menu_game_over.png", t_bitmap);
asset->add(prefix + "/data/gfx/menu_game_over_end.png", t_bitmap);
asset->add(prefix + "/data/gfx/item_points1_disk.png", t_bitmap);
asset->add(prefix + "/data/gfx/item_points1_disk.ani", t_data);
asset->add(prefix + "/data/gfx/item_points2_gavina.png", t_bitmap);
asset->add(prefix + "/data/gfx/item_points2_gavina.ani", t_data);
asset->add(prefix + "/data/gfx/item_points3_pacmar.png", t_bitmap);
asset->add(prefix + "/data/gfx/item_points3_pacmar.ani", t_data);
asset->add(prefix + "/data/gfx/item_clock.png", t_bitmap);
asset->add(prefix + "/data/gfx/item_clock.ani", t_data);
asset->add(prefix + "/data/gfx/item_coffee.png", t_bitmap);
asset->add(prefix + "/data/gfx/item_coffee.ani", t_data);
asset->add(prefix + "/data/gfx/item_coffee_machine.png", t_bitmap);
asset->add(prefix + "/data/gfx/item_coffee_machine.ani", t_data);
asset->add(prefix + "/data/gfx/title_bg_tile.png", t_bitmap);
asset->add(prefix + "/data/gfx/title_coffee.png", t_bitmap);
asset->add(prefix + "/data/gfx/title_crisis.png", t_bitmap);
asset->add(prefix + "/data/gfx/title_dust.png", t_bitmap);
asset->add(prefix + "/data/gfx/title_dust.ani", t_data);
asset->add(prefix + "/data/gfx/title_gradient.png", t_bitmap);
asset->add(prefix + "/data/gfx/player_head.ani", t_data);
asset->add(prefix + "/data/gfx/player_body.ani", t_data);
asset->add(prefix + "/data/gfx/player_legs.ani", t_data);
asset->add(prefix + "/data/gfx/player_death.ani", t_data);
asset->add(prefix + "/data/gfx/player_fire.ani", t_data);
asset->add(prefix + "/data/gfx/player_bal1_head.png", t_bitmap);
asset->add(prefix + "/data/gfx/player_bal1_body.png", t_bitmap);
asset->add(prefix + "/data/gfx/player_bal1_legs.png", t_bitmap);
asset->add(prefix + "/data/gfx/player_bal1_death.png", t_bitmap);
asset->add(prefix + "/data/gfx/player_bal1_fire.png", t_bitmap);
asset->add(prefix + "/data/gfx/player_arounder_head.png", t_bitmap);
asset->add(prefix + "/data/gfx/player_arounder_body.png", t_bitmap);
asset->add(prefix + "/data/gfx/player_arounder_legs.png", t_bitmap);
asset->add(prefix + "/data/gfx/player_arounder_death.png", t_bitmap);
asset->add(prefix + "/data/gfx/player_arounder_fire.png", t_bitmap);
// Fuentes
asset->add(prefix + "/data/font/8bithud.png", t_font);
asset->add(prefix + "/data/font/8bithud.txt", t_font);
asset->add(prefix + "/data/font/nokia.png", t_font);
asset->add(prefix + "/data/font/nokia_big2.png", t_font);
asset->add(prefix + "/data/font/nokia.txt", t_font);
asset->add(prefix + "/data/font/nokia2.png", t_font);
asset->add(prefix + "/data/font/nokia2.txt", t_font);
asset->add(prefix + "/data/font/nokia_big2.txt", t_font);
asset->add(prefix + "/data/font/smb2_big.png", t_font);
asset->add(prefix + "/data/font/smb2_big.txt", t_font);
asset->add(prefix + "/data/font/smb2.png", t_font);
asset->add(prefix + "/data/font/smb2.txt", t_font);
// Textos
asset->add(prefix + "/data/lang/es_ES.txt", t_lang);
asset->add(prefix + "/data/lang/en_UK.txt", t_lang);
asset->add(prefix + "/data/lang/ba_BA.txt", t_lang);
// Menus
asset->add(prefix + "/data/menu/title.men", t_data);
asset->add(prefix + "/data/menu/title_gc.men", t_data);
asset->add(prefix + "/data/menu/options.men", t_data);
asset->add(prefix + "/data/menu/options_gc.men", t_data);
asset->add(prefix + "/data/menu/pause.men", t_data);
asset->add(prefix + "/data/menu/gameover.men", t_data);
asset->add(prefix + "/data/menu/player_select.men", t_data);
return asset->check();
}
// Inicializa las opciones del programa
void Director::initOptions() {
// Crea el puntero a la estructura de opciones
options = new options_t;
// Pone unos valores por defecto para las opciones de control
options->input.clear();
input_t inp;
inp.id = 0;
inp.name = "KEYBOARD";
inp.deviceType = INPUT_USE_KEYBOARD;
options->input.push_back(inp);
inp.id = 0;
inp.name = "GAME CONTROLLER";
inp.deviceType = INPUT_USE_GAMECONTROLLER;
options->input.push_back(inp);
// Opciones de video
options->gameWidth = GAMECANVAS_WIDTH;
options->gameHeight = GAMECANVAS_HEIGHT;
options->videoMode = 0;
options->windowSize = 3;
options->filter = FILTER_NEAREST;
options->vSync = true;
options->integerScale = true;
options->keepAspect = true;
options->borderWidth = 0;
options->borderHeight = 0;
options->borderEnabled = false;
// Opciones varios
options->playerSelected = 0;
options->difficulty = DIFFICULTY_NORMAL;
options->language = ba_BA;
options->console = false;
#ifdef __EMSCRIPTEN__
// En Emscripten la ventana la gestiona el navegador
options->windowSize = 1;
options->videoMode = 0;
options->integerScale = false;
#endif
}
// Comprueba los parametros del programa
void Director::checkProgramArguments(int argc, const char *argv[]) {
// Establece la ruta del programa
executablePath = argv[0];
// Comprueba el resto de parametros
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--console") == 0) {
options->console = true;
}
}
}
// Crea la carpeta del sistema donde guardar datos
void Director::createSystemFolder(const std::string &folder) {
#ifdef __EMSCRIPTEN__
// En Emscripten usamos una carpeta en MEMFS (no persistente)
systemFolder = "/config/" + folder;
#elif _WIN32
systemFolder = std::string(getenv("APPDATA")) + "/" + folder;
#elif __APPLE__
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
systemFolder = std::string(homedir) + "/Library/Application Support" + "/" + folder;
#elif __linux__
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
systemFolder = 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
#ifdef __EMSCRIPTEN__
// En Emscripten no necesitamos crear carpetas (MEMFS las crea automáticamente)
(void)folder;
#else
struct stat st = {0};
if (stat(systemFolder.c_str(), &st) == -1) {
errno = 0;
#ifdef _WIN32
int ret = mkdir(systemFolder.c_str());
#else
int ret = mkdir(systemFolder.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);
}
}
}
#endif
}
// Carga el fichero de configuración
bool Director::loadConfigFile() {
// Indicador de éxito en la carga
bool success = true;
// Variables para manejar el fichero
const std::string filePath = "config.txt";
std::string line;
std::ifstream file(asset->get(filePath));
// Si el fichero se puede abrir
if (file.good()) {
// Procesa el fichero linea a linea
if (options->console) {
std::cout << "Reading file " << filePath << std::endl;
}
while (std::getline(file, line)) {
// Comprueba que la linea no sea un comentario
if (line.substr(0, 1) != "#") {
// Encuentra la posición del caracter '='
int pos = line.find("=");
// Procesa las dos subcadenas
if (!setOptions(options, line.substr(0, pos), line.substr(pos + 1, line.length()))) {
if (options->console) {
std::cout << "Warning: file " << filePath << std::endl;
std::cout << "Unknown parameter " << line.substr(0, pos).c_str() << std::endl;
}
success = false;
}
}
}
// Cierra el fichero
if (options->console) {
std::cout << "Closing file " << filePath << std::endl;
}
file.close();
}
// El fichero no existe
else { // Crea el fichero con los valores por defecto
saveConfigFile();
}
// Normaliza los valores
if (options->videoMode != 0 && options->videoMode != SDL_WINDOW_FULLSCREEN) {
options->videoMode = 0;
}
if (options->windowSize < 1 || options->windowSize > 4) {
options->windowSize = 3;
}
if (options->language < 0 || options->language > MAX_LANGUAGES) {
options->language = en_UK;
}
return success;
}
// Guarda el fichero de configuración
bool Director::saveConfigFile() {
bool success = true;
// Crea y abre el fichero de texto
std::ofstream file(asset->get("config.txt"));
if (file.good()) {
if (options->console) {
std::cout << asset->get("config.txt") << " open for writing" << std::endl;
}
} else {
if (options->console) {
std::cout << asset->get("config.txt") << " can't be opened" << std::endl;
}
}
// Opciones g´raficas
file << "## VISUAL OPTIONS\n";
if (options->videoMode == 0) {
file << "videoMode=0\n";
}
else if (options->videoMode == SDL_WINDOW_FULLSCREEN) {
file << "videoMode=SDL_WINDOW_FULLSCREEN\n";
}
file << "windowSize=" + std::to_string(options->windowSize) + "\n";
if (options->filter == FILTER_NEAREST) {
file << "filter=FILTER_NEAREST\n";
} else {
file << "filter=FILTER_LINEAL\n";
}
file << "vSync=" + boolToString(options->vSync) + "\n";
file << "integerScale=" + boolToString(options->integerScale) + "\n";
file << "keepAspect=" + boolToString(options->keepAspect) + "\n";
file << "borderEnabled=" + boolToString(options->borderEnabled) + "\n";
file << "borderWidth=" + std::to_string(options->borderWidth) + "\n";
file << "borderHeight=" + std::to_string(options->borderHeight) + "\n";
// Otras opciones del programa
file << "\n## OTHER OPTIONS\n";
file << "language=" + std::to_string(options->language) + "\n";
file << "difficulty=" + std::to_string(options->difficulty) + "\n";
file << "input0=" + std::to_string(options->input[0].deviceType) + "\n";
file << "input1=" + std::to_string(options->input[1].deviceType) + "\n";
// Cierra el fichero
file.close();
return success;
}
// Gestiona las transiciones entre secciones
void Director::handleSectionTransition() {
// Determina qué sección debería estar activa
ActiveSection targetSection = ActiveSection::None;
switch (section->name) {
case SECTION_PROG_LOGO:
targetSection = ActiveSection::Logo;
break;
case SECTION_PROG_INTRO:
targetSection = ActiveSection::Intro;
break;
case SECTION_PROG_TITLE:
targetSection = ActiveSection::Title;
break;
case SECTION_PROG_GAME:
targetSection = ActiveSection::Game;
break;
}
// Si no ha cambiado, no hay nada que hacer
if (targetSection == activeSection) return;
// Destruye la sección anterior
logo.reset();
intro.reset();
title.reset();
game.reset();
// Crea la nueva sección
activeSection = targetSection;
switch (activeSection) {
case ActiveSection::Logo:
logo = std::make_unique<Logo>(renderer, screen, asset, input, section);
break;
case ActiveSection::Intro:
intro = std::make_unique<Intro>(renderer, screen, asset, input, lang, section);
break;
case ActiveSection::Title:
title = std::make_unique<Title>(renderer, screen, input, asset, options, lang, section);
break;
case ActiveSection::Game: {
const int numPlayers = section->subsection == SUBSECTION_GAME_PLAY_1P ? 1 : 2;
game = std::make_unique<Game>(numPlayers, 0, renderer, screen, asset, lang, input, false, options, section);
break;
}
case ActiveSection::None:
break;
}
}
// Ejecuta un frame del juego
SDL_AppResult Director::iterate() {
#ifdef __EMSCRIPTEN__
// En WASM no se puede salir: reinicia al logo
if (section->name == SECTION_PROG_QUIT) {
section->name = SECTION_PROG_LOGO;
}
#else
if (section->name == SECTION_PROG_QUIT) {
return SDL_APP_SUCCESS;
}
#endif
// Actualiza la visibilidad del cursor del ratón
Mouse::updateCursorVisibility(options->videoMode != 0);
// Gestiona las transiciones entre secciones
handleSectionTransition();
// Ejecuta un frame de la sección activa
switch (activeSection) {
case ActiveSection::Logo:
logo->iterate();
break;
case ActiveSection::Intro:
intro->iterate();
break;
case ActiveSection::Title:
title->iterate();
break;
case ActiveSection::Game:
game->iterate();
break;
case ActiveSection::None:
break;
}
return SDL_APP_CONTINUE;
}
// Procesa un evento
SDL_AppResult Director::handleEvent(SDL_Event *event) {
#ifndef __EMSCRIPTEN__
// Evento de salida de la aplicación
if (event->type == SDL_EVENT_QUIT) {
section->name = SECTION_PROG_QUIT;
return SDL_APP_SUCCESS;
}
#endif
// Hot-plug de mandos
if (event->type == SDL_EVENT_GAMEPAD_ADDED) {
std::string name;
if (input->handleGamepadAdded(event->gdevice.which, name)) {
screen->notify(name + " " + lang->getText(94),
color_t{0x40, 0xFF, 0x40},
color_t{0, 0, 0},
2500);
}
} else if (event->type == SDL_EVENT_GAMEPAD_REMOVED) {
std::string name;
if (input->handleGamepadRemoved(event->gdevice.which, name)) {
screen->notify(name + " " + lang->getText(95),
color_t{0xFF, 0x50, 0x50},
color_t{0, 0, 0},
2500);
}
}
// Gestiona la visibilidad del cursor según el movimiento del ratón
Mouse::handleEvent(*event, options->videoMode != 0);
// Reenvía el evento a la sección activa
switch (activeSection) {
case ActiveSection::Logo:
logo->handleEvent(event);
break;
case ActiveSection::Intro:
intro->handleEvent(event);
break;
case ActiveSection::Title:
title->handleEvent(event);
break;
case ActiveSection::Game:
game->handleEvent(event);
break;
case ActiveSection::None:
break;
}
return SDL_APP_CONTINUE;
}
// Asigna variables a partir de dos cadenas
bool Director::setOptions(options_t *options, std::string var, std::string value) {
// Indicador de éxito en la asignación
bool success = true;
// Opciones de video
if (var == "videoMode") {
if (value == "SDL_WINDOW_FULLSCREEN" || value == "SDL_WINDOW_FULLSCREEN_DESKTOP") {
options->videoMode = SDL_WINDOW_FULLSCREEN;
} else {
options->videoMode = 0;
}
}
else if (var == "windowSize") {
options->windowSize = std::stoi(value);
if ((options->windowSize < 1) || (options->windowSize > 4)) {
options->windowSize = 3;
}
}
else if (var == "filter") {
if (value == "FILTER_LINEAL") {
options->filter = FILTER_LINEAL;
} else {
options->filter = FILTER_NEAREST;
}
}
else if (var == "vSync") {
options->vSync = stringToBool(value);
}
else if (var == "integerScale") {
options->integerScale = stringToBool(value);
}
else if (var == "keepAspect") {
options->keepAspect = stringToBool(value);
}
else if (var == "borderEnabled") {
options->borderEnabled = stringToBool(value);
}
else if (var == "borderWidth") {
options->borderWidth = std::stoi(value);
}
else if (var == "borderHeight") {
options->borderHeight = std::stoi(value);
}
// Opciones varias
else if (var == "language") {
options->language = std::stoi(value);
}
else if (var == "difficulty") {
options->difficulty = std::stoi(value);
}
else if (var == "input0") {
options->input[0].deviceType = std::stoi(value);
}
else if (var == "input1") {
options->input[1].deviceType = std::stoi(value);
}
// Lineas vacias o que empiezan por comentario
else if (var == "" || var.substr(0, 1) == "#") {
}
else {
success = false;
}
return success;
}