diff --git a/data/locale/ca.yaml b/data/locale/ca.yaml index 968c319..f15bb53 100644 --- a/data/locale/ca.yaml +++ b/data/locale/ca.yaml @@ -47,6 +47,7 @@ menu: system: "Sistema" labels: player_1: "Jugador 1" + player_2: "Jugador 2" gamepad_unavailable: "No disponible" items: video_submenu: "Vídeo" @@ -59,6 +60,8 @@ menu: move_down: "Avall" move_left: "Esquerra" move_right: "Dreta" + gamepad_p1: "Comandament P1" + gamepad_p2: "Comandament P2" zoom: "Zoom" screen: "Pantalla completa" vsync: "VSync" @@ -88,3 +91,5 @@ menu: render_info_off: "Apagat" render_info_top: "Dalt" render_info_bottom: "Baix" + gamepad_none: "Cap" + gamepad_disconnected: "Desconnectat" diff --git a/source/game/ui/service_menu.cpp b/source/game/ui/service_menu.cpp index 156c6b2..9495de6 100644 --- a/source/game/ui/service_menu.cpp +++ b/source/game/ui/service_menu.cpp @@ -7,6 +7,7 @@ #include #include "core/audio/audio.hpp" +#include "core/input/gamepad_manager.hpp" #include "core/input/input_state.hpp" #include "core/input/key_config.hpp" #include "core/locale/locale.hpp" @@ -752,14 +753,78 @@ auto ServiceMenu::buildKeyboardControls() -> Page { return page; } +namespace { + + // Índex del cycle per a un jugador donat. 0 = "Cap" (desassignat o + // sense coincidència amb cap mando connectat); 1..N = mando `idx-1` + // de la llista actual de `GamepadManager::getAll()`. La coincidència + // es fa primer per ruta (estable entre sessions) i si no, per nom. + auto currentPadIndex(int player_id) -> std::size_t { + const auto PADS = GamepadManager::get().getAll(); + const auto& slot = (player_id == 0) ? Options::controls().gamepad_p1 + : Options::controls().gamepad_p2; + if (slot.name.empty() && slot.path.empty()) { return 0; } + for (std::size_t i = 0; i < PADS.size(); ++i) { + if (!slot.path.empty() && PADS[i].path == slot.path) { return i + 1; } + } + for (std::size_t i = 0; i < PADS.size(); ++i) { + if (!slot.name.empty() && PADS[i].name == slot.name) { return i + 1; } + } + return 0; + } + + // Text visible per a cada índex del cycle. 0 → "Cap"; altrament + // el nom del mando. Si l'índex queda fora de rang (p.ex. un mando + // desenchufat just abans que es repinte el label) mostrem el nom + // persistit amb un sufix "(desconnectat)" per no deixar l'usuari + // sense feedback. + auto padLabel(int player_id, std::size_t idx) -> std::string { + if (idx == 0) { return Locale::get().text("menu.values.gamepad_none"); } + const auto PADS = GamepadManager::get().getAll(); + const std::size_t POS = idx - 1; + if (POS < PADS.size()) { return PADS[POS].name; } + const auto& slot = (player_id == 0) ? Options::controls().gamepad_p1 + : Options::controls().gamepad_p2; + return slot.name + " (" + Locale::get().text("menu.values.gamepad_disconnected") + ')'; + } + + // Aplica la selecció del cycle: 0 desassigna, altres índexs vinculen + // el mando corresponent. Options persisteix i crida el GamepadManager. + void applyPadSelection(int player_id, std::size_t idx) { + if (idx == 0) { + Options::setGamepadAssignment(player_id, "", ""); + return; + } + const auto PADS = GamepadManager::get().getAll(); + const std::size_t POS = idx - 1; + if (POS >= PADS.size()) { return; } + Options::setGamepadAssignment(player_id, PADS[POS].name, PADS[POS].path); + } + +} // namespace + auto ServiceMenu::buildGamepadControls() -> Page { Page page; page.title_key = "menu.titles.gamepad"; - page.center_items = true; - // Placeholder: un sol LABEL que indica que la pàgina encara no té - // contingut real. Quan s'afegeixi el suport de gamepad, aquesta - // pàgina es substituirà pel llistat real de botons mapejables. - page.items.push_back(makeLabel(Locale::get().text("menu.labels.gamepad_unavailable"))); + + // Un cycle per jugador. L'opció 0 és sempre "Cap"; la resta es + // generen dinàmicament a partir de `GamepadManager::getAll()` cada + // frame, així que un mando acabat de connectar ja apareix sense + // haver de tornar a obrir el submenú. + page.items.push_back(makeCycle( + "menu.items.gamepad_p1", + []() { return currentPadIndex(0); }, + []() { return GamepadManager::get().getAll().size() + 1; }, + [](std::size_t idx) { applyPadSelection(0, idx); }, + [](std::size_t idx) { return padLabel(0, idx); })); + + page.items.push_back(makeCycle( + "menu.items.gamepad_p2", + []() { return currentPadIndex(1); }, + []() { return GamepadManager::get().getAll().size() + 1; }, + [](std::size_t idx) { applyPadSelection(1, idx); }, + [](std::size_t idx) { return padLabel(1, idx); })); + return page; }