127 lines
4.8 KiB
C++
127 lines
4.8 KiB
C++
#pragma once
|
|
|
|
#include <SDL3/SDL.h>
|
|
|
|
#include <cstddef>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#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<Text> title_font,
|
|
std::shared_ptr<Text> 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<Text> title_font_;
|
|
std::shared_ptr<Text> items_font_;
|
|
Palette ui_palette_{};
|
|
|
|
std::vector<ButtonAssignment> 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};
|
|
};
|