Files
coffee_crisis_arcade_edition/source/options.cpp
2025-07-20 12:51:24 +02:00

328 lines
13 KiB
C++

#include "options.h"
#include <SDL3/SDL.h> // Para SDL_LogCategory, SDL_LogInfo, SDL_LogError
#include <algorithm> // Para clamp
#include <fstream> // Para basic_ostream, operator<<, basic_ostream::...
#include <utility> // Para swap
#include <vector> // Para vector
#include "asset.h"
#include "input.h" // Para InputDeviceToUse
#include "lang.h" // Para Code
#include "utils.h" // Para boolToString, stringToBool, getFileName
namespace Options {
// --- Variables globales ---
WindowOptions window; // Opciones de la ventana
SettingsOptions settings; // Opciones del juego
VideoOptions video; // Opciones de vídeo
AudioOptions audio; // Opciones de audio
std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador
PendingChanges pending_changes; // Opciones que se aplican al cerrar
// Vector con las dificultades
std::vector<Difficulty> difficulties = {
{DifficultyCode::EASY, "Easy"},
{DifficultyCode::NORMAL, "Normal"},
{DifficultyCode::HARD, "Hard"}};
// Declaraciones
auto set(const std::string &var, const std::string &value) -> bool;
// Inicializa las opciones del programa
void init() {
// Settings
settings.config_file = Asset::get()->get("config.txt");
// Opciones de control
controllers.clear();
controllers.resize(2);
controllers.at(0).player_id = 1;
controllers.at(1).player_id = 2;
setKeyboardToPlayer(1);
// Opciones pendientes
pending_changes.new_language = settings.language;
pending_changes.new_difficulty = settings.difficulty;
pending_changes.has_pending_changes = false;
}
// Carga el fichero de configuración
auto loadFromFile() -> bool {
// Inicializa las opciones del programa
init();
// Indicador de éxito en la carga
bool success = true;
// Variables para manejar el fichero
std::ifstream file(settings.config_file);
// Si el fichero se puede abrir
if (file.good()) {
// Procesa el fichero línea a línea
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(settings.config_file).c_str());
std::string line;
while (std::getline(file, line)) {
// Comprueba que la línea no sea un comentario
if (line.substr(0, 1) != "#") {
// Encuentra la posición del carácter '='
int pos = line.find("=");
// Procesa las dos subcadenas
if (!set(line.substr(0, pos), line.substr(pos + 1, line.length()))) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unknown parameter: %s", line.substr(0, pos).c_str());
success = false;
}
}
}
file.close();
}
// El fichero no existe
else {
saveToFile(); // Crea el fichero con los valores por defecto
}
// Normaliza los valores
if (settings.language != Lang::Code::ENGLISH &&
settings.language != Lang::Code::VALENCIAN &&
settings.language != Lang::Code::SPANISH) {
settings.language = Lang::Code::ENGLISH;
}
return success;
}
// Guarda el fichero de configuración
auto saveToFile() -> bool {
std::ofstream file(settings.config_file);
if (!file.good()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: %s can't be opened", getFileName(settings.config_file).c_str());
return false;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(settings.config_file).c_str());
applyPendingChanges();
// Opciones de video
file << "## VIDEO\n";
file << "## video.scale_mode [" << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": lineal]\n";
file << "\n";
file << "window.zoom=" << window.size << "\n";
file << "video.fullscreen=" << boolToString(video.fullscreen) << "\n";
file << "video.scale_mode=" << static_cast<int>(video.scale_mode) << "\n";
file << "video.v_sync=" << boolToString(video.v_sync) << "\n";
file << "video.integer_scale=" << boolToString(video.integer_scale) << "\n";
file << "video.shaders=" << boolToString(video.shaders) << "\n";
// Opciones de audio
file << "\n\n## AUDIO\n";
file << "## volume [0 .. 100]\n";
file << "\n";
file << "audio.enabled=" << boolToString(audio.enabled) << "\n";
file << "audio.volume=" << audio.volume << "\n";
file << "audio.music.enabled=" << boolToString(audio.music.enabled) << "\n";
file << "audio.music.volume=" << audio.music.volume << "\n";
file << "audio.sound.enabled=" << boolToString(audio.sound.enabled) << "\n";
file << "audio.sound.volume=" << audio.sound.volume << "\n";
// Opciones del juego
file << "\n\n## GAME\n";
file << "## game.language [0: spanish, 1: valencian, 2: english]\n";
file << "## game.difficulty [" << static_cast<int>(DifficultyCode::EASY) << ": easy, " << static_cast<int>(DifficultyCode::NORMAL) << ": normal, " << static_cast<int>(DifficultyCode::HARD) << ": hard]\n";
file << "\n";
file << "game.language=" << static_cast<int>(settings.language) << "\n";
file << "game.difficulty=" << static_cast<int>(settings.difficulty) << "\n";
file << "game.autofire=" << boolToString(settings.autofire) << "\n";
file << "game.shutdown_enabled=" << boolToString(settings.shutdown_enabled) << "\n";
// Opciones de mandos
file << "\n\n## CONTROLLERS\n";
int controller_index = 0;
for (const auto &controller : controllers) {
file << "\n";
file << "controller." << controller_index << ".name=" << controller.name << "\n";
file << "controller." << controller_index << ".player=" << controller.player_id << "\n";
file << "controller." << controller_index << ".type=" << static_cast<int>(controller.type) << "\n";
file << "controller." << controller_index << ".button.fire_left=" << controller.buttons.at(0) << "\n";
file << "controller." << controller_index << ".button.fire_center=" << controller.buttons.at(1) << "\n";
file << "controller." << controller_index << ".button.fire_right=" << controller.buttons.at(2) << "\n";
file << "controller." << controller_index << ".button.start=" << controller.buttons.at(3) << "\n";
file << "controller." << controller_index << ".button.service=" << controller.buttons.at(4) << "\n";
// Incrementa el índice
++controller_index;
}
// Cierra el fichero
file.close();
return true;
}
// Asigna variables a partir de dos cadenas
auto set(const std::string &var, const std::string &value) -> bool {
// Indicador de éxito en la asignación
auto success = true;
// Opciones de video
if (var == "video.fullscreen") {
video.fullscreen = stringToBool(value);
} else if (var == "window.zoom") {
window.size = std::stoi(value);
} else if (var == "video.scale_mode") {
video.scale_mode = static_cast<SDL_ScaleMode>(std::stoi(value));
} else if (var == "video.shaders") {
video.shaders = stringToBool(value);
} else if (var == "video.integer_scale") {
video.integer_scale = stringToBool(value);
} else if (var == "video.v_sync") {
video.v_sync = stringToBool(value);
}
// Opciones de audio
else if (var == "audio.enabled") {
audio.enabled = stringToBool(value);
} else if (var == "audio.volume") {
audio.volume = std::clamp(std::stoi(value), 0, 100);
} else if (var == "audio.music.enabled") {
audio.music.enabled = stringToBool(value);
} else if (var == "audio.music.volume") {
audio.music.volume = std::clamp(std::stoi(value), 0, 100);
} else if (var == "audio.sound.enabled") {
audio.sound.enabled = stringToBool(value);
} else if (var == "audio.sound.volume") {
audio.sound.volume = std::clamp(std::stoi(value), 0, 100);
}
// Opciones de juego
else if (var == "game.language") {
settings.language = static_cast<Lang::Code>(std::stoi(value));
pending_changes.new_language = settings.language;
} else if (var == "game.difficulty") {
settings.difficulty = static_cast<DifficultyCode>(std::stoi(value));
pending_changes.new_difficulty = settings.difficulty;
} else if (var == "game.autofire") {
settings.autofire = stringToBool(value);
} else if (var == "game.shutdown_enabled") {
settings.shutdown_enabled = stringToBool(value);
}
// Opciones de mandos
else if (var == "controller.0.name") {
controllers.at(0).name = value;
} else if (var == "controller.0.player") {
controllers.at(0).player_id = std::clamp(std::stoi(value), 1, 2);
} else if (var == "controller.0.type") {
controllers.at(0).type = static_cast<InputDevice>(std::stoi(value));
} else if (var == "controller.0.button.fire_left") {
controllers.at(0).buttons.at(0) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.0.button.fire_center") {
controllers.at(0).buttons.at(1) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.0.button.fire_right") {
controllers.at(0).buttons.at(2) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.0.button.start") {
controllers.at(0).buttons.at(3) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.0.button.service") {
controllers.at(0).buttons.at(4) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.1.name") {
controllers.at(1).name = value;
} else if (var == "controller.1.player") {
controllers.at(1).player_id = std::clamp(std::stoi(value), 1, 2);
} else if (var == "controller.1.type") {
controllers.at(1).type = static_cast<InputDevice>(std::stoi(value));
} else if (var == "controller.1.button.fire_left") {
controllers.at(1).buttons.at(0) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.1.button.fire_center") {
controllers.at(1).buttons.at(1) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.1.button.fire_right") {
controllers.at(1).buttons.at(2) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.1.button.start") {
controllers.at(1).buttons.at(3) = static_cast<SDL_GamepadButton>(std::stoi(value));
} else if (var == "controller.1.button.service") {
controllers.at(1).buttons.at(4) = static_cast<SDL_GamepadButton>(std::stoi(value));
}
// Lineas vacias o que empiezan por comentario
else if (var.empty() || var.starts_with("#")) {
} else {
success = false;
}
return success;
}
// Asigna el teclado al jugador
void setKeyboardToPlayer(int player_id) {
for (auto &controller : controllers) {
if (controller.player_id == player_id) {
controller.type = InputDevice::ANY;
} else {
controller.type = InputDevice::CONTROLLER;
}
}
}
// Intercambia el teclado de jugador
void swapKeyboard() {
std::swap(controllers.at(0).type, controllers.at(1).type);
}
// Intercambia los jugadores asignados a los dos primeros mandos
void swapControllers() {
std::swap(controllers.at(0).player_id, controllers.at(1).player_id);
std::swap(controllers.at(0).type, controllers.at(1).type);
}
// Averigua quien está usando el teclado
auto getPlayerWhoUsesKeyboard() -> int {
for (const auto &controller : controllers) {
if (controller.type == InputDevice::ANY) {
return controller.player_id;
}
}
return 0;
}
// Aplica los cambios pendientes copiando los valores a sus variables
void applyPendingChanges() {
if (pending_changes.has_pending_changes) {
settings.language = pending_changes.new_language;
settings.difficulty = pending_changes.new_difficulty;
pending_changes.has_pending_changes = false;
}
}
void checkPendingChanges() {
if (settings.language != pending_changes.new_language ||
settings.difficulty != pending_changes.new_difficulty) {
pending_changes.has_pending_changes = true;
} else {
pending_changes.has_pending_changes = false;
}
}
auto getDifficultyCodeFromName(const std::string &name) -> DifficultyCode {
for (const auto &difficulty : difficulties) {
if (difficulty.name == name)
return difficulty.code;
}
// Si no se encuentra, devuelve el primero por defecto
return difficulties[0].code;
}
auto getDifficultyNameFromCode(DifficultyCode code) -> std::string {
for (const auto &difficulty : difficulties) {
if (difficulty.code == code)
return difficulty.name;
}
// Si no se encuentra, devuelve el nombre del primero por defecto
return difficulties[0].name;
}
} // namespace Options