92 lines
3.8 KiB
C++
92 lines
3.8 KiB
C++
#pragma once
|
|
|
|
#include <SDL3/SDL.h>
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
// Vinculacions remapeables d'accions del joc a botons d'un mando concret,
|
|
// indexades per `(name, path)` del mando — replica el patró
|
|
// `GamepadConfig` + `GamepadConfigManager` de coffee_crisis_arcade_edition.
|
|
//
|
|
// Persisteix en un YAML aïllat al system folder (no a `config.yaml`) perquè
|
|
// és per dispositiu, no per usuari/perfil: dos mandos diferents poden
|
|
// tenir mappings diferents i cap d'ells va lligat a un slot de jugador.
|
|
//
|
|
// El layer runtime (consultat per Player/Director cada frame) és
|
|
// directament aquesta classe; no hi ha caché a `GamepadManager::Entry`. El
|
|
// nombre d'entries persistides és típicament <10, i les accions són 3
|
|
// (JUMP/ATTACK/SERVICE), així que les consultes són trivialment ràpides.
|
|
class GamepadBindings {
|
|
public:
|
|
enum class Action : std::uint8_t {
|
|
JUMP,
|
|
ATTACK,
|
|
SERVICE,
|
|
};
|
|
|
|
// Triggers L2/R2 representats com a "botons virtuals" — replica la
|
|
// convenció de coffee_crisis_arcade_edition (`TRIGGER_L2_AS_BUTTON`,
|
|
// `TRIGGER_R2_AS_BUTTON`). El threshold viu a `isPressed`.
|
|
static constexpr int TRIGGER_L2_AS_BUTTON = 100;
|
|
static constexpr int TRIGGER_R2_AS_BUTTON = 101;
|
|
|
|
static auto get() -> GamepadBindings&;
|
|
|
|
GamepadBindings(const GamepadBindings&) = delete;
|
|
GamepadBindings(GamepadBindings&&) = delete;
|
|
auto operator=(const GamepadBindings&) -> GamepadBindings& = delete;
|
|
auto operator=(GamepadBindings&&) -> GamepadBindings& = delete;
|
|
|
|
// Fixa la ruta del fitxer persistent + el carrega. Si no existix
|
|
// encara, l'estat queda buit (les consultes retornaran defaults) i
|
|
// `save()` el crearà al primer `setBindings`.
|
|
void load(const std::string& file_path);
|
|
|
|
// Persistix l'estat actual (totes les Config conegudes). Cridat
|
|
// automàticament dins de `setBindings` — no cal cridar-la a mà.
|
|
auto save() -> bool;
|
|
|
|
// Retorna el botó (o trigger virtual 100/101) lligat a `action` per al
|
|
// mando identificat per (name, path). Si no hi ha entrada per aquest
|
|
// mando, retorna el default global. La cerca és primer per `path`
|
|
// (re-vincular el mateix mando al mateix port és prioritari) i, si no
|
|
// troba, fallback per `name` (mando connectat a un altre port USB).
|
|
[[nodiscard]] auto getButton(const std::string& name, const std::string& path, Action action) const -> int;
|
|
|
|
// Reescriu totes les vinculacions del mando (name, path). Si no hi
|
|
// havia config per aquest dispositiu, en crea una nova. Persisteix a
|
|
// disc immediatament. Replicació de `Input::saveGamepadConfigFromGamepad`.
|
|
void setBindings(const std::string& name,
|
|
const std::string& path,
|
|
const std::unordered_map<Action, int>& bindings);
|
|
|
|
// Wrapper de polling: torna `true` si el "botó" (o trigger virtual)
|
|
// està premut ara mateix al mando `pad`. Encapsula el threshold per
|
|
// a L2/R2 (~50% del rang).
|
|
[[nodiscard]] static auto isPressed(SDL_Gamepad* pad, int button) -> bool;
|
|
|
|
// Default de fàbrica per a una acció — usat tant pel fallback de
|
|
// `getButton` com per inicialitzar la seqüència del DefineButtons.
|
|
[[nodiscard]] static auto defaultButton(Action action) -> int;
|
|
|
|
private:
|
|
struct Config {
|
|
std::string name;
|
|
std::string path;
|
|
std::unordered_map<Action, int> bindings;
|
|
};
|
|
|
|
GamepadBindings() = default;
|
|
~GamepadBindings() = default;
|
|
|
|
// Localitza la Config que coincideix amb (name, path); retorna -1 si
|
|
// no la troba. Match prioritari per path, fallback per nom.
|
|
[[nodiscard]] auto findConfig(const std::string& name, const std::string& path) const -> int;
|
|
|
|
std::vector<Config> configs_;
|
|
std::string file_path_;
|
|
};
|