modernitzat el sistema d'opcions

This commit is contained in:
2026-04-17 19:36:40 +02:00
parent 1bb0ebdef8
commit 7f703390f9
12 changed files with 690 additions and 553 deletions

View File

@@ -11,6 +11,8 @@
#include "core/rendering/text.h" // for Text, TXT_CENTER, TXT_COLOR, TXT_STROKE
#include "core/resources/asset.h" // for Asset
#include "core/resources/resource.h"
#include "game/defaults.hpp" // for GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT
#include "game/options.hpp" // for Options::video, Options::settings
#ifndef NO_SHADERS
#include "core/rendering/sdl3gpu/sdl3gpu_shader.hpp" // for Rendering::SDL3GPUShader
@@ -59,17 +61,14 @@ namespace {
#endif // __EMSCRIPTEN__
// Constructor
Screen::Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, options_t *options) {
Screen::Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset) {
// Inicializa variables
this->window = window;
this->renderer = renderer;
this->options = options;
this->asset = asset;
gameCanvasWidth = options->gameWidth;
gameCanvasHeight = options->gameHeight;
borderWidth = options->borderWidth * 2;
borderHeight = options->borderHeight * 2;
gameCanvasWidth = GAMECANVAS_WIDTH;
gameCanvasHeight = GAMECANVAS_HEIGHT;
// Define el color del borde para el modo de pantalla completa
borderColor = {0x00, 0x00, 0x00};
@@ -80,7 +79,7 @@ Screen::Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, options
// Mirror del pattern de jaildoctors_dilemma (que usa exactament 256×192 i
// funciona) on `initSDLVideo` configura la presentation abans de crear cap
// textura.
setVideoMode(options->videoMode != 0);
setVideoMode(Options::video.fullscreen);
// Força al window manager a completar el resize/posicionat abans de passar
// la ventana al dispositiu GPU. Sense açò en Linux/X11 hi ha un race
@@ -92,10 +91,10 @@ Screen::Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, options
// ARGB8888 per simplificar el readback cap al pipeline SDL3 GPU.
gameCanvas = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, gameCanvasWidth, gameCanvasHeight);
if (gameCanvas != nullptr) {
SDL_SetTextureScaleMode(gameCanvas, options->filter == FILTER_NEAREST ? SDL_SCALEMODE_NEAREST : SDL_SCALEMODE_LINEAR);
SDL_SetTextureScaleMode(gameCanvas, Options::video.scale_mode);
}
if (gameCanvas == nullptr) {
if (options->console) {
if (Options::settings.console) {
std::cout << "gameCanvas could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
}
}
@@ -182,7 +181,7 @@ void Screen::blit() {
}
SDL_SetRenderTarget(renderer, nullptr);
if (options->videoShaderEnabled) {
if (Options::video.shader.enabled) {
// Ruta normal: shader amb els seus params.
shader_backend_->uploadPixels(pixel_buffer_.data(), gameCanvasWidth, gameCanvasHeight);
shader_backend_->render();
@@ -237,54 +236,54 @@ void Screen::setVideoMode(bool fullscreen) {
// Cambia entre pantalla completa y ventana
void Screen::toggleVideoMode() {
setVideoMode(options->videoMode == 0);
setVideoMode(!Options::video.fullscreen);
}
// Reduce el zoom de la ventana
auto Screen::decWindowZoom() -> bool {
if (options->videoMode != 0) { return false; }
const int PREV = options->windowSize;
options->windowSize = std::max(options->windowSize - 1, WINDOW_ZOOM_MIN);
if (options->windowSize == PREV) { return false; }
if (Options::video.fullscreen) { return false; }
const int PREV = Options::window.zoom;
Options::window.zoom = std::max(Options::window.zoom - 1, WINDOW_ZOOM_MIN);
if (Options::window.zoom == PREV) { return false; }
setVideoMode(false);
return true;
}
// Aumenta el zoom de la ventana
auto Screen::incWindowZoom() -> bool {
if (options->videoMode != 0) { return false; }
const int PREV = options->windowSize;
options->windowSize = std::min(options->windowSize + 1, WINDOW_ZOOM_MAX);
if (options->windowSize == PREV) { return false; }
if (Options::video.fullscreen) { return false; }
const int PREV = Options::window.zoom;
Options::window.zoom = std::min(Options::window.zoom + 1, WINDOW_ZOOM_MAX);
if (Options::window.zoom == PREV) { return false; }
setVideoMode(false);
return true;
}
// Establece el zoom de la ventana directamente
auto Screen::setWindowZoom(int zoom) -> bool {
if (options->videoMode != 0) { return false; }
if (Options::video.fullscreen) { return false; }
if (zoom < WINDOW_ZOOM_MIN || zoom > WINDOW_ZOOM_MAX) { return false; }
if (zoom == options->windowSize) { return false; }
options->windowSize = zoom;
if (zoom == Options::window.zoom) { return false; }
Options::window.zoom = zoom;
setVideoMode(false);
return true;
}
// Establece el escalado entero
void Screen::setIntegerScale(bool enabled) {
if (options->integerScale == enabled) { return; }
options->integerScale = enabled;
setVideoMode(options->videoMode != 0);
if (Options::video.integer_scale == enabled) { return; }
Options::video.integer_scale = enabled;
setVideoMode(Options::video.fullscreen);
}
// Alterna el escalado entero
void Screen::toggleIntegerScale() {
setIntegerScale(!options->integerScale);
setIntegerScale(!Options::video.integer_scale);
}
// Establece el V-Sync
void Screen::setVSync(bool enabled) {
options->vSync = enabled;
Options::video.vsync = enabled;
SDL_SetRenderVSync(renderer, enabled ? 1 : SDL_RENDERER_VSYNC_DISABLED);
#ifndef NO_SHADERS
if (shader_backend_) {
@@ -295,7 +294,7 @@ void Screen::setVSync(bool enabled) {
// Alterna el V-Sync
void Screen::toggleVSync() {
setVSync(!options->vSync);
setVSync(!Options::video.vsync);
}
// Cambia el color del borde
@@ -322,15 +321,9 @@ void Screen::applyFullscreen(bool fullscreen) {
// Calcula windowWidth/Height/dest para el modo ventana y aplica SDL_SetWindowSize
void Screen::applyWindowedLayout() {
if (options->borderEnabled) {
windowWidth = gameCanvasWidth + borderWidth;
windowHeight = gameCanvasHeight + borderHeight;
dest = {0 + (borderWidth / 2), 0 + (borderHeight / 2), gameCanvasWidth, gameCanvasHeight};
} else {
windowWidth = gameCanvasWidth;
windowHeight = gameCanvasHeight;
dest = {0, 0, gameCanvasWidth, gameCanvasHeight};
}
windowWidth = gameCanvasWidth;
windowHeight = gameCanvasHeight;
dest = {0, 0, gameCanvasWidth, gameCanvasHeight};
#ifdef __EMSCRIPTEN__
windowWidth *= WASM_RENDER_SCALE;
@@ -340,7 +333,7 @@ void Screen::applyWindowedLayout() {
#endif
// Modifica el tamaño de la ventana
SDL_SetWindowSize(window, windowWidth * options->windowSize, windowHeight * options->windowSize);
SDL_SetWindowSize(window, windowWidth * Options::window.zoom, windowHeight * Options::window.zoom);
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
}
@@ -350,9 +343,9 @@ void Screen::applyFullscreenLayout() {
computeFullscreenGameRect();
}
// Calcula el rectángulo dest para fullscreen: integerScale / keepAspect / stretched
// Calcula el rectángulo dest para fullscreen: integer_scale / aspect ratio
void Screen::computeFullscreenGameRect() {
if (options->integerScale) {
if (Options::video.integer_scale) {
// Calcula el tamaño de la escala máxima
int scale = 0;
while (((gameCanvasWidth * (scale + 1)) <= windowWidth) && ((gameCanvasHeight * (scale + 1)) <= windowHeight)) {
@@ -363,7 +356,8 @@ void Screen::computeFullscreenGameRect() {
dest.h = gameCanvasHeight * scale;
dest.x = (windowWidth - dest.w) / 2;
dest.y = (windowHeight - dest.h) / 2;
} else if (options->keepAspect) {
} else {
// Manté la relació d'aspecte sense escalat enter (letterbox/pillarbox).
float ratio = (float)gameCanvasWidth / (float)gameCanvasHeight;
if ((windowWidth - gameCanvasWidth) >= (windowHeight - gameCanvasHeight)) {
dest.h = windowHeight;
@@ -376,21 +370,13 @@ void Screen::computeFullscreenGameRect() {
dest.x = (windowWidth - dest.w) / 2;
dest.y = (windowHeight - dest.h) / 2;
}
} else {
dest.w = windowWidth;
dest.h = windowHeight;
dest.x = dest.y = 0;
}
}
// Aplica la logical presentation y persiste el estado en options
void Screen::applyLogicalPresentation(bool fullscreen) {
SDL_SetRenderLogicalPresentation(renderer, windowWidth, windowHeight, SDL_LOGICAL_PRESENTATION_LETTERBOX);
// Actualiza las opciones
options->videoMode = fullscreen ? SDL_WINDOW_FULLSCREEN : 0;
options->screen.windowWidth = windowWidth;
options->screen.windowHeight = windowHeight;
Options::video.fullscreen = fullscreen;
}
// ============================================================================
@@ -436,13 +422,13 @@ void Screen::handleCanvasResized() {
// La crida a SDL_SetWindowFullscreen + SDL_SetRenderLogicalPresentation
// que fa setVideoMode és l'única manera de resincronitzar l'estat intern
// de SDL amb el canvas HTML real.
setVideoMode(options->videoMode != 0);
setVideoMode(Options::video.fullscreen);
#endif
}
void Screen::syncFullscreenFlagFromBrowser(bool isFullscreen) {
#ifdef __EMSCRIPTEN__
options->videoMode = isFullscreen ? SDL_WINDOW_FULLSCREEN : 0;
Options::video.fullscreen = isFullscreen;
#else
(void)isFullscreen;
#endif
@@ -474,10 +460,7 @@ void Screen::applyShaderParams() {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return;
}
const Rendering::ShaderType ACTIVE = options->videoShaderType == 1
? Rendering::ShaderType::CRTPI
: Rendering::ShaderType::POSTFX;
shader_backend_->setActiveShader(ACTIVE);
shader_backend_->setActiveShader(Options::video.shader.current_shader);
// Preset per defecte (carregador YAML pendent). Valors estil "CRT" de CCAE.
Rendering::PostFXParams POSTFX;
@@ -497,17 +480,17 @@ void Screen::initShaders() {
shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>();
const std::string FALLBACK_DRIVER = "none";
shader_backend_->setPreferredDriver(
options->videoGpuAcceleration ? options->videoGpuPreferredDriver : FALLBACK_DRIVER);
Options::video.gpu.acceleration ? Options::video.gpu.preferred_driver : FALLBACK_DRIVER);
}
if (!shader_backend_->isHardwareAccelerated()) {
const bool ok = shader_backend_->init(window, gameCanvas, "", "");
if (options->console) {
if (Options::settings.console) {
std::cout << "Screen::initShaders: SDL3GPUShader::init() = " << (ok ? "OK" : "FAILED") << '\n';
}
}
if (shader_backend_->isHardwareAccelerated()) {
shader_backend_->setScaleMode(options->integerScale);
shader_backend_->setVSync(options->vSync);
shader_backend_->setScaleMode(Options::video.integer_scale);
shader_backend_->setVSync(Options::video.vsync);
applyShaderParams(); // aplica preset del shader actiu
}
#endif
@@ -526,8 +509,8 @@ void Screen::shutdownShaders() {
}
void Screen::setGpuAcceleration(bool enabled) {
if (options->videoGpuAcceleration == enabled) { return; }
options->videoGpuAcceleration = enabled;
if (Options::video.gpu.acceleration == enabled) { return; }
Options::video.gpu.acceleration = enabled;
// Soft toggle: el backend es manté viu (vegeu shutdownShaders). El canvi
// s'aplica al proper arrencada. S'emet una notificació perquè l'usuari
// sap que ha tocat la tecla però el canvi no és immediat.
@@ -538,7 +521,7 @@ void Screen::setGpuAcceleration(bool enabled) {
}
void Screen::toggleGpuAcceleration() {
setGpuAcceleration(!options->videoGpuAcceleration);
setGpuAcceleration(!Options::video.gpu.acceleration);
}
auto Screen::isGpuAccelerated() const -> bool {
@@ -550,8 +533,8 @@ auto Screen::isGpuAccelerated() const -> bool {
}
void Screen::setShaderEnabled(bool enabled) {
if (options->videoShaderEnabled == enabled) { return; }
options->videoShaderEnabled = enabled;
if (Options::video.shader.enabled == enabled) { return; }
Options::video.shader.enabled = enabled;
#ifndef NO_SHADERS
if (enabled) {
applyShaderParams(); // restaura preset del shader actiu
@@ -566,17 +549,17 @@ void Screen::setShaderEnabled(bool enabled) {
}
void Screen::toggleShaderEnabled() {
setShaderEnabled(!options->videoShaderEnabled);
setShaderEnabled(!Options::video.shader.enabled);
}
auto Screen::isShaderEnabled() const -> bool {
return options->videoShaderEnabled;
return Options::video.shader.enabled;
}
#ifndef NO_SHADERS
void Screen::setActiveShader(Rendering::ShaderType type) {
options->videoShaderType = type == Rendering::ShaderType::CRTPI ? 1 : 0;
if (options->videoShaderEnabled) {
Options::video.shader.current_shader = type;
if (Options::video.shader.enabled) {
applyShaderParams();
}
const color_t MAGENTA = {0xFF, 0x00, 0xFF};
@@ -586,7 +569,7 @@ void Screen::setActiveShader(Rendering::ShaderType type) {
}
auto Screen::getActiveShader() const -> Rendering::ShaderType {
return options->videoShaderType == 1 ? Rendering::ShaderType::CRTPI : Rendering::ShaderType::POSTFX;
return Options::video.shader.current_shader;
}
#endif
@@ -597,6 +580,8 @@ void Screen::toggleActiveShader() {
: Rendering::ShaderType::POSTFX;
setActiveShader(NEXT);
#else
options->videoShaderType = options->videoShaderType == 1 ? 0 : 1;
Options::video.shader.current_shader = Options::video.shader.current_shader == Rendering::ShaderType::POSTFX
? Rendering::ShaderType::CRTPI
: Rendering::ShaderType::POSTFX;
#endif
}

View File

@@ -18,10 +18,6 @@ namespace Rendering {
class Asset;
class Text;
// Tipos de filtro
constexpr int FILTER_NEAREST = 0;
constexpr int FILTER_LINEAL = 1;
class Screen {
public:
// Constantes
@@ -35,7 +31,7 @@ class Screen {
#endif
// Constructor y destructor
Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, options_t *options);
Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset);
~Screen();
// Render loop
@@ -104,15 +100,12 @@ class Screen {
SDL_Renderer *renderer; // El renderizador de la ventana
Asset *asset; // Objeto con el listado de recursos
SDL_Texture *gameCanvas; // Textura para completar la ventana de juego hasta la pantalla completa
options_t *options; // Variable con todas las opciones del programa
// Variables
int windowWidth; // Ancho de la pantalla o ventana
int windowHeight; // Alto de la pantalla o ventana
int gameCanvasWidth; // Resolución interna del juego. Es el ancho de la textura donde se dibuja el juego
int gameCanvasHeight; // Resolución interna del juego. Es el alto de la textura donde se dibuja el juego
int borderWidth; // Anchura del borde
int borderHeight; // Altura del borde
SDL_Rect dest; // Coordenadas donde se va a dibujar la textura del juego sobre la pantalla o ventana
color_t borderColor; // Color del borde añadido a la textura de juego para rellenar la pantalla

View File

@@ -21,17 +21,18 @@
#include "core/input/input.h" // for Input, inputs_e, INPUT_USE_GAME...
#include "core/input/mouse.hpp" // for Mouse::handleEvent, Mouse::upda...
#include "core/locale/lang.h" // for Lang, MAX_LANGUAGES, ba_BA, en_UK
#include "core/rendering/screen.h" // for FILTER_NEAREST, Screen, FILTER_...
#include "core/rendering/screen.h" // for Screen
#include "core/rendering/texture.h" // for Texture
#include "core/resources/asset.h" // for Asset, assetType
#include "core/resources/resource.h"
#include "core/resources/resource_helper.h"
#include "game/defaults.hpp" // for SECTION_PROG_LOGO, GAMECANVAS_H...
#include "game/game.h" // for Game
#include "game/options.hpp" // for Options::init, loadFromFile...
#include "game/scenes/intro.h" // for Intro
#include "game/scenes/logo.h" // for Logo
#include "game/scenes/title.h" // for Title
#include "utils/utils.h" // for options_t, input_t, boolToString
#include "utils/utils.h" // for input_t, boolToString
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
#include <pwd.h>
@@ -44,10 +45,10 @@ Director::Director(int argc, const char *argv[]) {
section = new section_t();
section->name = SECTION_PROG_LOGO;
// Inicializa las opciones del programa
initOptions();
// Inicializa las opciones del programa (defaults + dispositivos d'entrada)
Options::init();
// Comprueba los parametros del programa
// Comprueba los parametros del programa (pot activar console)
checkProgramArguments(argc, argv);
// Crea la carpeta del sistema donde guardar datos
@@ -58,6 +59,11 @@ Director::Director(int argc, const char *argv[]) {
createSystemFolder("jailgames/coffee_crisis_debug");
#endif
// Estableix el fitxer de configuració i carrega les opcions (o crea el
// YAML amb defaults si no existeix).
Options::setConfigFile(systemFolder + "/config.yaml");
Options::loadFromFile();
// Inicializa el sistema de recursos (pack + fallback).
// En wasm siempre se usa filesystem (MEMFS) porque el propio --preload-file
// de emscripten ya empaqueta data/ — no hay resources.pack.
@@ -77,16 +83,13 @@ Director::Director(int argc, const char *argv[]) {
// Crea el objeto que controla los ficheros de recursos
asset = new Asset(executablePath);
asset->setVerbose(options->console);
asset->setVerbose(Options::settings.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();
@@ -94,11 +97,11 @@ Director::Director(int argc, const char *argv[]) {
initJailAudio();
// Establece el modo de escalado de texturas
Texture::setGlobalScaleMode(options->filter == FILTER_NEAREST ? SDL_SCALEMODE_NEAREST : SDL_SCALEMODE_LINEAR);
Texture::setGlobalScaleMode(Options::video.scale_mode);
// Crea los objetos
lang = new Lang(asset);
lang->setLang(options->language);
lang->setLang(Options::settings.language);
#ifdef __EMSCRIPTEN__
input = new Input("/gamecontrollerdb.txt");
@@ -122,10 +125,10 @@ Director::Director(int argc, const char *argv[]) {
//
// Por eso el constructor de Screen NO carga notificationText desde
// Resource; se enlaza después vía `screen->initNotifications()`.
screen = new Screen(window, renderer, asset, options);
screen = new Screen(window, renderer, asset);
#ifndef NO_SHADERS
if (options->videoGpuAcceleration) {
if (Options::video.gpu.acceleration) {
screen->initShaders();
}
#endif
@@ -142,7 +145,7 @@ Director::Director(int argc, const char *argv[]) {
}
Director::~Director() {
saveConfigFile();
Options::saveToFile();
// Libera las secciones primero: sus destructores tocan audio/render SDL
// (p.ej. Intro::~Intro llama a JA_DeleteMusic) y deben ejecutarse antes
@@ -162,7 +165,6 @@ Director::~Director() {
delete asset;
delete input;
delete lang;
delete options;
delete section;
SDL_DestroyRenderer(renderer);
@@ -178,7 +180,7 @@ Director::~Director() {
// Inicializa el objeto input
void Director::initInput() {
// Establece si ha de mostrar mensajes
input->setVerbose(options->console);
input->setVerbose(Options::settings.console);
// Busca si hay un mando conectado
input->discoverGameController();
@@ -237,7 +239,7 @@ bool Director::initSDL() {
// Inicializa SDL
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD)) {
if (options->console) {
if (Options::settings.console) {
std::cout << "SDL could not initialize!\nSDL Error: " << SDL_GetError() << std::endl;
}
success = false;
@@ -246,15 +248,13 @@ bool Director::initSDL() {
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);
window = SDL_CreateWindow(
Options::window.caption.c_str(),
GAMECANVAS_WIDTH * Options::window.zoom,
GAMECANVAS_HEIGHT * Options::window.zoom,
0);
if (window == nullptr) {
if (options->console) {
if (Options::settings.console) {
std::cout << "Window could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
}
success = false;
@@ -265,7 +265,7 @@ bool Director::initSDL() {
renderer = SDL_CreateRenderer(window, NULL);
if (renderer == nullptr) {
if (options->console) {
if (Options::settings.console) {
std::cout << "Renderer could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
}
success = false;
@@ -275,7 +275,7 @@ bool Director::initSDL() {
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
// Activa vsync si es necesario
if (options->vSync) {
if (Options::video.vsync) {
SDL_SetRenderVSync(renderer, 1);
}
@@ -283,7 +283,7 @@ bool Director::initSDL() {
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);
SDL_SetRenderLogicalPresentation(renderer, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT, SDL_LOGICAL_PRESENTATION_LETTERBOX);
// Establece el modo de mezcla
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
@@ -291,7 +291,7 @@ bool Director::initSDL() {
}
}
if (options->console) {
if (Options::settings.console) {
std::cout << std::endl;
}
return success;
@@ -306,7 +306,6 @@ bool Director::setFileList() {
#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/demo/demo.bin", t_data);
@@ -426,52 +425,6 @@ bool Director::setFileList() {
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 = 4;
options->videoMode = 0;
options->integerScale = true;
#endif
}
// Comprueba los parametros del programa
void Director::checkProgramArguments(int argc, const char *argv[]) {
// Establece la ruta del programa
@@ -480,7 +433,7 @@ void Director::checkProgramArguments(int argc, const char *argv[]) {
// Comprueba el resto de parametros
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--console") == 0) {
options->console = true;
Options::settings.console = true;
}
}
}
@@ -548,127 +501,6 @@ void Director::createSystemFolder(const std::string &folder) {
#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";
// Opciones de GPU / shaders (post-procesado)
file << "videoGpuAcceleration=" + boolToString(options->videoGpuAcceleration) + "\n";
file << "videoGpuPreferredDriver=" + options->videoGpuPreferredDriver + "\n";
file << "videoShaderEnabled=" + boolToString(options->videoShaderEnabled) + "\n";
file << "videoShaderType=" + std::to_string(options->videoShaderType) + "\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
@@ -707,11 +539,11 @@ void Director::handleSectionTransition() {
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);
title = std::make_unique<Title>(renderer, screen, input, asset, 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);
game = std::make_unique<Game>(numPlayers, 0, renderer, screen, asset, lang, input, false, section);
break;
}
case ActiveSection::None:
@@ -733,7 +565,7 @@ SDL_AppResult Director::iterate() {
#endif
// Actualiza la visibilidad del cursor del ratón
Mouse::updateCursorVisibility(options->videoMode != 0);
Mouse::updateCursorVisibility(Options::video.fullscreen);
// Gestiona las transiciones entre secciones
handleSectionTransition();
@@ -789,7 +621,7 @@ SDL_AppResult Director::handleEvent(SDL_Event *event) {
}
// Gestiona la visibilidad del cursor según el movimiento del ratón
Mouse::handleEvent(*event, options->videoMode != 0);
Mouse::handleEvent(*event, Options::video.fullscreen);
// Reenvía el evento a la sección activa
switch (activeSection) {
@@ -812,103 +644,3 @@ SDL_AppResult Director::handleEvent(SDL_Event *event) {
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 de GPU / shaders
else if (var == "videoGpuAcceleration") {
options->videoGpuAcceleration = stringToBool(value);
}
else if (var == "videoGpuPreferredDriver") {
options->videoGpuPreferredDriver = value;
}
else if (var == "videoShaderEnabled") {
options->videoShaderEnabled = stringToBool(value);
}
else if (var == "videoShaderType") {
options->videoShaderType = std::stoi(value);
if (options->videoShaderType < 0 || options->videoShaderType > 1) {
options->videoShaderType = 0;
}
}
// 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;
}

View File

@@ -12,12 +12,8 @@ class Lang;
class Logo;
class Screen;
class Title;
struct options_t;
struct section_t;
// Textos
constexpr const char *WINDOW_CAPTION = "© 2020 Coffee Crisis — JailDesigner";
// Secciones activas del Director
enum class ActiveSection { None,
Logo,
@@ -44,7 +40,6 @@ class Director {
std::unique_ptr<Game> game;
// Variables
struct options_t *options; // Variable con todas las opciones del programa
std::string executablePath; // Path del ejecutable
std::string systemFolder; // Carpeta del sistema donde guardar datos
@@ -57,21 +52,9 @@ class Director {
// Inicializa el objeto input
void initInput();
// Inicializa las opciones del programa
void initOptions();
// Asigna variables a partir de dos cadenas
bool setOptions(options_t *options, std::string var, std::string value);
// Crea el indice de ficheros
bool setFileList();
// Carga el fichero de configuración
bool loadConfigFile();
// Guarda el fichero de configuración
bool saveConfigFile();
// Comprueba los parametros del programa
void checkProgramArguments(int argc, const char *argv[]);