Files

285 lines
12 KiB
C++

#pragma once
#include <SDL3/SDL.h>
#include <string>
// Configuració del tema UI (índexs de paleta, timings, fonts i flags)
// carregada des de data/config/ui.yaml. Cada camp s'inicialitza amb el valor
// de Defaults::UI com a fallback: si el fitxer falta o li manca una
// clau, el joc s'executa amb el tema compilat.
//
// Patró idèntic a Locale i KeyConfig: Meyers singleton + load(path).
// No hi ha hot-reload — els canvis al YAML requereixen reiniciar.
class UiConfig {
public:
struct Font {
std::string name;
std::string gif;
std::string fnt;
int kerning;
};
// Signatura d'una funció d'easing: t ∈ [0, 1] → valor ∈ [0, 1].
// Permet triar corbes des de data/config/ui.yaml per nom (cadena) i guardar
// el punter a la corresponent funció de `utils/easing.hpp`.
using CurveFn = auto (*)(float) -> float;
// Geometria del Notifier (popup superior). Carregada des de
// data/config/ui.yaml secció `notifier:`; els valors per defecte vénen de
// Defaults::UI::Notifier. El moviment és per temps (slide_ms) i no per
// velocitat, perquè una caixa més gran no triga més a entrar/eixir.
struct Notifier {
int padding_h;
int padding_top;
int padding_bottom;
int line_gap;
int margin_top;
int border_px;
int slide_ms;
CurveFn slide_in_curve;
CurveFn slide_out_curve;
};
static auto get() -> UiConfig&;
UiConfig(const UiConfig&) = delete;
UiConfig(UiConfig&&) = delete;
auto operator=(const UiConfig&) -> UiConfig& = delete;
auto operator=(UiConfig&&) -> UiConfig& = delete;
void load(const std::string& file_path);
// Colors — índexs sobre la paleta "ui" (Resource::Cache).
[[nodiscard]] auto renderInfoText() const -> Uint8 { return render_info_text_; }
[[nodiscard]] auto renderInfoShadow() const -> Uint8 { return render_info_shadow_; }
[[nodiscard]] auto canvasBg() const -> Uint8 { return canvas_bg_; }
[[nodiscard]] auto notificationText() const -> Uint8 { return notification_text_; }
[[nodiscard]] auto notificationBg() const -> Uint8 { return notification_bg_; }
[[nodiscard]] auto exitText() const -> Uint8 { return exit_text_; }
[[nodiscard]] auto exitBg() const -> Uint8 { return exit_bg_; }
[[nodiscard]] auto menuBg() const -> Uint8 { return menu_bg_; }
[[nodiscard]] auto menuBorder() const -> Uint8 { return menu_border_; }
[[nodiscard]] auto menuTitle() const -> Uint8 { return menu_title_; }
[[nodiscard]] auto menuLabel() const -> Uint8 { return menu_label_; }
[[nodiscard]] auto menuValue() const -> Uint8 { return menu_value_; }
[[nodiscard]] auto menuCursor() const -> Uint8 { return menu_cursor_; }
[[nodiscard]] auto menuDisabled() const -> Uint8 { return menu_disabled_; }
[[nodiscard]] auto menuTitleOutline() const -> Uint8 { return menu_title_outline_; }
[[nodiscard]] auto menuLabelOutline() const -> Uint8 { return menu_label_outline_; }
[[nodiscard]] auto menuValueOutline() const -> Uint8 { return menu_value_outline_; }
[[nodiscard]] auto menuCursorOutline() const -> Uint8 { return menu_cursor_outline_; }
[[nodiscard]] auto hudBg() const -> Uint8 { return hud_bg_; }
// Famílies de color del HUD per bloc (LEFT/CENTER/RIGHT). Cada
// família té label (etiqueta apagada), value (valor destacat) i
// shadow (to fosc per a la sombra-embossing). El flash és comú a
// les tres famílies (groc neó al guanyar punts).
[[nodiscard]] auto hudP1Label() const -> Uint8 { return hud_p1_label_; }
[[nodiscard]] auto hudP1Value() const -> Uint8 { return hud_p1_value_; }
[[nodiscard]] auto hudP1Shadow() const -> Uint8 { return hud_p1_shadow_; }
[[nodiscard]] auto hudCenterLabel() const -> Uint8 { return hud_center_label_; }
[[nodiscard]] auto hudCenterValue() const -> Uint8 { return hud_center_value_; }
[[nodiscard]] auto hudCenterShadow() const -> Uint8 { return hud_center_shadow_; }
[[nodiscard]] auto hudP2Label() const -> Uint8 { return hud_p2_label_; }
[[nodiscard]] auto hudP2Value() const -> Uint8 { return hud_p2_value_; }
[[nodiscard]] auto hudP2Shadow() const -> Uint8 { return hud_p2_shadow_; }
[[nodiscard]] auto hudFlash() const -> Uint8 { return hud_flash_; }
[[nodiscard]] auto roomText() const -> Uint8 { return room_text_; }
[[nodiscard]] auto roomTextOutline() const -> Uint8 { return room_text_outline_; }
// Timings (ms).
[[nodiscard]] auto notificationMs() const -> int { return notification_ms_; }
[[nodiscard]] auto exitMs() const -> int { return exit_ms_; }
[[nodiscard]] auto hudEntryMs() const -> int { return hud_entry_ms_; }
[[nodiscard]] auto hudFlashMs() const -> int { return hud_flash_ms_; }
[[nodiscard]] auto hudShakeMs() const -> int { return hud_shake_ms_; }
[[nodiscard]] auto hudBlinkCycleMs() const -> int { return hud_blink_cycle_ms_; }
[[nodiscard]] auto hudBlinkVisibleRatio() const -> float { return hud_blink_visible_ratio_; }
// Escena TITLE (placeholder visual fins que tinguem artwork).
struct TitleHeatHaze {
bool enabled;
float amplitude_px;
float wavelength_px;
float speed_hz;
};
// Seqüència d'aparició dels elements en fase IDLE: tots els temps
// són absoluts des de l'entrada a l'escena. `slide_curve` es
// comparteix entre el slide-in del mini-logo i el del copyright.
struct TitleIntro {
int fade_in_ms;
int game_logo_at_ms;
int mini_logo_at_ms;
int mini_logo_slide_ms;
int copyright_at_ms;
int copyright_slide_ms;
CurveFn slide_curve;
int press_start_at_ms;
};
[[nodiscard]] auto titleDurationMs() const -> int { return title_duration_ms_; }
[[nodiscard]] auto titleBgColor() const -> Uint8 { return title_bg_color_; }
[[nodiscard]] auto titleTextColor() const -> Uint8 { return title_text_color_; }
[[nodiscard]] auto titleCopyrightColor() const -> Uint8 { return title_copyright_color_; }
[[nodiscard]] auto titlePressStartOutlineColor() const -> Uint8 { return title_press_start_outline_color_; }
[[nodiscard]] auto titleLogoYPercent() const -> int { return title_logo_y_percent_; }
[[nodiscard]] auto titlePromptYPercent() const -> int { return title_prompt_y_percent_; }
[[nodiscard]] auto titleCopyrightYPercent() const -> int { return title_copyright_y_percent_; }
[[nodiscard]] auto titleMiniLogoGapPx() const -> int { return title_mini_logo_gap_px_; }
[[nodiscard]] auto titleBlinkVisibleMs() const -> int { return title_blink_visible_ms_; }
[[nodiscard]] auto titleBlinkHiddenMs() const -> int { return title_blink_hidden_ms_; }
[[nodiscard]] auto titleStartingMs() const -> int { return title_starting_ms_; }
[[nodiscard]] auto titleStartingBlinkPeriodMs() const -> int { return title_starting_blink_period_ms_; }
[[nodiscard]] auto titleFadeOutMs() const -> int { return title_fade_out_ms_; }
[[nodiscard]] auto titleFadeOutMaxBlock() const -> int { return title_fade_out_max_block_; }
[[nodiscard]] auto titleMusicFadeOutMs() const -> int { return title_music_fade_out_ms_; }
[[nodiscard]] auto titleAttractFadeToBlackMs() const -> int { return title_attract_fade_to_black_ms_; }
[[nodiscard]] auto titleIntro() const -> const TitleIntro& { return title_intro_; }
[[nodiscard]] auto titleHeatHaze() const -> const TitleHeatHaze& { return title_heat_haze_; }
// Escena LOGO — paràmetres del scanline reveal del logo de l'editora.
// `duration_ms` és la durada de la línia descendent; `tail_glitch_ms`
// és el temps post-flash en què el logo queda totalment visible
// però els bursts VHS encara hi salten (glitch tail).
struct LogoScan {
int duration_ms;
int tail_glitch_ms;
int band_height_px;
int band_alpha;
float band_pulse_hz;
float band_pulse_depth;
int glitch_amplitude_px;
int glitch_band_radius_px;
int vhs_burst_per_sec;
int vhs_burst_duration_ms;
int vhs_burst_height_px;
int vhs_burst_amplitude_px;
int flash_ms;
};
[[nodiscard]] auto logoScan() const -> const LogoScan& { return logo_scan_; }
// Timeline lineal de la LogoScene. Cada camp és la durada (ms) d'una
// fase. La SCAN no apareix aquí perquè la marca el propi
// `LogoScan` (duration_ms + tail_glitch_ms + flash_ms).
struct LogoTimeline {
int black_intro_ms;
int hold_silent_ms;
int hold_with_music_ms;
int fade_out_ms;
int black_outro_ms;
};
[[nodiscard]] auto logoTimeline() const -> const LogoTimeline& { return logo_timeline_; }
// Resol un nom de corba a la seua funció. Pública per permetre a
// futurs camps d'UI triar corbes pel mateix mecanisme (ex.: HUD
// entry, notifications slide...). Si el nom no està registrat,
// retorna Easing::linear i deixa un warning al log.
[[nodiscard]] static auto curveByName(const std::string& name) -> CurveFn;
// Sons del menú de servei. Claus de Resource::Cache::loadSound (els
// fitxers viuen a `data/audio/sounds/`).
[[nodiscard]] auto menuSelectSound() const -> const std::string& { return menu_select_sound_; }
[[nodiscard]] auto menuAcceptSound() const -> const std::string& { return menu_accept_sound_; }
[[nodiscard]] auto playerStartSound() const -> const std::string& { return player_start_sound_; }
[[nodiscard]] auto logoGlitchSound() const -> const std::string& { return logo_glitch_sound_; }
// Fonts de UI.
[[nodiscard]] auto hudFont() const -> const Font& { return hud_font_; }
[[nodiscard]] auto placeholderFont() const -> const Font& { return placeholder_font_; }
[[nodiscard]] auto roomTextFont() const -> const Font& { return room_text_font_; }
[[nodiscard]] auto menuTitleFont() const -> const Font& { return menu_title_font_; }
[[nodiscard]] auto menuItemsFont() const -> const Font& { return menu_items_font_; }
[[nodiscard]] auto copyrightFont() const -> const Font& { return copyright_font_; }
// Geometria del Notifier.
[[nodiscard]] auto notifier() const -> const Notifier& { return notifier_; }
// Flags visuals.
[[nodiscard]] auto hudBgEnabled() const -> bool { return hud_bg_enabled_; }
private:
UiConfig();
~UiConfig() = default;
Uint8 render_info_text_;
Uint8 render_info_shadow_;
Uint8 canvas_bg_;
Uint8 notification_text_;
Uint8 notification_bg_;
Uint8 exit_text_;
Uint8 exit_bg_;
Uint8 menu_bg_;
Uint8 menu_border_;
Uint8 menu_title_;
Uint8 menu_label_;
Uint8 menu_value_;
Uint8 menu_cursor_;
Uint8 menu_disabled_;
Uint8 menu_title_outline_;
Uint8 menu_label_outline_;
Uint8 menu_value_outline_;
Uint8 menu_cursor_outline_;
Uint8 hud_bg_;
Uint8 hud_p1_label_;
Uint8 hud_p1_value_;
Uint8 hud_p1_shadow_;
Uint8 hud_center_label_;
Uint8 hud_center_value_;
Uint8 hud_center_shadow_;
Uint8 hud_p2_label_;
Uint8 hud_p2_value_;
Uint8 hud_p2_shadow_;
Uint8 hud_flash_;
Uint8 room_text_;
Uint8 room_text_outline_;
int notification_ms_;
int exit_ms_;
int hud_entry_ms_;
int hud_flash_ms_;
int hud_shake_ms_;
int hud_blink_cycle_ms_;
float hud_blink_visible_ratio_;
int title_duration_ms_;
Uint8 title_bg_color_;
Uint8 title_text_color_;
Uint8 title_copyright_color_;
Uint8 title_press_start_outline_color_;
int title_logo_y_percent_;
int title_prompt_y_percent_;
int title_copyright_y_percent_;
int title_mini_logo_gap_px_;
int title_blink_visible_ms_;
int title_blink_hidden_ms_;
int title_starting_ms_;
int title_starting_blink_period_ms_;
int title_fade_out_ms_;
int title_fade_out_max_block_;
int title_music_fade_out_ms_;
int title_attract_fade_to_black_ms_;
TitleIntro title_intro_;
TitleHeatHaze title_heat_haze_;
LogoScan logo_scan_;
LogoTimeline logo_timeline_;
std::string menu_select_sound_;
std::string menu_accept_sound_;
std::string player_start_sound_;
std::string logo_glitch_sound_;
Font hud_font_;
Font placeholder_font_;
Font room_text_font_;
Font menu_title_font_;
Font menu_items_font_;
Font copyright_font_;
Notifier notifier_;
bool hud_bg_enabled_{false};
};