DUP-03+HC-07: enum↔string per taula a options.cpp

Tres parelles de `*ToString`/`*FromString` substituïdes per tres taules
`constexpr std::array<pair<Enum, string_view>, N>` i dos helpers plantilla
(`enumToString`, `enumFromString`). La primera entrada de cada taula és
el fallback quan l'enum és desconegut o el text no casa: així els valors
"letterbox"/"nearest"/"off" només apareixen una vegada. Resol també HC-07.
This commit is contained in:
JailDesigner committed 2026-04-20 18:01:19 +02:00
1 parent 206ba07a6f
commit 982a1788f1
2 files changed
+44 -52

No files matched your search

+2 -2
View File
@@ -32,7 +32,7 @@ Els IDs es mantenen estables una vegada assignats. Cada entrada apunta al fitxer
|----|--------------|----------|-----------|-------|-------| |----|--------------|----------|-----------|-------|-------|
| DUP-01 | `options.cpp:152-335,443-547` | PostFX i CrtPi tenen `default*Presets/parse*Preset/write*Preset/load*FromFile/save*ToFile/set*File` duplicats | Importante | DONE | Afegits `loadPresetsFromFile<T>` i `savePresetsToFile<T>` a l'anonymous namespace; els 4 entry-points passen a ser one-liners que injecten parse/write/defaults per tipus. Els fitxers `postfx.yaml`/`crtpi.yaml` regenerats són idèntics a l'anterior. `std::cerr` manté el comportament — INCONS-02 el corregirà | | DUP-01 | `options.cpp:152-335,443-547` | PostFX i CrtPi tenen `default*Presets/parse*Preset/write*Preset/load*FromFile/save*ToFile/set*File` duplicats | Importante | DONE | Afegits `loadPresetsFromFile<T>` i `savePresetsToFile<T>` a l'anonymous namespace; els 4 entry-points passen a ser one-liners que injecten parse/write/defaults per tipus. Els fitxers `postfx.yaml`/`crtpi.yaml` regenerats són idèntics a l'anterior. `std::cerr` manté el comportament — INCONS-02 el corregirà |
| DUP-02 | `player.cpp:100-140` | `snapToHorizontalLane` i `snapToVerticalLane` són el mateix algorisme amb eixos permutats | Importante | DONE | `snapToLane(dt, base, pitch, max_idx, get_pos, set_pos)` pren member-fn pointers; els dos wrappers deleguen només amb `Map::pitch_*`/`grid_*` distints | | DUP-02 | `player.cpp:100-140` | `snapToHorizontalLane` i `snapToVerticalLane` són el mateix algorisme amb eixos permutats | Importante | DONE | `snapToLane(dt, base, pitch, max_idx, get_pos, set_pos)` pren member-fn pointers; els dos wrappers deleguen només amb `Map::pitch_*`/`grid_*` distints |
| DUP-03 | `options.cpp:14-62` | Tres parelles `enumToString`/`enumFromString` amb la mateixa forma | Menor | PENDING | Taula `constexpr` + helpers genèrics | | DUP-03 | `options.cpp:14-62` | Tres parelles `enumToString`/`enumFromString` amb la mateixa forma | Menor | DONE | Tres `constexpr std::array<std::pair<Enum, string_view>, N>` + helpers `enumToString`/`enumFromString` plantilla. La primera entrada és el fallback (documentat). També elimina HC-07 |
| DUP-04 | `options.cpp:79-149` | Cada `parse*` repeteix `if (node.contains) { ... get_value<T> }` | Menor | PENDING | Helper `readField<T>(node, key, dst)` | | DUP-04 | `options.cpp:79-149` | Cada `parse*` repeteix `if (node.contains) { ... get_value<T> }` | Menor | PENDING | Helper `readField<T>(node, key, dst)` |
| DUP-05 | `player.cpp:81-88` | Càlcul `MIN_/MAX_` + `clamp` per eix és la mateixa fórmula que el snap | Menor | PENDING | Fold en helper | | DUP-05 | `player.cpp:81-88` | Càlcul `MIN_/MAX_` + `clamp` per eix és la mateixa fórmula que el snap | Menor | PENDING | Fold en helper |
@@ -46,7 +46,7 @@ Els IDs es mantenen estables una vegada assignats. Cada entrada apunta al fitxer
| HC-04 | `notification_presets.hpp:24-38` | `text_color`, `bg_color`, `time_ms` en `constexpr` | Menor | PENDING | Bloc `notifications:` a `config.yaml` o tema extern | | HC-04 | `notification_presets.hpp:24-38` | `text_color`, `bg_color`, `time_ms` en `constexpr` | Menor | PENDING | Bloc `notifications:` a `config.yaml` o tema extern |
| HC-05 | `ui_palette.hpp:12-21` | Índexs de paleta (`RENDER_INFO_TEXT=47`, `SHADOW=34`, `CANVAS_BG=65`) en `constexpr` | Menor | PENDING | Bloc `ui:` a `config.yaml` | | HC-05 | `ui_palette.hpp:12-21` | Índexs de paleta (`RENDER_INFO_TEXT=47`, `SHADOW=34`, `CANVAS_BG=65`) en `constexpr` | Menor | PENDING | Bloc `ui:` a `config.yaml` |
| HC-06 | `game_scene.cpp:24-26,52` | Rutes `data/maps/pyramid.yaml`, `data/player/player.*`, `pyramid_1.ogg` cablejades | Importante | DONE | Afegit `Defaults::Pyramid::{MAP_YAML,PLAYER_SPRITE,PLAYER_YAML,MUSIC_TRACK}`; `GameScene::Config` struct + `defaultConfig()` passa les rutes al ctor (default ve de Defaults). `Player` ctor accepta `anim_key` en lloc de cablejar `"player.yaml"`. Cache key = basename (mateix conveni que abans) | | HC-06 | `game_scene.cpp:24-26,52` | Rutes `data/maps/pyramid.yaml`, `data/player/player.*`, `pyramid_1.ogg` cablejades | Importante | DONE | Afegit `Defaults::Pyramid::{MAP_YAML,PLAYER_SPRITE,PLAYER_YAML,MUSIC_TRACK}`; `GameScene::Config` struct + `defaultConfig()` passa les rutes al ctor (default ve de Defaults). `Player` ctor accepta `anim_key` en lloc de cablejar `"player.yaml"`. Cache key = basename (mateix conveni que abans) |
| HC-07 | `options.cpp:27,55` | Fallbacks `"letterbox"`/`"off"` com a strings màgics que dupliquen els enums | Menor | PENDING | Passar per `toString(Defaults::...)` | | HC-07 | `options.cpp:27,55` | Fallbacks `"letterbox"`/`"off"` com a strings màgics que dupliquen els enums | Menor | DONE | Resolt amb DUP-03: la primera entrada de cada taula és el fallback i només apareix una vegada |
| HC-08 | `tilemap.cpp:135` | `0xFF000000U` com a màscara alfa | Menor | PENDING | Constant amb nom a `defines.hpp` | | HC-08 | `tilemap.cpp:135` | `0xFF000000U` com a màscara alfa | Menor | PENDING | Constant amb nom a `defines.hpp` |
## 3. Parxes i improvisacions ## 3. Parxes i improvisacions
+42 -50
View File
@@ -1,64 +1,56 @@
#include "game/options.hpp" #include "game/options.hpp"
#include <algorithm> #include <algorithm>
#include <array>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <string_view>
#include <utility>
#include "external/fkyaml_node.hpp" #include "external/fkyaml_node.hpp"
#include "utils/defines.hpp" #include "utils/defines.hpp"
namespace { namespace {
auto scalingModeToString(Defaults::ScalingMode mode) -> const char* { // Taules enum ↔ string per a la serialització a YAML. La primera entrada
switch (mode) { // és el fallback quan l'enum no té nom conegut o el text no casa amb
case Defaults::ScalingMode::DISABLED: // cap entrada coneguda — per això `LETTERBOX`/`NEAREST`/`OFF` encapçalen.
return "disabled"; constexpr std::array SCALING_MODE_NAMES = {
case Defaults::ScalingMode::STRETCH: std::pair{Defaults::ScalingMode::LETTERBOX, std::string_view{"letterbox"}},
return "stretch"; std::pair{Defaults::ScalingMode::DISABLED, std::string_view{"disabled"}},
case Defaults::ScalingMode::LETTERBOX: std::pair{Defaults::ScalingMode::STRETCH, std::string_view{"stretch"}},
return "letterbox"; std::pair{Defaults::ScalingMode::OVERSCAN, std::string_view{"overscan"}},
case Defaults::ScalingMode::OVERSCAN: std::pair{Defaults::ScalingMode::INTEGER, std::string_view{"integer"}},
return "overscan"; };
case Defaults::ScalingMode::INTEGER:
return "integer"; constexpr std::array TEXTURE_FILTER_NAMES = {
std::pair{Defaults::TextureFilter::NEAREST, std::string_view{"nearest"}},
std::pair{Defaults::TextureFilter::LINEAR, std::string_view{"linear"}},
};
constexpr std::array RENDER_INFO_POSITION_NAMES = {
std::pair{Defaults::RenderInfoPosition::OFF, std::string_view{"off"}},
std::pair{Defaults::RenderInfoPosition::TOP, std::string_view{"top"}},
std::pair{Defaults::RenderInfoPosition::BOTTOM, std::string_view{"bottom"}},
};
// Helpers generics: el fallback és la primera entrada de la taula.
template <typename Enum, std::size_t N>
auto enumToString(const std::array<std::pair<Enum, std::string_view>, N>& table, Enum value) -> const char* {
for (const auto& [e, name] : table) {
if (e == value) { return name.data(); }
} }
return "letterbox"; return table.front().second.data();
} }
auto scalingModeFromString(const std::string& text) -> Defaults::ScalingMode { template <typename Enum, std::size_t N>
if (text == "disabled") { return Defaults::ScalingMode::DISABLED; } auto enumFromString(const std::array<std::pair<Enum, std::string_view>, N>& table, const std::string& text) -> Enum {
if (text == "stretch") { return Defaults::ScalingMode::STRETCH; } for (const auto& [e, name] : table) {
if (text == "overscan") { return Defaults::ScalingMode::OVERSCAN; } if (name == text) { return e; }
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 renderInfoPositionToString(Defaults::RenderInfoPosition pos) -> const char* {
switch (pos) {
case Defaults::RenderInfoPosition::TOP:
return "top";
case Defaults::RenderInfoPosition::BOTTOM:
return "bottom";
case Defaults::RenderInfoPosition::OFF:
return "off";
} }
return "off"; return table.front().first;
}
auto renderInfoPositionFromString(const std::string& text) -> Defaults::RenderInfoPosition {
if (text == "top") { return Defaults::RenderInfoPosition::TOP; }
if (text == "bottom") { return Defaults::RenderInfoPosition::BOTTOM; }
return Defaults::RenderInfoPosition::OFF;
} }
auto readFileOrEmpty(const std::string& path) -> std::string { auto readFileOrEmpty(const std::string& path) -> std::string {
@@ -124,13 +116,13 @@ namespace {
const auto& node = yaml["video"]; const auto& node = yaml["video"];
if (node.contains("vsync")) { Options::video.vsync = node["vsync"].get_value<bool>(); } if (node.contains("vsync")) { Options::video.vsync = node["vsync"].get_value<bool>(); }
if (node.contains("scaling_mode")) { if (node.contains("scaling_mode")) {
Options::video.scaling_mode = scalingModeFromString(node["scaling_mode"].get_value<std::string>()); Options::video.scaling_mode = enumFromString(SCALING_MODE_NAMES, node["scaling_mode"].get_value<std::string>());
} }
if (node.contains("texture_filter")) { if (node.contains("texture_filter")) {
Options::video.texture_filter = filterFromString(node["texture_filter"].get_value<std::string>()); Options::video.texture_filter = enumFromString(TEXTURE_FILTER_NAMES, node["texture_filter"].get_value<std::string>());
} }
if (node.contains("render_info_position")) { if (node.contains("render_info_position")) {
Options::video.render_info_position = renderInfoPositionFromString(node["render_info_position"].get_value<std::string>()); Options::video.render_info_position = enumFromString(RENDER_INFO_POSITION_NAMES, node["render_info_position"].get_value<std::string>());
} }
if (node.contains("render_info_show_time")) { if (node.contains("render_info_show_time")) {
Options::video.render_info_show_time = node["render_info_show_time"].get_value<bool>(); Options::video.render_info_show_time = node["render_info_show_time"].get_value<bool>();
@@ -485,9 +477,9 @@ namespace Options {
out << "video:\n"; out << "video:\n";
out << " vsync: " << (video.vsync ? "true" : "false") << "\n"; out << " vsync: " << (video.vsync ? "true" : "false") << "\n";
out << " scaling_mode: \"" << scalingModeToString(video.scaling_mode) << "\"\n"; out << " scaling_mode: \"" << enumToString(SCALING_MODE_NAMES, video.scaling_mode) << "\"\n";
out << " texture_filter: \"" << filterToString(video.texture_filter) << "\"\n"; out << " texture_filter: \"" << enumToString(TEXTURE_FILTER_NAMES, video.texture_filter) << "\"\n";
out << " render_info_position: \"" << renderInfoPositionToString(video.render_info_position) << "\"\n"; out << " render_info_position: \"" << enumToString(RENDER_INFO_POSITION_NAMES, video.render_info_position) << "\"\n";
out << " render_info_show_time: " << (video.render_info_show_time ? "true" : "false") << "\n"; out << " render_info_show_time: " << (video.render_info_show_time ? "true" : "false") << "\n";
out << " gpu:\n"; out << " gpu:\n";
out << " acceleration: " << (video.gpu.acceleration ? "true" : "false") << "\n"; out << " acceleration: " << (video.gpu.acceleration ? "true" : "false") << "\n";