#pragma once #include #include #include #include #include #include "core/input/gamepad_bindings.hpp" #include "core/rendering/surface.hpp" // Palette class Text; // Sub-mòdul de captura seqüencial de botons per a un mando concret. // Replica `DefineButtons` de coffee_crisis_arcade_edition: l'usuari obre el // menú de servei, escull "Definir botons P1" / "P2", i el sub-mòdul li // demana un per un els botons per a cada acció (JUMP → ATTACK → SERVICE). // // Cicle de vida: // 1. `enable(pad, name, path)`: arrenca la seqüència. `index_` = 0. // 2. Cada SDL_EVENT_GAMEPAD_BUTTON_DOWN (o transició de L2/R2 via // AXIS_MOTION) que NO duplique cap botó ja assignat dins la sessió // avança `index_`. Si `index_` arriba a la mida de la seqüència, // `finished_` es posa a true i, al pròxim BUTTON_UP, escrivim el mapeig // complet a `GamepadBindings`, mostrem "Configuració completa" durant // `MESSAGE_DISPLAY_DURATION_MS`, i posem `closing_`. Quan tot l'overlay // ja no és visible, `isReadyToClose()` torna `true` i el caller ens // fa `disable()`. // // El layer runtime de l'aplicació viu a `GamepadBindings`: aquesta classe // només decideix què s'escriu i quan. class DefineButtons { public: DefineButtons() = default; ~DefineButtons() = default; DefineButtons(const DefineButtons&) = delete; DefineButtons(DefineButtons&&) = delete; auto operator=(const DefineButtons&) -> DefineButtons& = delete; auto operator=(DefineButtons&&) -> DefineButtons& = delete; // Injecta les fonts i la paleta UI; cridat des de `ServiceMenu::init` // perquè comparteixin tipografia i colors de tema. void init(std::shared_ptr title_font, std::shared_ptr items_font, const Palette& ui_palette); // Arranca la sessió per al mando indicat. `pad` ha de ser un handle // viu obtingut de `GamepadManager`. Retorna false si `pad` és nullptr. auto enable(SDL_Gamepad* pad, std::string name, std::string path) -> bool; // Atura la sessió (sense persistir) i neteja l'estat. void disable(); [[nodiscard]] auto isEnabled() const -> bool { return enabled_; } [[nodiscard]] auto isReadyToClose() const -> bool; // Avança el timer de tancament (ms) — replica el `update` de // coffee_crisis_arcade_edition. No fa res si no està actiu. void update(int delta_ms); // Pinta l'overlay sobre el canvas actual (centrat). No fa res si no // està actiu. void render() const; // Punt d'entrada d'events: BUTTON_DOWN / BUTTON_UP / AXIS_MOTION del // mando objectiu. La resta s'ignoren. void handleEvent(const SDL_Event& ev); private: static constexpr int MESSAGE_DISPLAY_DURATION_MS = 2000; struct ButtonAssignment { std::string label_key; GamepadBindings::Action action; int button; // SDL_GamepadButton o trigger virtual; INVALID = sense assignar. }; // Reinicialitza la seqüència ordenada (JUMP → ATTACK → SERVICE) amb // tots els botons sense assignar. Cridat a `enable`. void resetSequence(); // Comprova si un botó ja ha estat assignat dins de la sessió actual. // Replica `checkButtonNotInUse` / `checkTriggerNotInUse`. [[nodiscard]] auto isInUse(int button) const -> bool; void handleButtonDown(const SDL_GamepadButtonEvent& ev); void handleAxisMotion(const SDL_GamepadAxisEvent& ev); // Helper de la lògica comuna a button-down i als triggers L2/R2: // assigna `button` a l'acció pendent i avança l'índex (o marca // finished_ si era l'última). No fa res si `button` ja està en ús. void assignAndAdvance(int button); // Tracta una transició de trigger (L2 o R2) com a botó virtual amb // edge-detect. `virtual_button` és `TRIGGER_L?_AS_BUTTON`, // `was_pressed` és l'estat anterior (mutat per referència). void processTrigger(int virtual_button, bool& was_pressed, bool now); // Crida `onSequenceEnd()` quan l'usuari deixa anar el botó després // d'haver completat la seqüència — replica la branca de // `SDL_EVENT_GAMEPAD_BUTTON_UP` a `DefineButtons::handleEvents`. void onButtonUp(); // Persistix les assignacions a `GamepadBindings` i posa `message_shown_`. void persistAndShowComplete(); SDL_Gamepad* pad_{nullptr}; std::string pad_name_; std::string pad_path_; std::shared_ptr title_font_; std::shared_ptr items_font_; Palette ui_palette_{}; std::vector sequence_; std::size_t index_{0}; int message_timer_ms_{0}; bool enabled_{false}; bool finished_{false}; bool closing_{false}; bool message_shown_{false}; bool l2_was_pressed_{false}; bool r2_was_pressed_{false}; };