feat(input): accio MENU i assignacio de mando per path + name
Afegeix l'accio MENU a InputAction (obre el menu de servei des del mando, equivalent a F12 al teclat) i els camps gamepad.button_start i gamepad.button_menu al config per jugador. Tambe afegeix gamepad_path per distingir dos mandos del mateix model i prioritza path > name > slot a applyPlayerNBindings via el nou resolvePlayerGamepad. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -48,12 +48,15 @@ namespace Config {
|
||||
int button_right{SDL_GAMEPAD_BUTTON_DPAD_RIGHT};
|
||||
int button_thrust{SDL_GAMEPAD_BUTTON_WEST}; // X button
|
||||
int button_shoot{SDL_GAMEPAD_BUTTON_SOUTH}; // A button
|
||||
int button_start{SDL_GAMEPAD_BUTTON_START}; // Start button
|
||||
int button_menu{SDL_GAMEPAD_BUTTON_BACK}; // Select/Back -> obre menu servei
|
||||
};
|
||||
|
||||
struct PlayerBindings {
|
||||
KeyboardBindings keyboard{};
|
||||
GamepadBindings gamepad{};
|
||||
std::string gamepad_name; // Empty = auto-assign by index
|
||||
std::string gamepad_path; // Prioritari sobre name per distingir mateixos models
|
||||
};
|
||||
|
||||
struct AudioConfig {
|
||||
|
||||
@@ -62,7 +62,9 @@ class Input {
|
||||
{Action::LEFT, ButtonState{.button = static_cast<int>(SDL_GAMEPAD_BUTTON_DPAD_LEFT)}},
|
||||
{Action::RIGHT, ButtonState{.button = static_cast<int>(SDL_GAMEPAD_BUTTON_DPAD_RIGHT)}},
|
||||
{Action::THRUST, ButtonState{.button = static_cast<int>(SDL_GAMEPAD_BUTTON_WEST)}},
|
||||
{Action::SHOOT, ButtonState{.button = static_cast<int>(SDL_GAMEPAD_BUTTON_SOUTH)}}} {}
|
||||
{Action::SHOOT, ButtonState{.button = static_cast<int>(SDL_GAMEPAD_BUTTON_SOUTH)}},
|
||||
{Action::START, ButtonState{.button = static_cast<int>(SDL_GAMEPAD_BUTTON_START)}},
|
||||
{Action::MENU, ButtonState{.button = static_cast<int>(SDL_GAMEPAD_BUTTON_BACK)}}} {}
|
||||
|
||||
~Gamepad() {
|
||||
if (pad != nullptr) {
|
||||
@@ -107,6 +109,10 @@ class Input {
|
||||
auto checkActionPlayer1(Action action, bool repeat = true) -> bool;
|
||||
auto checkActionPlayer2(Action action, bool repeat = true) -> bool;
|
||||
|
||||
// Accés al gamepad assignat per jugador (0=P1, 1=P2). nullptr si no n'hi
|
||||
// ha cap d'assignat o connectat. Usat per la UI de redefinició de botons.
|
||||
[[nodiscard]] auto getPlayerGamepad(int player_index) const -> std::shared_ptr<Gamepad>;
|
||||
|
||||
// Check if any player pressed any action from a list
|
||||
auto checkAnyPlayerAction(const std::span<const InputAction>& actions, bool repeat = DO_NOT_ALLOW_REPEAT) -> bool;
|
||||
|
||||
@@ -142,6 +148,8 @@ class Input {
|
||||
auto removeGamepad(SDL_JoystickID id) -> std::string;
|
||||
void addGamepadMappingsFromFile();
|
||||
void discoverGamepads();
|
||||
auto resolvePlayerGamepad(const Config::PlayerBindings& bindings,
|
||||
std::size_t fallback_index) -> std::shared_ptr<Gamepad>;
|
||||
|
||||
// --- Variables miembro ---
|
||||
static Input* instance; // Instancia única del singleton
|
||||
|
||||
@@ -6,6 +6,8 @@ const std::unordered_map<InputAction, std::string> ACTION_TO_STRING = {
|
||||
{InputAction::RIGHT, "RIGHT"},
|
||||
{InputAction::THRUST, "THRUST"},
|
||||
{InputAction::SHOOT, "SHOOT"},
|
||||
{InputAction::START, "START"},
|
||||
{InputAction::MENU, "MENU"},
|
||||
{InputAction::WINDOW_INC_ZOOM, "WINDOW_INC_ZOOM"},
|
||||
{InputAction::WINDOW_DEC_ZOOM, "WINDOW_DEC_ZOOM"},
|
||||
{InputAction::TOGGLE_FULLSCREEN, "TOGGLE_FULLSCREEN"},
|
||||
@@ -18,6 +20,8 @@ const std::unordered_map<std::string, InputAction> STRING_TO_ACTION = {
|
||||
{"RIGHT", InputAction::RIGHT},
|
||||
{"THRUST", InputAction::THRUST},
|
||||
{"SHOOT", InputAction::SHOOT},
|
||||
{"START", InputAction::START},
|
||||
{"MENU", InputAction::MENU},
|
||||
{"WINDOW_INC_ZOOM", InputAction::WINDOW_INC_ZOOM},
|
||||
{"WINDOW_DEC_ZOOM", InputAction::WINDOW_DEC_ZOOM},
|
||||
{"TOGGLE_FULLSCREEN", InputAction::TOGGLE_FULLSCREEN},
|
||||
|
||||
@@ -15,6 +15,7 @@ enum class InputAction : std::uint8_t { // Acciones de entrada posibles en el j
|
||||
THRUST, // Acelerar
|
||||
SHOOT, // Disparar
|
||||
START, // Empezar match
|
||||
MENU, // Abrir/cerrar menu de servicio (equivalent a F12)
|
||||
|
||||
// Inputs de sistema (globales)
|
||||
WINDOW_INC_ZOOM, // F2
|
||||
|
||||
@@ -43,6 +43,37 @@ namespace GlobalEvents {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Botó MENU al mando d'algun jugador → alterna el menú de servei
|
||||
// (mateix comportament que F12 al teclat). Retorna true si l'event és
|
||||
// un GAMEPAD_BUTTON_DOWN consumit.
|
||||
auto handleGamepadMenuButton(const SDL_Event& event) -> bool {
|
||||
if (event.type != SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
|
||||
return false;
|
||||
}
|
||||
auto* input = Input::get();
|
||||
if (input == nullptr) {
|
||||
return false;
|
||||
}
|
||||
auto match_player = [&](int player_index) {
|
||||
auto pad = input->getPlayerGamepad(player_index);
|
||||
if (!pad || pad->instance_id != event.gbutton.which) {
|
||||
return false;
|
||||
}
|
||||
auto it = pad->bindings.find(InputAction::MENU);
|
||||
if (it == pad->bindings.end()) {
|
||||
return false;
|
||||
}
|
||||
return it->second.button == static_cast<int>(event.gbutton.button);
|
||||
};
|
||||
if (!match_player(0) && !match_player(1)) {
|
||||
return false;
|
||||
}
|
||||
if (auto* menu = System::ServiceMenu::get(); menu != nullptr) {
|
||||
menu->toggle();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto handle(const SDL_Event& event, SDLManager& sdl, SceneContext& context) -> bool {
|
||||
@@ -62,6 +93,11 @@ namespace GlobalEvents {
|
||||
// 3. Gestió del ratolí (auto-ocultar)
|
||||
Mouse::handleEvent(event);
|
||||
|
||||
// 3b. Botó MENU al mando (equivalent a F12)
|
||||
if (handleGamepadMenuButton(event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4. Service Menu (F12): consumeix tot KEY_DOWN excepte tecles de
|
||||
// funció (F1-F12) i ESC, que continuen sent globals (zoom, fullscreen,
|
||||
// vsync, AA, postfx, locale, exit prompt). Aixi el menu captura
|
||||
|
||||
@@ -373,12 +373,21 @@ namespace ConfigYaml {
|
||||
if (gp.contains("button_shoot")) {
|
||||
player1.gamepad.button_shoot = stringToButton(gp["button_shoot"].get_value<std::string>());
|
||||
}
|
||||
if (gp.contains("button_start")) {
|
||||
player1.gamepad.button_start = stringToButton(gp["button_start"].get_value<std::string>());
|
||||
}
|
||||
if (gp.contains("button_menu")) {
|
||||
player1.gamepad.button_menu = stringToButton(gp["button_menu"].get_value<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
// Carregar nom del gamepad
|
||||
// Carregar nom i path del gamepad assignat
|
||||
if (p1.contains("gamepad_name")) {
|
||||
player1.gamepad_name = p1["gamepad_name"].get_value<std::string>();
|
||||
}
|
||||
if (p1.contains("gamepad_path")) {
|
||||
player1.gamepad_path = p1["gamepad_path"].get_value<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
// Carregar controls del player 2 desde YAML
|
||||
@@ -421,12 +430,21 @@ namespace ConfigYaml {
|
||||
if (gp.contains("button_shoot")) {
|
||||
player2.gamepad.button_shoot = stringToButton(gp["button_shoot"].get_value<std::string>());
|
||||
}
|
||||
if (gp.contains("button_start")) {
|
||||
player2.gamepad.button_start = stringToButton(gp["button_start"].get_value<std::string>());
|
||||
}
|
||||
if (gp.contains("button_menu")) {
|
||||
player2.gamepad.button_menu = stringToButton(gp["button_menu"].get_value<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
// Carregar nom del gamepad
|
||||
// Carregar nom i path del gamepad assignat
|
||||
if (p2.contains("gamepad_name")) {
|
||||
player2.gamepad_name = p2["gamepad_name"].get_value<std::string>();
|
||||
}
|
||||
if (p2.contains("gamepad_path")) {
|
||||
player2.gamepad_path = p2["gamepad_path"].get_value<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
// Carregar configuración des del file YAML
|
||||
@@ -531,7 +549,10 @@ namespace ConfigYaml {
|
||||
file << " button_right: " << buttonToString(player1.gamepad.button_right) << "\n";
|
||||
file << " button_thrust: " << buttonToString(player1.gamepad.button_thrust) << "\n";
|
||||
file << " button_shoot: " << buttonToString(player1.gamepad.button_shoot) << "\n";
|
||||
file << " gamepad_name: \"" << player1.gamepad_name << "\" # Buit = primer disponible\n\n";
|
||||
file << " button_start: " << buttonToString(player1.gamepad.button_start) << "\n";
|
||||
file << " button_menu: " << buttonToString(player1.gamepad.button_menu) << "\n";
|
||||
file << " gamepad_name: \"" << player1.gamepad_name << "\" # Buit = primer disponible\n";
|
||||
file << " gamepad_path: \"" << player1.gamepad_path << "\" # Prioritari sobre name\n\n";
|
||||
}
|
||||
|
||||
// Guardar controls del player 2 a YAML
|
||||
@@ -548,7 +569,10 @@ namespace ConfigYaml {
|
||||
file << " button_right: " << buttonToString(player2.gamepad.button_right) << "\n";
|
||||
file << " button_thrust: " << buttonToString(player2.gamepad.button_thrust) << "\n";
|
||||
file << " button_shoot: " << buttonToString(player2.gamepad.button_shoot) << "\n";
|
||||
file << " gamepad_name: \"" << player2.gamepad_name << "\" # Buit = segon disponible\n\n";
|
||||
file << " button_start: " << buttonToString(player2.gamepad.button_start) << "\n";
|
||||
file << " button_menu: " << buttonToString(player2.gamepad.button_menu) << "\n";
|
||||
file << " gamepad_name: \"" << player2.gamepad_name << "\" # Buit = segon disponible\n";
|
||||
file << " gamepad_path: \"" << player2.gamepad_path << "\" # Prioritari sobre name\n\n";
|
||||
}
|
||||
|
||||
// Guardar configuración al file YAML
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace ConfigYaml {
|
||||
.key_start = SDL_SCANCODE_2,
|
||||
},
|
||||
.gamepad_name = "",
|
||||
.gamepad_path = "",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user