feat(service_menu): slot 'sense mando' al cycle i swap automatic en conflicte

El CYCLE de la pagina CONTROLS ara inclou un slot virtual al final que
desassigna el mando (gamepad_name + gamepad_path buits → padDisplayName
mostra "SENSE MANDO"). Aixi l'usuari pot recuperar el control teclat
sense haver d'editar el YAML.

A mes, si en assignar un mando l'altre jugador ja el tenia, fem swap
automatic: l'altre jugador rep l'assignacio previa d'aquest, evitant
que dos jugadors comparteixen el mateix dispositiu. La deteccio
prioritza path (mateixa branca que resolvePlayerGamepad).

Extracta tambe reapplyBindings per mantenir cyclePlayerPad llegible.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-24 21:22:23 +02:00
parent 85050c8da4
commit 99d0f62ab5
2 changed files with 63 additions and 18 deletions
+58 -15
View File
@@ -84,8 +84,8 @@ namespace {
return Utils::toUpperAscii(pad->name);
}
// Index actual del pad assignat dins de la llista de mandos detectats.
// Prioritat path > name. Si no n'hi ha cap match, retorna 0.
// Index del pad assignat dins de la llista. Retorna pads.size() per
// representar el slot virtual SENSE MANDO (al final del cycle).
auto findAssignedIndex(const std::vector<std::shared_ptr<Input::Gamepad>>& pads,
const Config::PlayerBindings& pcfg) -> std::size_t {
for (std::size_t i = 0; i < pads.size(); ++i) {
@@ -98,12 +98,35 @@ namespace {
return i;
}
}
return 0;
return pads.size(); // Slot virtual "sense mando"
}
// Aplica les noves assignacions a Input. Si ha hagut swap, refresca els
// dos jugadors; en cas contrari nomes el que ha canviat.
void reapplyBindings(int player_index, bool swap_other) {
auto* input = Input::get();
if (input == nullptr) {
return;
}
if (player_index == 0) {
input->applyPlayer1Bindings(ConfigYaml::engine_config.player1);
if (swap_other) {
input->applyPlayer2Bindings(ConfigYaml::engine_config.player2);
}
} else {
input->applyPlayer2Bindings(ConfigYaml::engine_config.player2);
if (swap_other) {
input->applyPlayer1Bindings(ConfigYaml::engine_config.player1);
}
}
}
// Avança ciclicament l'assignacio de pad d'un jugador i la persisteix.
// El cycle inclou un slot virtual "sense mando" al final (NEXT == N).
// Si l'altre jugador ja tenia el pad triat, fa swap: l'altre rep la
// assignacio prèvia d'aquest jugador.
void cyclePlayerPad(int player_index, int dir) {
auto* input = Input::get();
const auto* input = Input::get();
if (input == nullptr) {
return;
}
@@ -114,21 +137,41 @@ namespace {
auto& pcfg = (player_index == 0)
? ConfigYaml::engine_config.player1
: ConfigYaml::engine_config.player2;
auto& other = (player_index == 0)
? ConfigYaml::engine_config.player2
: ConfigYaml::engine_config.player1;
const std::size_t CURRENT = findAssignedIndex(pads, pcfg);
const std::size_t N = pads.size();
const std::size_t STEP = (dir > 0) ? 1 : (N - 1);
const std::size_t NEXT = (CURRENT + STEP) % N;
if (!pads[NEXT]) {
return;
const std::size_t SLOTS = N + 1; // N pads + slot "sense mando"
const std::size_t CURRENT = findAssignedIndex(pads, pcfg);
const std::size_t STEP = (dir > 0) ? 1 : (SLOTS - 1);
const std::size_t NEXT = (CURRENT + STEP) % SLOTS;
// Determinem el nou nom + path (buits si seleccionem "sense mando").
std::string new_name;
std::string new_path;
if (NEXT < N && pads[NEXT]) {
new_name = pads[NEXT]->name;
new_path = pads[NEXT]->path;
}
pcfg.gamepad_name = pads[NEXT]->name;
pcfg.gamepad_path = pads[NEXT]->path;
if (player_index == 0) {
input->applyPlayer1Bindings(ConfigYaml::engine_config.player1);
} else {
input->applyPlayer2Bindings(ConfigYaml::engine_config.player2);
// Detecta conflicte amb l'altre jugador per fer swap.
const bool CONFLICT = !new_path.empty() && other.gamepad_path == new_path;
const bool CONFLICT_BY_NAME = !new_name.empty() && new_path.empty() &&
other.gamepad_name == new_name;
const bool DO_SWAP = CONFLICT || CONFLICT_BY_NAME;
const std::string PREV_NAME = pcfg.gamepad_name;
const std::string PREV_PATH = pcfg.gamepad_path;
pcfg.gamepad_name = new_name;
pcfg.gamepad_path = new_path;
if (DO_SWAP) {
other.gamepad_name = PREV_NAME;
other.gamepad_path = PREV_PATH;
}
reapplyBindings(player_index, DO_SWAP);
ConfigYaml::saveToFile();
}