#pragma once #include #include // 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}; };