233 lines
9.2 KiB
C++
233 lines
9.2 KiB
C++
#pragma once
|
|
|
|
#include <SDL3/SDL.h>
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "core/audio/audio.hpp"
|
|
#include "core/rendering/render_info.hpp"
|
|
#include "core/rendering/screen.hpp"
|
|
#include "core/rendering/shader_backend.hpp"
|
|
#include "utils/defaults.hpp"
|
|
|
|
// Gestor de configuració del joc. És l'ÚNIC lloc que coneix YAML i la ruta
|
|
// del config.yaml. Els subsistemes (Screen, Audio, ...) reben structs tipats
|
|
// a través dels build*Config(), de manera que no queden acoblats a aquest
|
|
// namespace ni al format concret del fitxer.
|
|
//
|
|
// L'estat mutable viu en un anonymous namespace dins d'`options.cpp`: els
|
|
// callers el llegeixen via getters const-ref (`window()`, `video()`, ...) i
|
|
// el muten via setters específics que persisteixen a config.yaml. Els
|
|
// setters NO notifiquen els subsistemes (Screen/Audio): el caller (típic.
|
|
// `core/input/global_inputs.cpp`) crida després el setter del subsistema.
|
|
// Aquesta asimetria preserva la direcció `Director → Screen/Audio` i evita
|
|
// que Options aprenga del layout dels subsistemes (decisió WONTFIX d'aquest
|
|
// refactor: veure docs/audit-game.md → COUP-03).
|
|
namespace Options {
|
|
|
|
struct Window {
|
|
int zoom{Defaults::Window::ZOOM};
|
|
bool fullscreen{Defaults::Window::FULLSCREEN};
|
|
};
|
|
|
|
struct Gpu {
|
|
bool acceleration{Defaults::Video::GPU_ACCELERATION};
|
|
std::string preferred_driver{Defaults::Video::GPU_PREFERRED_DRIVER};
|
|
};
|
|
|
|
struct Shader {
|
|
bool enabled{Defaults::Video::SHADER_ENABLED};
|
|
std::string current_shader{Defaults::Video::CURRENT_SHADER}; // "postfx" o "crtpi"
|
|
std::string current_postfx_preset{Defaults::Video::POSTFX_DEFAULT_PRESET};
|
|
std::string current_crtpi_preset{Defaults::Video::CRTPI_DEFAULT_PRESET};
|
|
};
|
|
|
|
struct Video {
|
|
bool vsync{Defaults::Video::VSYNC};
|
|
Defaults::ScalingMode scaling_mode{Defaults::Video::SCALING_MODE};
|
|
Defaults::TextureFilter texture_filter{Defaults::Video::TEXTURE_FILTER};
|
|
Defaults::RenderInfoPosition render_info_position{Defaults::Video::RENDER_INFO_POSITION};
|
|
bool render_info_show_time{Defaults::Video::RENDER_INFO_SHOW_TIME};
|
|
Gpu gpu{};
|
|
Shader shader{};
|
|
};
|
|
|
|
// Preset d'efectes PostFX (un per entrada a postfx.yaml).
|
|
struct PostFXPreset {
|
|
std::string name;
|
|
float vignette{0.0F};
|
|
float scanlines{0.0F};
|
|
// Aberració cromàtica entre min i max (si coincideixen, estàtica).
|
|
float chroma_min{0.0F};
|
|
float chroma_max{0.0F};
|
|
float mask{0.0F};
|
|
float gamma{0.0F};
|
|
float curvature{0.0F};
|
|
float bleeding{0.0F};
|
|
float flicker{0.0F};
|
|
// Forma de les scanlines (3 subpíxels per fila lògica per defecte).
|
|
float scan_dark_ratio{0.333F};
|
|
float scan_dark_floor{0.42F};
|
|
float scan_edge_soft{1.0F};
|
|
};
|
|
|
|
// Preset del shader CRT-Pi (un per entrada a crtpi.yaml).
|
|
struct CrtPiPreset {
|
|
std::string name;
|
|
float scanline_weight{6.0F};
|
|
float scanline_gap_brightness{0.12F};
|
|
float bloom_factor{3.5F};
|
|
float input_gamma{2.4F};
|
|
float output_gamma{2.2F};
|
|
float mask_brightness{0.80F};
|
|
float curvature_x{0.05F};
|
|
float curvature_y{0.10F};
|
|
int mask_type{2};
|
|
bool enable_scanlines{true};
|
|
bool enable_multisample{true};
|
|
bool enable_gamma{true};
|
|
bool enable_curvature{false};
|
|
bool enable_sharper{false};
|
|
};
|
|
|
|
struct Music {
|
|
bool enabled{Defaults::Audio::MUSIC_ENABLED};
|
|
float volume{Defaults::Audio::MUSIC_VOLUME};
|
|
};
|
|
|
|
struct Sound {
|
|
bool enabled{Defaults::Audio::SOUND_ENABLED};
|
|
float volume{Defaults::Audio::SOUND_VOLUME};
|
|
};
|
|
|
|
struct Audio {
|
|
bool enabled{Defaults::Audio::ENABLED};
|
|
float volume{Defaults::Audio::VOLUME};
|
|
Music music{};
|
|
Sound sound{};
|
|
};
|
|
|
|
// Assignació d'un mando a un jugador: nom visible (el que retorna
|
|
// `GamepadManager::Info::name`) + ruta física (de `SDL_GetGamepadPath`).
|
|
// Tots dos buits = sense mando. La ruta serveix per re-vincular
|
|
// automàticament el mateix mando al mateix port; el nom és el
|
|
// fallback quan l'usuari el canvia de port USB.
|
|
struct GamepadAssignment {
|
|
std::string name;
|
|
std::string path;
|
|
};
|
|
|
|
// Tecles de joc d'un jugador (vinculacions remapeables des del menú).
|
|
// Cada camp és un `SDL_Scancode` literal; els defaults venen de
|
|
// `Defaults::KeysGame*` segons el slot (P1 fletxes, P2 WASD).
|
|
struct Keyboard {
|
|
SDL_Scancode up{SDL_SCANCODE_UNKNOWN};
|
|
SDL_Scancode down{SDL_SCANCODE_UNKNOWN};
|
|
SDL_Scancode left{SDL_SCANCODE_UNKNOWN};
|
|
SDL_Scancode right{SDL_SCANCODE_UNKNOWN};
|
|
SDL_Scancode jump{SDL_SCANCODE_UNKNOWN};
|
|
SDL_Scancode attack{SDL_SCANCODE_UNKNOWN};
|
|
};
|
|
|
|
// Identificadors d'acció per als setters genèrics: una sola funció
|
|
// `setKeyboardBinding(player_id, action, scancode)` cobreix les 12
|
|
// possibles vinculacions sense haver de generar 12 setters individuals.
|
|
enum class KeyboardAction : std::uint8_t {
|
|
UP,
|
|
DOWN,
|
|
LEFT,
|
|
RIGHT,
|
|
JUMP,
|
|
ATTACK,
|
|
};
|
|
|
|
struct Controls {
|
|
GamepadAssignment gamepad_p1{};
|
|
GamepadAssignment gamepad_p2{};
|
|
Keyboard keyboard_p1{
|
|
.up = Defaults::KeysGame::UP,
|
|
.down = Defaults::KeysGame::DOWN,
|
|
.left = Defaults::KeysGame::LEFT,
|
|
.right = Defaults::KeysGame::RIGHT,
|
|
.jump = Defaults::KeysGame::JUMP,
|
|
.attack = Defaults::KeysGame::ATTACK,
|
|
};
|
|
Keyboard keyboard_p2{
|
|
.up = Defaults::KeysGameP2::UP,
|
|
.down = Defaults::KeysGameP2::DOWN,
|
|
.left = Defaults::KeysGameP2::LEFT,
|
|
.right = Defaults::KeysGameP2::RIGHT,
|
|
.jump = Defaults::KeysGameP2::JUMP,
|
|
.attack = Defaults::KeysGameP2::ATTACK,
|
|
};
|
|
};
|
|
|
|
// --- Getters de l'estat (read-only) ---
|
|
[[nodiscard]] auto window() -> const Window&;
|
|
[[nodiscard]] auto video() -> const Video&;
|
|
[[nodiscard]] auto audio() -> const Audio&;
|
|
[[nodiscard]] auto controls() -> const Controls&;
|
|
[[nodiscard]] auto locale() -> const std::string&;
|
|
[[nodiscard]] auto configVersion() -> const std::string&;
|
|
[[nodiscard]] auto postfxPresets() -> const std::vector<PostFXPreset>&;
|
|
[[nodiscard]] auto crtpiPresets() -> const std::vector<CrtPiPreset>&;
|
|
|
|
// --- Setters: muten + persisteixen a config.yaml ---
|
|
// No notifiquen els subsistemes; el caller ha de propagar el canvi al
|
|
// subsistema corresponent (p.ex. Screen::setWindowZoom) per separat.
|
|
void setWindowZoom(int value);
|
|
void setWindowFullscreen(bool value);
|
|
void setVideoVSync(bool value);
|
|
void setVideoScalingMode(Defaults::ScalingMode value);
|
|
void setVideoRenderInfoPosition(Defaults::RenderInfoPosition value);
|
|
void setVideoRenderInfoShowTime(bool value);
|
|
void setShaderEnabled(bool value);
|
|
void setActiveShaderName(const std::string& value); // "postfx" | "crtpi"
|
|
void setPostFXPreset(const std::string& value);
|
|
void setCrtPiPreset(const std::string& value);
|
|
void setAudioEnabled(bool value);
|
|
void setAudioVolume(float value);
|
|
void setAudioMusicEnabled(bool value);
|
|
void setAudioMusicVolume(float value);
|
|
void setAudioSoundEnabled(bool value);
|
|
void setAudioSoundVolume(float value);
|
|
// Fixa l'assignació mando→jugador. Persisteix a `config.yaml` i
|
|
// propaga a `GamepadManager::assignToPlayer`. `name` i `path` buits
|
|
// = desassignació. `player_id`: 0 = P1, 1 = P2.
|
|
void setGamepadAssignment(int player_id, const std::string& name, const std::string& path);
|
|
|
|
// Assigna un nou scancode a una acció d'un jugador i persisteix a
|
|
// `config.yaml`. `player_id`: 0 = P1, 1 = P2. Sense validació de
|
|
// conflictes — replicant la referència AEE, el mateix scancode pot
|
|
// quedar associat a múltiples accions si l'usuari ho elegeix.
|
|
void setKeyboardBinding(int player_id, KeyboardAction action, SDL_Scancode scancode);
|
|
|
|
// --- API pública: càrrega/desament de fitxers ---
|
|
void setConfigFile(const std::string& path);
|
|
auto loadFromFile() -> bool;
|
|
auto saveToFile() -> bool;
|
|
|
|
void setPostFXFile(const std::string& path);
|
|
auto loadPostFXFromFile() -> bool;
|
|
auto savePostFXToFile() -> bool;
|
|
|
|
void setCrtPiFile(const std::string& path);
|
|
auto loadCrtPiFromFile() -> bool;
|
|
auto saveCrtPiToFile() -> bool;
|
|
|
|
// --- Builders per als subsistemes ---
|
|
[[nodiscard]] auto buildScreenConfig() -> Screen::Config;
|
|
[[nodiscard]] auto buildPostFXParams() -> Rendering::PostFXParams;
|
|
[[nodiscard]] auto buildCrtPiParams() -> Rendering::CrtPiParams;
|
|
[[nodiscard]] auto buildActiveShader() -> Rendering::ShaderType;
|
|
// Els colors de text/ombra els decideix la UI del joc (no Options), per
|
|
// això es passen com a paràmetres.
|
|
[[nodiscard]] auto buildRenderInfoFrame(Uint8 text_color, Uint8 shadow_color) -> RenderInfo::Frame;
|
|
// `::Audio` és la classe global d'àudio (no `Options::Audio`, que és la
|
|
// struct serialitzable). Retornem la seua Config per passar-la a init().
|
|
[[nodiscard]] auto buildAudioConfig() -> ::Audio::Config;
|
|
|
|
} // namespace Options
|