diff --git a/game/system/hid/devices/game_controller.cpp b/game/system/hid/devices/game_controller.cpp index 29e31fdaf4..a5ba5f694d 100644 --- a/game/system/hid/devices/game_controller.cpp +++ b/game/system/hid/devices/game_controller.cpp @@ -91,6 +91,23 @@ void GameController::process_event(const SDL_Event& event, binds.button_axii.find(event.caxis.axis) != binds.button_axii.end()) { // Binding re-assignment if (bind_assignment) { + // In the event that the user binds an analog input to the confirm binds + // (ie. a trigger on X) we wait until it hits a neutral position (0) + // before proceeding to rebind. + // + // TODO - this still isn't perfect as the data mutation below will trigger it after the bind + // assignment has been set but atleast it's in a mostly working state right now + if (!bind_assignment->seen_controller_confirm_neutral) { + for (const auto& confirm_bind : bind_assignment->controller_confirmation_binds) { + if (confirm_bind.sdl_idx == event.caxis.axis) { + if (event.caxis.value <= 0) { + bind_assignment->seen_controller_confirm_neutral = true; + } + return; + } + } + } + if (bind_assignment->device_type == InputDeviceType::CONTROLLER && !bind_assignment->for_analog) { binds.assign_button_bind(event.caxis.axis, bind_assignment.value(), true); diff --git a/game/system/hid/devices/keyboard.cpp b/game/system/hid/devices/keyboard.cpp index 0e5188da86..82c3ce6cbb 100644 --- a/game/system/hid/devices/keyboard.cpp +++ b/game/system/hid/devices/keyboard.cpp @@ -122,10 +122,10 @@ void KeyboardDevice::process_event(const SDL_Event& event, // modifiers are instead inspected on a KEYUP, however if it's one of the keys // for triggering the binding assignment, and it's the first time we've seen it -- we ignore // it - if (!bind_assignment->seen_confirm_up) { + if (!bind_assignment->seen_keyboard_confirm_up) { for (const auto& confirm_bind : bind_assignment->keyboard_confirmation_binds) { if (confirm_bind.sdl_idx == key_event.keysym.sym) { - bind_assignment->seen_confirm_up = true; + bind_assignment->seen_keyboard_confirm_up = true; return; } } diff --git a/game/system/hid/input_bindings.cpp b/game/system/hid/input_bindings.cpp index 2da9c6a40c..b587e2cedb 100644 --- a/game/system/hid/input_bindings.cpp +++ b/game/system/hid/input_bindings.cpp @@ -150,7 +150,7 @@ std::vector InputBindingGroups::lookup_analog_binds(PadData::A InputBindingInfo new_info; switch (device_type) { case KEYBOARD: - new_info = InputBindingInfo(bind, device_type, sdl_code); + new_info = InputBindingInfo(bind, device_type, sdl_code, false); break; default: new_info.host_name = "TODO - NON-KB ANALOG BIND LOOKUP"; @@ -176,7 +176,7 @@ std::vector InputBindingGroups::lookup_button_binds(PadData::B if (bind.pad_data_index != idx) { continue; } - InputBindingInfo new_info(bind, device_type, sdl_code); + InputBindingInfo new_info(bind, device_type, sdl_code, false); entry.push_back(new_info); } } @@ -185,7 +185,7 @@ std::vector InputBindingGroups::lookup_button_binds(PadData::B if (bind.pad_data_index != idx) { continue; } - InputBindingInfo new_info(bind, device_type, sdl_code); + InputBindingInfo new_info(bind, device_type, sdl_code, true); entry.push_back(new_info); } } @@ -309,7 +309,7 @@ void InputBindingGroups::assign_button_bind(u32 sdl_idx, // unmapped const auto current_button_binds = lookup_button_binds((PadData::ButtonIndex)bind_meta.pad_idx); const auto current_analog_bind = find_analog_bind_from_sdl_idx(sdl_idx, modifiers); - if (buttons.find(sdl_idx) != buttons.end()) { + if (!analog_button && buttons.find(sdl_idx) != buttons.end()) { const auto existing_binds = buttons.at(sdl_idx); if (analog_button) { button_axii[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)}; @@ -324,6 +324,22 @@ void InputBindingGroups::assign_button_bind(u32 sdl_idx, buttons[current_button_binds.front().sdl_idx] = existing_binds; } } + } else if (analog_button && button_axii.find(sdl_idx) != button_axii.end()) { + // TODO - cleanup this duplication + const auto existing_binds = button_axii.at(sdl_idx); + if (analog_button) { + button_axii[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)}; + } else { + buttons[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)}; + } + // there already a bind, so swap (as long as it's not the same key) + if (!current_button_binds.empty() && current_button_binds.front().sdl_idx != (s32)sdl_idx) { + if (current_button_binds.front().analog_button) { + button_axii[current_button_binds.front().sdl_idx] = existing_binds; + } else { + buttons[current_button_binds.front().sdl_idx] = existing_binds; + } + } } else if (!current_button_binds.empty() && current_analog_bind.has_value()) { if (analog_button) { button_axii[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)}; @@ -357,12 +373,19 @@ void InputBindingGroups::set_bindings(const InputBindingGroups& binds) { InputBindingInfo::InputBindingInfo(const InputBinding bind, const InputDeviceType device_type, - const s32 sdl_code) - : sdl_idx(sdl_code), pad_idx(bind.pad_data_index), modifiers(bind.modifiers) { - analog_button = false; + const s32 sdl_code, + const bool _analog_button) + : sdl_idx(sdl_code), + pad_idx(bind.pad_data_index), + modifiers(bind.modifiers), + analog_button(_analog_button) { switch (device_type) { case CONTROLLER: - host_name = sdl_util::get_controller_button_name(sdl_code); + if (analog_button) { + host_name = sdl_util::get_controller_axis_name(sdl_code); + } else { + host_name = sdl_util::get_controller_button_name(sdl_code); + } break; case KEYBOARD: host_name = sdl_util::get_keyboard_button_name(sdl_code, modifiers); diff --git a/game/system/hid/input_bindings.h b/game/system/hid/input_bindings.h index 20650849e3..d169933718 100644 --- a/game/system/hid/input_bindings.h +++ b/game/system/hid/input_bindings.h @@ -219,7 +219,10 @@ struct InputBindingInfo { InputModifiers modifiers; InputBindingInfo() = default; - InputBindingInfo(const InputBinding bind, const InputDeviceType device_type, const s32 sdl_code); + InputBindingInfo(const InputBinding bind, + const InputDeviceType device_type, + const s32 sdl_code, + const bool analog_button); }; // Contains all info related to the current binding we are waiting for @@ -230,18 +233,21 @@ struct InputBindAssignmentMeta { bool for_analog = false; bool for_analog_minimum = false; - // For only rebinding keyboards, we have to know what keys were originally bound to the + // For only some rebindings, we have to know what keys were originally bound to the // confirmation buttons this is because the user has to hit said key to initiate waiting for the // new assignment // // For most input sources this doesn't matter because we listen for the DOWN event, but in order // to allow modifiers as binds (ie. Shift for X) we have to listen to UP events as well (only for - // the modifiers) + // the modifiers). This is also relevant for analog rebinds as the transition from fully pressed + // to unpressed triggers another press. // // TLDR - we ignore the first UP event if it was bound to a confirmation key. Additionally, this // depends on the game as Jak 1 treats X or O as a confirm key... std::vector keyboard_confirmation_binds = {}; - bool seen_confirm_up = false; + bool seen_keyboard_confirm_up = false; + std::vector controller_confirmation_binds = {}; + bool seen_controller_confirm_neutral = false; // Indicates the binding has been received, assigned, and we can proceed. bool assigned = false; diff --git a/game/system/hid/input_manager.cpp b/game/system/hid/input_manager.cpp index 87f7401d99..b1f2dea661 100644 --- a/game/system/hid/input_manager.cpp +++ b/game/system/hid/input_manager.cpp @@ -439,13 +439,38 @@ void InputManager::set_wait_for_bind(const InputDeviceType device_type, m_waiting_for_bind->pad_idx = input_idx; m_waiting_for_bind->for_analog = for_analog; m_waiting_for_bind->for_analog_minimum = for_minimum_analog; + m_waiting_for_bind->seen_keyboard_confirm_up = false; m_waiting_for_bind->keyboard_confirmation_binds = m_settings->keyboard_binds.lookup_button_binds(PadData::CROSS); + m_waiting_for_bind->seen_controller_confirm_neutral = false; + if (m_controller_port_mapping.find(0) != m_controller_port_mapping.end() && + m_controller_port_mapping.at(0) < (int)m_available_controllers.size() && + m_settings->controller_binds.find( + m_available_controllers.at(m_controller_port_mapping.at(0))->get_guid()) != + m_settings->controller_binds.end()) { + m_waiting_for_bind->controller_confirmation_binds = + m_settings->controller_binds + .at(m_available_controllers.at(m_controller_port_mapping.at(0))->get_guid()) + .lookup_button_binds(PadData::CROSS); + } if (g_game_version == GameVersion::Jak1) { - auto circle_binds = m_settings->keyboard_binds.lookup_button_binds(PadData::CIRCLE); + auto keyboard_circle_binds = m_settings->keyboard_binds.lookup_button_binds(PadData::CIRCLE); m_waiting_for_bind->keyboard_confirmation_binds.insert( - m_waiting_for_bind->keyboard_confirmation_binds.end(), circle_binds.begin(), - circle_binds.end()); + m_waiting_for_bind->keyboard_confirmation_binds.end(), keyboard_circle_binds.begin(), + keyboard_circle_binds.end()); + if (m_controller_port_mapping.find(0) != m_controller_port_mapping.end() && + m_controller_port_mapping.at(0) < (int)m_available_controllers.size() && + m_settings->controller_binds.find( + m_available_controllers.at(m_controller_port_mapping.at(0))->get_guid()) != + m_settings->controller_binds.end()) { + auto controller_circle_binds = + m_settings->controller_binds + .at(m_available_controllers.at(m_controller_port_mapping.at(0))->get_guid()) + .lookup_button_binds(PadData::CIRCLE); + m_waiting_for_bind->controller_confirmation_binds.insert( + m_waiting_for_bind->controller_confirmation_binds.end(), controller_circle_binds.begin(), + controller_circle_binds.end()); + } } }