mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-22 22:00:39 -06:00
ControllerEmu: Add new "input override" system
This commit is contained in:
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
const auto input = GetReshapableState(true);
|
||||
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;
|
||||
|
||||
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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user