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:
@@ -55,13 +55,15 @@ namespace {
|
||||
}
|
||||
|
||||
// Scancodes que MAI capturem com a binding (reservats per a navegacio o
|
||||
// global hotkeys). Evita que l'usuari es bloquegi ell mateix.
|
||||
// global hotkeys). Tornen true → handleEvent les deixa passar al pipeline
|
||||
// global perque facin la seua feina (ESC obre el prompt d'eixida, F1-F12
|
||||
// son hotkeys de sistema, RETURN/BACKSPACE/TAB son navegacio).
|
||||
auto isReservedScancode(SDL_Scancode sc) -> bool {
|
||||
if (sc == SDL_SCANCODE_ESCAPE) {
|
||||
return true; // ESC sempre cancel·la
|
||||
return true;
|
||||
}
|
||||
if (sc >= SDL_SCANCODE_F1 && sc <= SDL_SCANCODE_F12) {
|
||||
return true; // F1-F12 globals
|
||||
return true;
|
||||
}
|
||||
if (sc == SDL_SCANCODE_RETURN || sc == SDL_SCANCODE_KP_ENTER) {
|
||||
return true;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user