diff options
| author | JosJuice <josjuice@gmail.com> | 2021-03-21 20:27:00 +0100 |
|---|---|---|
| committer | JosJuice <josjuice@gmail.com> | 2022-10-03 22:00:21 +0200 |
| commit | cb16d20f2d4ed1ebf150b6ba4d64fe469c1263f1 (patch) | |
| tree | 4cf6bf751dd1cdea3d199f5885571598325d44d7 /Source/Core/InputCommon/ControllerEmu | |
| parent | cb6d476538afe85c25d30e8679b6f15abe5d1760 (diff) | |
ControllerEmu: Add new "input override" system
Diffstat (limited to 'Source/Core/InputCommon/ControllerEmu')
15 files changed, 192 insertions, 6 deletions
diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.cpp index 539e02b449..0f30b50f63 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.cpp @@ -4,6 +4,7 @@ #include "InputCommon/ControllerEmu/ControlGroup/AnalogStick.h" #include <cmath> +#include <optional> #include "Common/Common.h" #include "Common/MathUtil.h" @@ -48,6 +49,34 @@ AnalogStick::StateData AnalogStick::GetState() const return GetReshapableState(true); } +AnalogStick::StateData AnalogStick::GetState(const InputOverrideFunction& override_func) const +{ + bool override_occurred = false; + return GetState(override_func, &override_occurred); +} + +AnalogStick::StateData AnalogStick::GetState(const InputOverrideFunction& override_func, + bool* override_occurred) const +{ + StateData state = GetState(); + if (!override_func) + return state; + + if (const std::optional<ControlState> x_override = override_func(name, X_INPUT_OVERRIDE, state.x)) + { + state.x = *x_override; + *override_occurred = true; + } + + if (const std::optional<ControlState> y_override = override_func(name, Y_INPUT_OVERRIDE, state.y)) + { + state.y = *y_override; + *override_occurred = true; + } + + return state; +} + ControlState AnalogStick::GetGateRadiusAtAngle(double ang) const { return m_stick_gate->GetRadiusAtAngle(ang); diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.h index 47e3ad5743..377e63251d 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/AnalogStick.h @@ -21,6 +21,8 @@ public: ControlState GetGateRadiusAtAngle(double ang) const override; StateData GetState() const; + StateData GetState(const InputOverrideFunction& override_func) const; + StateData GetState(const InputOverrideFunction& override_func, bool* override_occurred) const; private: Control* GetModifierInput() const override; diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Buttons.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Buttons.h index 2d02adabf1..652f64ae5f 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Buttons.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Buttons.h @@ -3,6 +3,7 @@ #pragma once +#include <cmath> #include <string> #include "InputCommon/ControlReference/ControlReference.h" @@ -24,5 +25,21 @@ public: for (auto& control : controls) *buttons |= *(bitmasks++) * control->GetState<bool>(); } + + template <typename C> + void GetState(C* const buttons, const C* bitmasks, + const InputOverrideFunction& override_func) const + { + if (!override_func) + return GetState(buttons, bitmasks); + + for (auto& control : controls) + { + ControlState state = control->GetState(); + if (std::optional<ControlState> state_override = override_func(name, control->name, state)) + state = *state_override; + *buttons |= *(bitmasks++) * (std::lround(state) > 0); + } + } }; } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h index c08c072aba..fb10bab074 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h @@ -5,14 +5,18 @@ #include <algorithm> #include <cmath> +#include <functional> #include <memory> +#include <optional> #include <string> +#include <string_view> #include <type_traits> #include <vector> #include "Common/CommonTypes.h" #include "Common/IniFile.h" #include "InputCommon/ControllerEmu/Control/Control.h" +#include "InputCommon/ControllerInterface/CoreDevice.h" namespace ControllerEmu { @@ -27,6 +31,9 @@ class NumericSetting; template <typename T> class SettingValue; +using InputOverrideFunction = std::function<std::optional<ControlState>( + const std::string_view group_name, const std::string_view control_name, ControlState state)>; + enum class GroupType { Other, diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.cpp index 043ef37356..7359a4e66d 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.cpp @@ -82,15 +82,28 @@ ControlState Cursor::GetGateRadiusAtAngle(double ang) const Cursor::StateData Cursor::GetState(const bool adjusted) { - if (!adjusted) - { - const auto raw_input = GetReshapableState(false); + const ReshapeData input = GetReshapableState(adjusted); + const StateData state = adjusted ? UpdateState(input) : StateData{input.x, input.y}; + return state; +} - return {raw_input.x, raw_input.y}; - } +Cursor::StateData Cursor::GetState(const bool adjusted, + const ControllerEmu::InputOverrideFunction& override_func) +{ + StateData state = GetState(adjusted); + if (!override_func) + return state; + + if (const std::optional<ControlState> x_override = override_func(name, X_INPUT_OVERRIDE, state.x)) + state.x = *x_override; + if (const std::optional<ControlState> y_override = override_func(name, Y_INPUT_OVERRIDE, state.y)) + state.y = *y_override; - const auto input = GetReshapableState(true); + return state; +} +Cursor::StateData Cursor::UpdateState(Cursor::ReshapeData input) +{ // TODO: Using system time is ugly. // Kill this after state is moved into wiimote rather than this class. const auto now = Clock::now(); diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.h index 5e64ce86dc..3710a40d92 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Cursor.h @@ -29,6 +29,7 @@ public: // Modifies the state StateData GetState(bool adjusted); + StateData GetState(bool adjusted, const ControllerEmu::InputOverrideFunction& override_func); // Yaw movement in radians. ControlState GetTotalYaw() const; @@ -40,6 +41,8 @@ public: ControlState GetVerticalOffset() const; private: + Cursor::StateData UpdateState(Cursor::ReshapeData input); + // This is used to reduce the cursor speed for relative input // to something that makes sense with the default range. static constexpr double STEP_PER_SEC = 0.01 * 200; diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.cpp index 8c2f4cbac5..8783087650 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.cpp @@ -4,6 +4,7 @@ #include "InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h" #include <algorithm> +#include <cmath> #include <cstddef> #include <memory> #include <string> @@ -63,6 +64,53 @@ void MixedTriggers::GetState(u16* const digital, const u16* bitmasks, ControlSta } } +void MixedTriggers::GetState(u16* digital, const u16* bitmasks, ControlState* analog, + const InputOverrideFunction& override_func, bool adjusted) const +{ + if (!override_func) + return GetState(digital, bitmasks, analog, adjusted); + + const ControlState threshold = GetThreshold(); + ControlState deadzone = GetDeadzone(); + + // Return raw values. (used in UI) + if (!adjusted) + { + deadzone = 0.0; + } + + const int trigger_count = int(controls.size() / 2); + for (int i = 0; i != trigger_count; ++i) + { + bool button_bool = false; + const ControlState button_value = ApplyDeadzone(controls[i]->GetState(), deadzone); + ControlState analog_value = ApplyDeadzone(controls[trigger_count + i]->GetState(), deadzone); + + // Apply threshold: + if (button_value > threshold) + { + analog_value = 1.0; + button_bool = true; + } + + if (const std::optional<ControlState> button_override = + override_func(name, controls[i]->name, static_cast<ControlState>(button_bool))) + { + button_bool = std::lround(*button_override) > 0; + } + + if (const std::optional<ControlState> analog_override = + override_func(name, controls[trigger_count + i]->name, analog_value)) + { + analog_value = *analog_override; + } + + if (button_bool) + *digital |= bitmasks[i]; + analog[i] = std::min(analog_value, 1.0); + } +} + ControlState MixedTriggers::GetDeadzone() const { return m_deadzone_setting.GetValue() / 100; diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h index fcd98b279e..2e1014d047 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h @@ -17,6 +17,8 @@ public: void GetState(u16* digital, const u16* bitmasks, ControlState* analog, bool adjusted = true) const; + void GetState(u16* digital, const u16* bitmasks, ControlState* analog, + const InputOverrideFunction& override_func, bool adjusted = true) const; ControlState GetDeadzone() const; ControlState GetThreshold() const; diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.cpp index f0516a567f..708831a4c3 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.cpp @@ -35,4 +35,21 @@ Slider::StateData Slider::GetState() const return {std::clamp(ApplyDeadzone(state, deadzone), -1.0, 1.0)}; } + +Slider::StateData Slider::GetState(const InputOverrideFunction& override_func) const +{ + if (!override_func) + return GetState(); + + const ControlState deadzone = m_deadzone_setting.GetValue() / 100; + ControlState state = controls[1]->GetState() - controls[0]->GetState(); + + state = ApplyDeadzone(state, deadzone); + + if (std::optional<ControlState> state_override = override_func(name, X_INPUT_OVERRIDE, state)) + state = *state_override; + + return {std::clamp(state, -1.0, 1.0)}; +} + } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.h index 9d1bf697a5..0e635da235 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Slider.h @@ -23,6 +23,9 @@ public: explicit Slider(const std::string& name_); StateData GetState() const; + StateData GetState(const InputOverrideFunction& override_func) const; + + static constexpr const char* X_INPUT_OVERRIDE = "X"; private: SettingValue<double> m_deadzone_setting; diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.cpp index 17e64d2a8b..321c9272d0 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.cpp @@ -6,6 +6,7 @@ #include <algorithm> #include <cstddef> #include <memory> +#include <optional> #include <string> #include "Common/Common.h" @@ -31,4 +32,25 @@ Triggers::StateData Triggers::GetState() const return result; } + +Triggers::StateData Triggers::GetState(const InputOverrideFunction& override_func) const +{ + if (!override_func) + return GetState(); + + const size_t trigger_count = controls.size(); + const ControlState deadzone = m_deadzone_setting.GetValue() / 100; + + StateData result(trigger_count); + for (size_t i = 0; i < trigger_count; ++i) + { + ControlState state = ApplyDeadzone(controls[i]->GetState(), deadzone); + if (std::optional<ControlState> state_override = override_func(name, controls[i]->name, state)) + state = *state_override; + result.data[i] = std::min(state, 1.0); + } + + return result; +} + } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.h index 2ea02bedb5..c73dee9cfa 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Triggers.h @@ -26,6 +26,7 @@ public: explicit Triggers(const std::string& name); StateData GetState() const; + StateData GetState(const InputOverrideFunction& override_func) const; private: SettingValue<double> m_deadzone_setting; diff --git a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp index 0a3de3e391..dc9830de3f 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp @@ -6,6 +6,7 @@ #include <memory> #include <mutex> #include <string> +#include <utility> #include "Common/IniFile.h" @@ -176,4 +177,15 @@ void EmulatedController::LoadDefaults(const ControllerInterface& ciface) SetDefaultDevice(default_device_string); } } + +void EmulatedController::SetInputOverrideFunction(InputOverrideFunction override_func) +{ + m_input_override_function = std::move(override_func); +} + +void EmulatedController::ClearInputOverrideFunction() +{ + m_input_override_function = {}; +} + } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h index 2db991eb82..b0e514d20a 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h +++ b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h @@ -15,6 +15,7 @@ #include "Common/IniFile.h" #include "Common/MathUtil.h" #include "InputCommon/ControlReference/ExpressionParser.h" +#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" #include "InputCommon/ControllerInterface/CoreDevice.h" class ControllerInterface; @@ -184,6 +185,9 @@ public: void SetDefaultDevice(const std::string& device); void SetDefaultDevice(ciface::Core::DeviceQualifier devq); + void SetInputOverrideFunction(InputOverrideFunction override_func); + void ClearInputOverrideFunction(); + void UpdateReferences(const ControllerInterface& devi); void UpdateSingleControlReference(const ControllerInterface& devi, ControlReference* ref); @@ -228,6 +232,8 @@ protected: // so theirs won't be used (and thus shouldn't even exist). ciface::ExpressionParser::ControlEnvironment::VariableContainer m_expression_vars; + InputOverrideFunction m_input_override_function; + void UpdateReferences(ciface::ExpressionParser::ControlEnvironment& env); private: diff --git a/Source/Core/InputCommon/ControllerEmu/StickGate.h b/Source/Core/InputCommon/ControllerEmu/StickGate.h index 528aa4a5c8..831762fda3 100644 --- a/Source/Core/InputCommon/ControllerEmu/StickGate.h +++ b/Source/Core/InputCommon/ControllerEmu/StickGate.h @@ -106,6 +106,10 @@ public: const ReshapeData& GetCenter() const; void SetCenter(ReshapeData center); + static constexpr const char* X_INPUT_OVERRIDE = "X"; + static constexpr const char* Y_INPUT_OVERRIDE = "Y"; + static constexpr const char* Z_INPUT_OVERRIDE = "Z"; + protected: ReshapeData Reshape(ControlState x, ControlState y, ControlState modifier = 0.0, ControlState clamp = 1.0) const; |
