Config.yaml + Screen::Config desacoblat via Options (v0.3.0)
This commit is contained in:
1 parent
de5a670fdb
commit
0348acb0eb
8 files changed
+335
-16
No files matched your search
@@ -42,6 +42,7 @@ set(APP_SOURCES
|
|||||||
source/core/resources/resource_helper.cpp
|
source/core/resources/resource_helper.cpp
|
||||||
source/core/resources/resource_cache.cpp
|
source/core/resources/resource_cache.cpp
|
||||||
source/utils/utils.cpp
|
source/utils/utils.cpp
|
||||||
|
source/game/options.cpp
|
||||||
source/game/scenes/game_scene.cpp
|
source/game/scenes/game_scene.cpp
|
||||||
source/game/entities/player.cpp
|
source/game/entities/player.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
#include "utils/defaults.hpp"
|
|
||||||
#include "utils/defines.hpp"
|
#include "utils/defines.hpp"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -48,12 +47,41 @@ Screen::Screen()
|
|||||||
std::fprintf(stderr, "SDL_InitSubSystem(VIDEO) failed: %s\n", SDL_GetError());
|
std::fprintf(stderr, "SDL_InitSubSystem(VIDEO) failed: %s\n", SDL_GetError());
|
||||||
std::exit(EXIT_FAILURE);
|
std::exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
// Els recursos SDL (finestra, renderer, textura) es creen a configure().
|
||||||
|
}
|
||||||
|
|
||||||
const int WIN_W = GameCanvas::WIDTH * Defaults::Window::ZOOM;
|
Screen::~Screen() {
|
||||||
const int WIN_H = GameCanvas::HEIGHT * Defaults::Window::ZOOM;
|
destroySdl();
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::configure(const Config& config) {
|
||||||
|
destroySdl();
|
||||||
|
config_ = config;
|
||||||
|
createSdl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::destroySdl() {
|
||||||
|
if (game_texture_ != nullptr) {
|
||||||
|
SDL_DestroyTexture(game_texture_);
|
||||||
|
game_texture_ = nullptr;
|
||||||
|
}
|
||||||
|
if (renderer_ != nullptr) {
|
||||||
|
SDL_DestroyRenderer(renderer_);
|
||||||
|
renderer_ = nullptr;
|
||||||
|
}
|
||||||
|
if (window_ != nullptr) {
|
||||||
|
SDL_DestroyWindow(window_);
|
||||||
|
window_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::createSdl() {
|
||||||
|
const int WIN_W = GameCanvas::WIDTH * config_.zoom;
|
||||||
|
const int WIN_H = GameCanvas::HEIGHT * config_.zoom;
|
||||||
|
|
||||||
Uint32 win_flags = SDL_WINDOW_RESIZABLE;
|
Uint32 win_flags = SDL_WINDOW_RESIZABLE;
|
||||||
if (Defaults::Window::FULLSCREEN) { win_flags |= SDL_WINDOW_FULLSCREEN; }
|
if (config_.fullscreen) { win_flags |= SDL_WINDOW_FULLSCREEN; }
|
||||||
|
|
||||||
window_ = SDL_CreateWindow(Texts::WINDOW_TITLE, WIN_W, WIN_H, win_flags);
|
window_ = SDL_CreateWindow(Texts::WINDOW_TITLE, WIN_W, WIN_H, win_flags);
|
||||||
if (window_ == nullptr) {
|
if (window_ == nullptr) {
|
||||||
@@ -67,8 +95,8 @@ Screen::Screen()
|
|||||||
std::exit(EXIT_FAILURE);
|
std::exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_SetRenderVSync(renderer_, Defaults::Video::VSYNC ? 1 : 0);
|
SDL_SetRenderVSync(renderer_, config_.vsync ? 1 : 0);
|
||||||
SDL_SetRenderLogicalPresentation(renderer_, GameCanvas::WIDTH, GameCanvas::HEIGHT, toSdlPresentation(Defaults::Video::SCALING_MODE));
|
SDL_SetRenderLogicalPresentation(renderer_, GameCanvas::WIDTH, GameCanvas::HEIGHT, toSdlPresentation(config_.scaling_mode));
|
||||||
|
|
||||||
// STREAMING: el framebuffer ARGB del joc s'actualitza per CPU cada frame
|
// STREAMING: el framebuffer ARGB del joc s'actualitza per CPU cada frame
|
||||||
// i s'envia a la GPU via SDL_UpdateTexture en present().
|
// i s'envia a la GPU via SDL_UpdateTexture en present().
|
||||||
@@ -77,14 +105,7 @@ Screen::Screen()
|
|||||||
std::fprintf(stderr, "SDL_CreateTexture failed: %s\n", SDL_GetError());
|
std::fprintf(stderr, "SDL_CreateTexture failed: %s\n", SDL_GetError());
|
||||||
std::exit(EXIT_FAILURE);
|
std::exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
SDL_SetTextureScaleMode(game_texture_, toSdlScaleMode(Defaults::Video::TEXTURE_FILTER));
|
SDL_SetTextureScaleMode(game_texture_, toSdlScaleMode(config_.texture_filter));
|
||||||
}
|
|
||||||
|
|
||||||
Screen::~Screen() {
|
|
||||||
if (game_texture_ != nullptr) { SDL_DestroyTexture(game_texture_); }
|
|
||||||
if (renderer_ != nullptr) { SDL_DestroyRenderer(renderer_); }
|
|
||||||
if (window_ != nullptr) { SDL_DestroyWindow(window_); }
|
|
||||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Screen::clear(std::uint8_t r, std::uint8_t g, std::uint8_t b) {
|
void Screen::clear(std::uint8_t r, std::uint8_t g, std::uint8_t b) {
|
||||||
|
|||||||
@@ -5,8 +5,20 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "utils/defaults.hpp"
|
||||||
|
|
||||||
class Screen {
|
class Screen {
|
||||||
public:
|
public:
|
||||||
|
// Configuració typada de Screen. L'omple el Director des d'Options,
|
||||||
|
// de manera que Screen no coneix YAML ni la capa Options directament.
|
||||||
|
struct Config {
|
||||||
|
int zoom{Defaults::Window::ZOOM};
|
||||||
|
bool fullscreen{Defaults::Window::FULLSCREEN};
|
||||||
|
bool vsync{Defaults::Video::VSYNC};
|
||||||
|
Defaults::ScalingMode scaling_mode{Defaults::Video::SCALING_MODE};
|
||||||
|
Defaults::TextureFilter texture_filter{Defaults::Video::TEXTURE_FILTER};
|
||||||
|
};
|
||||||
|
|
||||||
// Meyers singleton: la destrucció ocorre durant la termianció estàtica
|
// Meyers singleton: la destrucció ocorre durant la termianció estàtica
|
||||||
// (LIFO respecte a Resource::Cache). El SDL_Quit s'ha de registrar via
|
// (LIFO respecte a Resource::Cache). El SDL_Quit s'ha de registrar via
|
||||||
// std::atexit ABANS de la primera crida a get(); així Screen::~Screen
|
// std::atexit ABANS de la primera crida a get(); així Screen::~Screen
|
||||||
@@ -18,6 +30,10 @@ class Screen {
|
|||||||
auto operator=(const Screen&) -> Screen& = delete;
|
auto operator=(const Screen&) -> Screen& = delete;
|
||||||
auto operator=(Screen&&) -> Screen& = delete;
|
auto operator=(Screen&&) -> Screen& = delete;
|
||||||
|
|
||||||
|
// Crea (o recrea) finestra, renderer i textura a partir de la config.
|
||||||
|
// Imprescindible cridar-lo abans de qualsevol clear()/present().
|
||||||
|
void configure(const Config& config);
|
||||||
|
|
||||||
void clear(std::uint8_t r = 0, std::uint8_t g = 0, std::uint8_t b = 0);
|
void clear(std::uint8_t r = 0, std::uint8_t g = 0, std::uint8_t b = 0);
|
||||||
void present();
|
void present();
|
||||||
|
|
||||||
@@ -30,6 +46,11 @@ class Screen {
|
|||||||
Screen();
|
Screen();
|
||||||
~Screen();
|
~Screen();
|
||||||
|
|
||||||
|
void destroySdl();
|
||||||
|
void createSdl();
|
||||||
|
|
||||||
|
Config config_{};
|
||||||
|
|
||||||
SDL_Window* window_{nullptr};
|
SDL_Window* window_{nullptr};
|
||||||
SDL_Renderer* renderer_{nullptr};
|
SDL_Renderer* renderer_{nullptr};
|
||||||
SDL_Texture* game_texture_{nullptr};
|
SDL_Texture* game_texture_{nullptr};
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -12,6 +14,7 @@
|
|||||||
#include "core/resources/resource_cache.hpp"
|
#include "core/resources/resource_cache.hpp"
|
||||||
#include "core/scene/scene.hpp"
|
#include "core/scene/scene.hpp"
|
||||||
#include "core/system/event_buffer.hpp"
|
#include "core/system/event_buffer.hpp"
|
||||||
|
#include "game/options.hpp"
|
||||||
#include "game/scenes/game_scene.hpp"
|
#include "game/scenes/game_scene.hpp"
|
||||||
|
|
||||||
Director::Director() {
|
Director::Director() {
|
||||||
@@ -26,10 +29,18 @@ Director::Director() {
|
|||||||
// amb SDL ja apagat → caos de destrucció clàssic.
|
// amb SDL ja apagat → caos de destrucció clàssic.
|
||||||
std::atexit(+[] { SDL_Quit(); });
|
std::atexit(+[] { SDL_Quit(); });
|
||||||
|
|
||||||
|
// Prepara la carpeta de sistema (~/.config/jailgames/aee_2026/) i carrega
|
||||||
|
// les opcions; si no existeix config.yaml el crea amb els defaults.
|
||||||
|
createSystemFolder();
|
||||||
|
Options::setConfigFile(system_folder_ + "config.yaml");
|
||||||
|
if (!Options::loadFromFile()) {
|
||||||
|
std::cerr << "Director: no s'ha pogut carregar ni desar config.yaml a " << system_folder_ << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
// Forcem la construcció dels singletons en l'ordre desitjat: primer
|
// Forcem la construcció dels singletons en l'ordre desitjat: primer
|
||||||
// Screen (necessari per a renderitzar), després Cache. La destrucció
|
// Screen (necessari per a renderitzar), després Cache. La destrucció
|
||||||
// estàtica anirà LIFO: Cache primer, Screen últim.
|
// estàtica anirà LIFO: Cache primer, Screen últim.
|
||||||
(void)Screen::get();
|
Screen::get().configure(Options::buildScreenConfig());
|
||||||
(void)Resource::Cache::get();
|
(void)Resource::Cache::get();
|
||||||
|
|
||||||
current_scene_ = std::make_unique<GameScene>();
|
current_scene_ = std::make_unique<GameScene>();
|
||||||
@@ -41,6 +52,27 @@ Director::~Director() {
|
|||||||
// SDL_Quit corre via atexit; els singletons via destrucció estàtica.
|
// SDL_Quit corre via atexit; els singletons via destrucció estàtica.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Director::createSystemFolder() {
|
||||||
|
// Volem la config a $XDG_CONFIG_HOME/jailgames/aee_2026/ (defecte
|
||||||
|
// ~/.config/jailgames/aee_2026/). SDL_GetPrefPath apunta a XDG_DATA_HOME
|
||||||
|
// (.local/share), que és el lloc per a dades, no per a configuració.
|
||||||
|
const char* xdg = std::getenv("XDG_CONFIG_HOME");
|
||||||
|
std::string base;
|
||||||
|
if (xdg != nullptr && *xdg != '\0') {
|
||||||
|
base = xdg;
|
||||||
|
} else {
|
||||||
|
const char* home = std::getenv("HOME");
|
||||||
|
base = (home != nullptr) ? std::string(home) + "/.config" : ".";
|
||||||
|
}
|
||||||
|
system_folder_ = base + "/jailgames/aee_2026/";
|
||||||
|
|
||||||
|
std::error_code error;
|
||||||
|
std::filesystem::create_directories(system_folder_, error);
|
||||||
|
if (error) {
|
||||||
|
std::cerr << "Director: no s'ha pogut crear " << system_folder_ << ": " << error.message() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Director::processEvents() {
|
void Director::processEvents() {
|
||||||
const bool QUIT_REQUESTED = std::ranges::any_of(
|
const bool QUIT_REQUESTED = std::ranges::any_of(
|
||||||
EventBuffer::events,
|
EventBuffer::events,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class Scene;
|
class Scene;
|
||||||
|
|
||||||
@@ -15,9 +16,11 @@ class Director {
|
|||||||
auto iterate() -> SDL_AppResult;
|
auto iterate() -> SDL_AppResult;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::string system_folder_;
|
||||||
std::unique_ptr<Scene> current_scene_;
|
std::unique_ptr<Scene> current_scene_;
|
||||||
std::uint64_t last_ticks_ms_{0};
|
std::uint64_t last_ticks_ms_{0};
|
||||||
|
|
||||||
|
void createSystemFolder();
|
||||||
void processEvents();
|
void processEvents();
|
||||||
void switchSceneIfRequested();
|
void switchSceneIfRequested();
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
#include "game/options.hpp"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "external/fkyaml_node.hpp"
|
||||||
|
#include "utils/defines.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
auto scalingModeToString(Defaults::ScalingMode mode) -> const char* {
|
||||||
|
switch (mode) {
|
||||||
|
case Defaults::ScalingMode::DISABLED:
|
||||||
|
return "disabled";
|
||||||
|
case Defaults::ScalingMode::STRETCH:
|
||||||
|
return "stretch";
|
||||||
|
case Defaults::ScalingMode::LETTERBOX:
|
||||||
|
return "letterbox";
|
||||||
|
case Defaults::ScalingMode::OVERSCAN:
|
||||||
|
return "overscan";
|
||||||
|
case Defaults::ScalingMode::INTEGER:
|
||||||
|
return "integer";
|
||||||
|
}
|
||||||
|
return "letterbox";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto scalingModeFromString(const std::string& text) -> Defaults::ScalingMode {
|
||||||
|
if (text == "disabled") { return Defaults::ScalingMode::DISABLED; }
|
||||||
|
if (text == "stretch") { return Defaults::ScalingMode::STRETCH; }
|
||||||
|
if (text == "overscan") { return Defaults::ScalingMode::OVERSCAN; }
|
||||||
|
if (text == "integer") { return Defaults::ScalingMode::INTEGER; }
|
||||||
|
return Defaults::ScalingMode::LETTERBOX;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto filterToString(Defaults::TextureFilter filter) -> const char* {
|
||||||
|
return filter == Defaults::TextureFilter::LINEAR ? "linear" : "nearest";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto filterFromString(const std::string& text) -> Defaults::TextureFilter {
|
||||||
|
return text == "linear" ? Defaults::TextureFilter::LINEAR : Defaults::TextureFilter::NEAREST;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto readFileOrEmpty(const std::string& path) -> std::string {
|
||||||
|
std::ifstream file(path);
|
||||||
|
if (!file) { return {}; }
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << file.rdbuf();
|
||||||
|
return buffer.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto writeFile(const std::string& path, const std::string& content) -> bool {
|
||||||
|
std::ofstream file(path, std::ios::binary);
|
||||||
|
if (!file) { return false; }
|
||||||
|
file.write(content.data(), static_cast<std::streamsize>(content.size()));
|
||||||
|
return file.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseWindow(const fkyaml::node& yaml) {
|
||||||
|
if (!yaml.contains("window")) { return; }
|
||||||
|
const auto& node = yaml["window"];
|
||||||
|
if (node.contains("zoom")) { Options::window.zoom = node["zoom"].get_value<int>(); }
|
||||||
|
if (node.contains("fullscreen")) { Options::window.fullscreen = node["fullscreen"].get_value<bool>(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseVideo(const fkyaml::node& yaml) {
|
||||||
|
if (!yaml.contains("video")) { return; }
|
||||||
|
const auto& node = yaml["video"];
|
||||||
|
if (node.contains("vsync")) { Options::video.vsync = node["vsync"].get_value<bool>(); }
|
||||||
|
if (node.contains("scaling_mode")) {
|
||||||
|
Options::video.scaling_mode = scalingModeFromString(node["scaling_mode"].get_value<std::string>());
|
||||||
|
}
|
||||||
|
if (node.contains("texture_filter")) {
|
||||||
|
Options::video.texture_filter = filterFromString(node["texture_filter"].get_value<std::string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseMusic(const fkyaml::node& audio_node) {
|
||||||
|
if (!audio_node.contains("music")) { return; }
|
||||||
|
const auto& node = audio_node["music"];
|
||||||
|
if (node.contains("enabled")) { Options::audio.music.enabled = node["enabled"].get_value<bool>(); }
|
||||||
|
if (node.contains("volume")) { Options::audio.music.volume = node["volume"].get_value<float>(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseSound(const fkyaml::node& audio_node) {
|
||||||
|
if (!audio_node.contains("sound")) { return; }
|
||||||
|
const auto& node = audio_node["sound"];
|
||||||
|
if (node.contains("enabled")) { Options::audio.sound.enabled = node["enabled"].get_value<bool>(); }
|
||||||
|
if (node.contains("volume")) { Options::audio.sound.volume = node["volume"].get_value<float>(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseAudio(const fkyaml::node& yaml) {
|
||||||
|
if (!yaml.contains("audio")) { return; }
|
||||||
|
const auto& node = yaml["audio"];
|
||||||
|
if (node.contains("enabled")) { Options::audio.enabled = node["enabled"].get_value<bool>(); }
|
||||||
|
if (node.contains("volume")) { Options::audio.volume = node["volume"].get_value<float>(); }
|
||||||
|
parseMusic(node);
|
||||||
|
parseSound(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace Options {
|
||||||
|
|
||||||
|
void setConfigFile(const std::string& path) {
|
||||||
|
config_file_path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto loadFromFile() -> bool {
|
||||||
|
if (config_file_path.empty()) { return false; }
|
||||||
|
|
||||||
|
const std::string CONTENT = readFileOrEmpty(config_file_path);
|
||||||
|
if (CONTENT.empty()) {
|
||||||
|
// Primera execució: creem el fitxer amb els valors per defecte.
|
||||||
|
return saveToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto yaml = fkyaml::node::deserialize(CONTENT);
|
||||||
|
|
||||||
|
std::string file_version;
|
||||||
|
if (yaml.contains("version")) {
|
||||||
|
file_version = yaml["version"].get_value<std::string>();
|
||||||
|
}
|
||||||
|
// Sense migració per ara: si la versió no coincideix, reescrivim
|
||||||
|
// amb els defaults de la versió nova.
|
||||||
|
if (file_version != Texts::VERSION) { return saveToFile(); }
|
||||||
|
config_version = file_version;
|
||||||
|
|
||||||
|
parseWindow(yaml);
|
||||||
|
parseVideo(yaml);
|
||||||
|
parseAudio(yaml);
|
||||||
|
return true;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Options: error parsejant config.yaml: " << e.what() << '\n';
|
||||||
|
std::cerr << "Options: reescrivint el fitxer amb defaults.\n";
|
||||||
|
return saveToFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto saveToFile() -> bool {
|
||||||
|
if (config_file_path.empty()) { return false; }
|
||||||
|
|
||||||
|
config_version = Texts::VERSION;
|
||||||
|
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "# Configuració d'AEE 2026. Generat automàticament; editable a mà.\n";
|
||||||
|
out << "version: \"" << config_version << "\"\n\n";
|
||||||
|
|
||||||
|
out << "window:\n";
|
||||||
|
out << " zoom: " << window.zoom << "\n";
|
||||||
|
out << " fullscreen: " << (window.fullscreen ? "true" : "false") << "\n\n";
|
||||||
|
|
||||||
|
out << "video:\n";
|
||||||
|
out << " vsync: " << (video.vsync ? "true" : "false") << "\n";
|
||||||
|
out << " scaling_mode: \"" << scalingModeToString(video.scaling_mode) << "\"\n";
|
||||||
|
out << " texture_filter: \"" << filterToString(video.texture_filter) << "\"\n\n";
|
||||||
|
|
||||||
|
out << "audio:\n";
|
||||||
|
out << " enabled: " << (audio.enabled ? "true" : "false") << "\n";
|
||||||
|
out << " volume: " << audio.volume << "\n";
|
||||||
|
out << " music:\n";
|
||||||
|
out << " enabled: " << (audio.music.enabled ? "true" : "false") << "\n";
|
||||||
|
out << " volume: " << audio.music.volume << "\n";
|
||||||
|
out << " sound:\n";
|
||||||
|
out << " enabled: " << (audio.sound.enabled ? "true" : "false") << "\n";
|
||||||
|
out << " volume: " << audio.sound.volume << "\n";
|
||||||
|
|
||||||
|
return writeFile(config_file_path, out.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buildScreenConfig() -> Screen::Config {
|
||||||
|
Screen::Config config;
|
||||||
|
config.zoom = window.zoom;
|
||||||
|
config.fullscreen = window.fullscreen;
|
||||||
|
config.vsync = video.vsync;
|
||||||
|
config.scaling_mode = video.scaling_mode;
|
||||||
|
config.texture_filter = video.texture_filter;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Options
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "core/rendering/screen.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.
|
||||||
|
namespace Options {
|
||||||
|
|
||||||
|
struct Window {
|
||||||
|
int zoom{Defaults::Window::ZOOM};
|
||||||
|
bool fullscreen{Defaults::Window::FULLSCREEN};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Video {
|
||||||
|
bool vsync{Defaults::Video::VSYNC};
|
||||||
|
Defaults::ScalingMode scaling_mode{Defaults::Video::SCALING_MODE};
|
||||||
|
Defaults::TextureFilter texture_filter{Defaults::Video::TEXTURE_FILTER};
|
||||||
|
};
|
||||||
|
|
||||||
|
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{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Estat global (inline vars per a que una sola definició viva a tot
|
||||||
|
// el programa sense necessitar un .cpp extra per a cada una) ---
|
||||||
|
inline std::string config_version{};
|
||||||
|
inline std::string config_file_path{};
|
||||||
|
inline Window window{};
|
||||||
|
inline Video video{};
|
||||||
|
inline Audio audio{};
|
||||||
|
|
||||||
|
// --- API pública ---
|
||||||
|
void setConfigFile(const std::string& path);
|
||||||
|
auto loadFromFile() -> bool;
|
||||||
|
auto saveToFile() -> bool;
|
||||||
|
|
||||||
|
// --- Builders per als subsistemes ---
|
||||||
|
[[nodiscard]] auto buildScreenConfig() -> Screen::Config;
|
||||||
|
|
||||||
|
} // namespace Options
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
namespace Texts {
|
namespace Texts {
|
||||||
constexpr const char* WINDOW_TITLE = "© 2026 Aventures en Egipte — JailDesigner";
|
constexpr const char* WINDOW_TITLE = "© 2026 Aventures en Egipte — JailDesigner";
|
||||||
constexpr const char* COPYRIGHT = "© 2026 JailDesigner";
|
constexpr const char* COPYRIGHT = "© 2026 JailDesigner";
|
||||||
constexpr const char* VERSION = "0.2.0"; // Single source of truth — Makefile el grep
|
constexpr const char* VERSION = "0.3.0"; // Single source of truth — Makefile el grep
|
||||||
} // namespace Texts
|
} // namespace Texts
|
||||||
|
|
||||||
namespace GameCanvas {
|
namespace GameCanvas {
|
||||||
|
|||||||
Reference in new issue
Block a user