diff --git a/src/spice2x/build/controller_presets.xml b/src/spice2x/build/controller_presets.xml
index 9707e5a..3314b96 100644
--- a/src/spice2x/build/controller_presets.xml
+++ b/src/spice2x/build/controller_presets.xml
@@ -39,8 +39,8 @@
-
-
+
+
@@ -117,8 +117,8 @@
-
-
+
+
@@ -144,34 +144,34 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -277,9 +277,9 @@
-
-
-
+
+
+
@@ -296,8 +296,8 @@
-
-
+
+
diff --git a/src/spice2x/cfg/analog.cpp b/src/spice2x/cfg/analog.cpp
index f946bfc..4855902 100644
--- a/src/spice2x/cfg/analog.cpp
+++ b/src/spice2x/cfg/analog.cpp
@@ -237,7 +237,8 @@ float Analog::applyDeadzone(float raw_value) {
deadzone = -deadzone;
}
- if (getType() != GameAPI::Analogs::AnalogType::LinearPositive) {
+ // relative mode assumes that user is using a stick, so center is neutral regardless of analog type
+ if (getType() != GameAPI::Analogs::AnalogType::LinearPositive || isRelativeMode()) {
// calculate values
const auto delta = value - 0.5f;
@@ -290,4 +291,75 @@ float Analog::applyDeadzone(float raw_value) {
}
}
return value;
-}
\ No newline at end of file
+}
+
+float Analog::getRelativeModeValue(float raw_value) {
+ const auto now = get_performance_seconds();
+ auto delta_time = now - this->rel_mode_last_read_time_s;
+
+ if (this->rel_mode_last_read_time_s != 0.f) {
+ // some heuristics to prevent huge jumps:
+ // * if we went for more than 250ms without polls, discard it (e.g., during loading screens)
+ // * cap the delta at 100ms to prevent huge jumps in case of very infrequent polling
+ if (delta_time < 0.f || 0.25f < delta_time) {
+ delta_time = 0.f;
+ } else if (delta_time > 0.1f) {
+ delta_time = 0.1f;
+ }
+
+ // scale [0, 1] to [-1, 1] to simplify calculations
+ const auto delta_raw_value = (raw_value - 0.5f) * 2.f;
+
+ // target is one revolution per second at max speed at 1.0 sensitivity
+ auto adjusted_delta_value = delta_raw_value * delta_time;
+
+ // multiplier / divisor
+ if (this->getMultiplier() > 1) {
+ adjusted_delta_value *= this->getMultiplier();
+ } else if (this->getMultiplier() < -1) {
+ adjusted_delta_value /= -this->getMultiplier();
+ }
+
+ // sensitivity
+ if (this->isSensitivitySet()) {
+ adjusted_delta_value *= this->getSensitivity();
+ }
+
+ // calculate the new absolute value
+ this->rel_mode_absolute_value += adjusted_delta_value;
+ }
+
+ // update for next poll
+ this->rel_mode_last_read_time_s = now;
+ this->rel_mode_absolute_value = normalizeAnalogValue(this->rel_mode_absolute_value);
+ return this->rel_mode_absolute_value;
+}
+
+float Analog::getDelayedValue(float raw_value) {
+ const double delay_ms = static_cast(this->getDelayMs());
+ if (delay_ms == 0.0) {
+ return raw_value;
+ }
+
+ // always push a new value
+ const auto now = get_performance_milliseconds();
+ this->delayed_inputs.emplace(now, raw_value);
+
+ // drain the queue down to reasonable length to prevent unconstrained growth
+ // this would accommodate 1 second at ~1000Hz which is overkill
+ // (UI only allows for 250ms of delay)
+ while (this->delayed_inputs.size() > 1024) {
+ this->delayed_inputs.pop();
+ }
+
+ // pop until we find the oldest value still inside the delay window
+ while (this->delayed_inputs.size() > 1) {
+ const auto delta_t = now - this->delayed_inputs.front().time_in_ms;
+ if (delta_t <= delay_ms) {
+ break;
+ }
+ this->delayed_inputs.pop();
+ }
+
+ return this->delayed_inputs.front().value;
+}
diff --git a/src/spice2x/cfg/analog.h b/src/spice2x/cfg/analog.h
index fc3a4e8..6fe79b7 100644
--- a/src/spice2x/cfg/analog.h
+++ b/src/spice2x/cfg/analog.h
@@ -1,5 +1,6 @@
#pragma once
+#include
#include
#include
#include
@@ -34,6 +35,11 @@ struct AnalogMovingAverage {
float cosine;
};
+struct AnalogDelayEntry {
+ double time_in_ms;
+ float value;
+};
+
class Analog {
private:
std::string name;
@@ -63,6 +69,15 @@ private:
float divisor_previous_value = 0.5f;
unsigned short divisor_region = 0;
+ // relative input mode
+ float rel_mode_absolute_value = 0.5f;
+ float rel_mode_last_read_time_s = 0.f;
+ bool relative_mode = false;
+
+ // delay
+ uint32_t delay_ms = 0;
+ std::queue delayed_inputs;
+
float calculateAngularDifference(float old_rads, float new_rads);
float normalizeAngle(float rads);
float normalizeAnalogValue(float value);
@@ -83,6 +98,8 @@ public:
float applyAngularSensitivity(float raw_rads);
float applyMultiplier(float raw_value);
float applyDeadzone(float raw_value);
+ float getRelativeModeValue(float raw_value);
+ float getDelayedValue(float raw_value);
inline bool isSet() {
if (this->override_enabled) {
@@ -98,7 +115,9 @@ public:
smoothing = false;
deadzone_mirror = false;
setMultiplier(1);
+ setRelativeMode(false);
setLastState(0.5f);
+ setDelayMs(0);
}
inline void clearBindings() {
@@ -195,6 +214,19 @@ public:
this->last_state = last_state;
}
+ inline bool isRelativeMode() const {
+ return this->relative_mode;
+ }
+
+ inline void setRelativeMode(bool relative_mode) {
+ if (relative_mode) {
+ this->smoothing = false;
+ }
+ this->relative_mode = relative_mode;
+ this->rel_mode_absolute_value = 0.5f;
+ this->rel_mode_last_read_time_s = 0.f;
+ }
+
inline GameAPI::Analogs::AnalogType getType() const {
return this->type;
}
@@ -202,4 +234,12 @@ public:
inline void setType(GameAPI::Analogs::AnalogType type) {
this->type = type;
}
+
+ inline uint32_t getDelayMs() const {
+ return this->delay_ms;
+ }
+
+ inline void setDelayMs(uint32_t delay_ms) {
+ this->delay_ms = delay_ms;
+ }
};
diff --git a/src/spice2x/cfg/api.cpp b/src/spice2x/cfg/api.cpp
index fad0a8b..2ffab82 100644
--- a/src/spice2x/cfg/api.cpp
+++ b/src/spice2x/cfg/api.cpp
@@ -679,38 +679,45 @@ float GameAPI::Analogs::getState(rawinput::RawInputManager *manager, rawinput::D
}
// deadzone
- // do not apply deadzone to circular analogs since it doesn't make sense
- if (analog.isDeadzoneSet() && analog.getType() != AnalogType::Circular) {
+ // do not apply deadzone to circular analogs since it doesn't make sense (except in relative mode)
+ if (analog.isDeadzoneSet() &&
+ (analog.getType() != AnalogType::Circular || analog.isRelativeMode())) {
value = analog.applyDeadzone(value);
}
if (analog.getType() == AnalogType::Circular) {
- // integer multiplier
- value = analog.applyMultiplier(value);
- // smoothing/sensitivity
- if (analog.getSmoothing() || analog.isSensitivitySet()) {
- float rads = value * (float) M_TAU;
+ if (analog.isRelativeMode()) {
+ value = analog.getRelativeModeValue(value);
- // smoothing
- if (analog.getSmoothing()) {
+ } else {
+ // integer multiplier
+ value = analog.applyMultiplier(value);
- // preserve direction
- if (rads >= M_TAU) {
- rads -= 0.0001f;
+ // smoothing/sensitivity
+ if (analog.getSmoothing() || analog.isSensitivitySet()) {
+ float rads = value * (float) M_TAU;
+
+ // smoothing
+ if (analog.getSmoothing()) {
+
+ // preserve direction
+ if (rads >= M_TAU) {
+ rads -= 0.0001f;
+ }
+
+ // calculate angle
+ rads = analog.getSmoothedValue(rads);
}
- // calculate angle
- rads = analog.getSmoothedValue(rads);
- }
+ // sensitivity
+ if (analog.isSensitivitySet()) {
+ rads = analog.applyAngularSensitivity(rads);
+ }
- // sensitivity
- if (analog.isSensitivitySet()) {
- rads = analog.applyAngularSensitivity(rads);
+ // apply to value
+ value = rads * (float) M_1_TAU;
}
-
- // apply to value
- value = rads * (float) M_1_TAU;
}
} else {
// sensitivity
@@ -760,6 +767,12 @@ float GameAPI::Analogs::getState(rawinput::RawInputManager *manager, rawinput::D
value = std::clamp(value, 0.f, 1.f);
}
}
+
+ // delay
+ if (analog.getDelayMs() > 0) {
+ value = analog.getDelayedValue(value);
+ }
+
break;
}
case rawinput::MIDI: {
diff --git a/src/spice2x/cfg/config.cpp b/src/spice2x/cfg/config.cpp
index ef626d9..07a8dcf 100644
--- a/src/spice2x/cfg/config.cpp
+++ b/src/spice2x/cfg/config.cpp
@@ -252,6 +252,8 @@ bool Config::addGame(Game &game) {
bool invert = false;
bool smoothing = false;
int multiplier = 1;
+ bool relative_mode = false;
+ uint32_t delay = 0;
tinyxml2::XMLError err1 = gameAnalogNode->QueryIntAttribute("index", &index);
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
@@ -259,6 +261,8 @@ bool Config::addGame(Game &game) {
gameAnalogNode->QueryBoolAttribute("invert", &invert);
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
+ gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
+ gameAnalogNode->QueryUnsignedAttribute("delay_ms", &delay);
const char *devid = gameAnalogNode->Attribute("devid");
if (err1 != tinyxml2::XMLError::XML_SUCCESS || !devid) {
@@ -273,6 +277,8 @@ bool Config::addGame(Game &game) {
gameAnalogNode->SetAttribute("invert", it.getInvert());
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
+ gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
+ gameAnalogNode->SetAttribute("delay_ms", it.getDelayMs());
gameAnalogsNode->InsertEndChild(gameAnalogNode);
} else {
it.setIndex(static_cast(index));
@@ -283,6 +289,8 @@ bool Config::addGame(Game &game) {
it.setInvert(invert);
it.setSmoothing(smoothing);
it.setMultiplier(multiplier);
+ it.setRelativeMode(relative_mode);
+ it.setDelayMs(delay);
}
} else {
gameAnalogNode = this->configFile.NewElement("analog");
@@ -294,6 +302,8 @@ bool Config::addGame(Game &game) {
gameAnalogNode->SetAttribute("invert", it.getInvert());
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
+ gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
+ gameAnalogNode->SetAttribute("delay_ms", it.getDelayMs());
gameAnalogNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
gameAnalogsNode->InsertEndChild(gameAnalogNode);
}
@@ -447,6 +457,8 @@ bool Config::addGame(Game &game) {
gameAnalogNode->SetAttribute("invert", it.getInvert());
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
+ gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
+ gameAnalogNode->SetAttribute("delay_ms", it.getDelayMs());
gameAnalogsNode->InsertEndChild(gameAnalogNode);
}
@@ -679,6 +691,8 @@ bool Config::updateBinding(const Game &game, const Analog &analog) {
gameAnalogNode->SetAttribute("invert", analog.getInvert());
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
+ gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
+ gameAnalogNode->SetAttribute("delay_ms", analog.getDelayMs());
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
} else {
gameAnalogNode = this->configFile.NewElement("analog");
@@ -689,6 +703,8 @@ bool Config::updateBinding(const Game &game, const Analog &analog) {
gameAnalogNode->SetAttribute("invert", analog.getInvert());
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
+ gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
+ gameAnalogNode->SetAttribute("delay_ms", analog.getDelayMs());
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
gameAnalogsNode->InsertEndChild(gameAnalogNode);
}
@@ -1125,6 +1141,8 @@ std::vector Config::getAnalogs(const std::string &gameName) {
bool invert = false;
bool smoothing = false;
int multiplier = 1;
+ bool relative_mode = false;
+ uint32_t delay = 0;
gameAnalogNode->QueryIntAttribute("index", &index);
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
@@ -1132,6 +1150,8 @@ std::vector Config::getAnalogs(const std::string &gameName) {
gameAnalogNode->QueryBoolAttribute("invert", &invert);
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
+ gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
+ gameAnalogNode->QueryUnsignedAttribute("delay_ms", &delay);
const char *devid = gameAnalogNode->Attribute("devid");
// create analog and add to list
@@ -1143,6 +1163,8 @@ std::vector Config::getAnalogs(const std::string &gameName) {
analog.setInvert(invert);
analog.setSmoothing(smoothing);
analog.setMultiplier(multiplier);
+ analog.setRelativeMode(relative_mode);
+ analog.setDelayMs(delay);
if (devid) {
analog.setDeviceIdentifier(devid);
}
diff --git a/src/spice2x/cfg/controller_presets.cpp b/src/spice2x/cfg/controller_presets.cpp
index 6d7cb24..9b9a3e3 100644
--- a/src/spice2x/cfg/controller_presets.cpp
+++ b/src/spice2x/cfg/controller_presets.cpp
@@ -69,6 +69,8 @@ namespace overlay::windows {
el->SetAttribute("invert", analog.invert);
el->SetAttribute("smoothing", analog.smoothing);
el->SetAttribute("multiplier", analog.multiplier);
+ el->SetAttribute("relative", analog.relative_mode);
+ el->SetAttribute("delay_ms", analog.delay_ms);
parent->InsertEndChild(el);
}
@@ -90,6 +92,8 @@ namespace overlay::windows {
el->QueryBoolAttribute("invert", &a.invert);
el->QueryBoolAttribute("smoothing", &a.smoothing);
el->QueryIntAttribute("multiplier", &a.multiplier);
+ el->QueryBoolAttribute("relative", &a.relative_mode);
+ el->QueryUnsignedAttribute("delay_ms", &a.delay_ms);
return a;
}
diff --git a/src/spice2x/games/iidx/iidx.cpp b/src/spice2x/games/iidx/iidx.cpp
index f7449dd..4321de6 100644
--- a/src/spice2x/games/iidx/iidx.cpp
+++ b/src/spice2x/games/iidx/iidx.cpp
@@ -783,42 +783,7 @@ namespace games::iidx {
}
// return higher 8 bit
- uint8_t result = (uint8_t) (ret_value >> 2);
-
- // delay
- if ((player == 0 && TT_DELAY_P1 > 0) ||
- (player == 1 && TT_DELAY_P2 > 0)) {
-
- static std::queue> delay_queue[2];
- auto &queue = delay_queue[player];
-
- const auto max_delta_ms =
- static_cast((player == 0) ? TT_DELAY_P1 : TT_DELAY_P2);
-
- // always push a new value
- const auto now = get_performance_milliseconds();
- queue.push(std::make_pair(now, result));
-
- // drain the queue down to reasonable length to prevent unconstrained growth
- // this would accommodate 1 second at ~1000Hz
- // (in reality all three iidx I/O emulation runs well under 500Hz)
- while (queue.size() > 1024) {
- queue.pop();
- }
-
- // pop until we find one that falls just under the time threshold
- while (!queue.empty()) {
- const auto delta_t = now - queue.front().first;
- if (delta_t <= max_delta_ms) {
- break;
- }
- queue.pop();
- }
-
- result = queue.front().second;
- }
-
- return result;
+ return (uint8_t)(ret_value >> 2);
}
unsigned char get_slider(uint8_t slider) {
diff --git a/src/spice2x/games/iidx/iidx.h b/src/spice2x/games/iidx/iidx.h
index f5cadfe..2178901 100644
--- a/src/spice2x/games/iidx/iidx.h
+++ b/src/spice2x/games/iidx/iidx.h
@@ -31,8 +31,6 @@ namespace games::iidx {
extern bool NATIVE_TOUCH;
extern std::optional SOUND_OUTPUT_DEVICE;
extern std::optional ASIO_DRIVER;
- extern uint32_t TT_DELAY_P1;
- extern uint32_t TT_DELAY_P2;
extern uint8_t DIGITAL_TT_SENS;
extern std::optional SUBSCREEN_OVERLAY_SIZE;
extern std::optional SCREEN_MODE;
diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp
index 9be9e88..32b032b 100644
--- a/src/spice2x/launcher/launcher.cpp
+++ b/src/spice2x/launcher/launcher.cpp
@@ -518,12 +518,6 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::IIDXTDJMode].value_bool()) {
games::iidx::TDJ_MODE = true;
}
- if (options[launcher::Options::IIDXTTDelayP1].is_active()) {
- games::iidx::TT_DELAY_P1 = options[launcher::Options::IIDXTTDelayP1].value_uint32();
- }
- if (options[launcher::Options::IIDXTTDelayP2].is_active()) {
- games::iidx::TT_DELAY_P2 = options[launcher::Options::IIDXTTDelayP2].value_uint32();
- }
if (options[launcher::Options::spice2x_IIDXDigitalTTSensitivity].is_active()) {
games::iidx::DIGITAL_TT_SENS = (uint8_t)
options[launcher::Options::spice2x_IIDXDigitalTTSensitivity].value_uint32();
diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp
index aeb7a0a..42944cf 100644
--- a/src/spice2x/launcher/options.cpp
+++ b/src/spice2x/launcher/options.cpp
@@ -655,30 +655,6 @@ static const std::vector OPTION_DEFINITIONS = {
.game_name = "Beatmania IIDX",
.category = "Game Options",
},
- {
- // IIDXTTDelayP1
- .title = "IIDX TT Delay ms (Player 1)",
- .name = "iidxttdelayp1",
- .desc = "Delays turntable by number of milliseconds. "
- "Delay will be *at most* the specified period; maximum error is dependent on the game's poll rate. "
- "As usual, changing any option requires a restart. Default: 0 (no delay).",
- .type = OptionType::Integer,
- .setting_name = "(0-500)",
- .game_name = "Beatmania IIDX",
- .category = "Game Options (Advanced)",
- },
- {
- // IIDXTTDelayP2
- .title = "IIDX TT Delay ms (Player 2)",
- .name = "iidxttdelayp2",
- .desc = "Delays turntable by number of milliseconds. "
- "Delay will be *at most* the specified period; maximum error is dependent on the game's poll rate. "
- "As usual, changing any option requires a restart. Default: 0 (no delay).",
- .type = OptionType::Integer,
- .setting_name = "(0-500)",
- .game_name = "Beatmania IIDX",
- .category = "Game Options (Advanced)",
- },
{
// spice2x_IIDXDigitalTTSensitivity
.title = "IIDX Digital TT Sensitivity",
diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h
index 2b97a9f..49974c5 100644
--- a/src/spice2x/launcher/options.h
+++ b/src/spice2x/launcher/options.h
@@ -69,8 +69,6 @@ namespace launcher {
IIDXAsioDriver,
IIDXBIO2FW,
IIDXTDJMode,
- IIDXTTDelayP1,
- IIDXTTDelayP2,
spice2x_IIDXDigitalTTSensitivity,
IIDXDigitalTTSocd,
spice2x_IIDXLDJForce720p,
diff --git a/src/spice2x/overlay/windows/config.cpp b/src/spice2x/overlay/windows/config.cpp
index b72549c..23cbe1e 100644
--- a/src/spice2x/overlay/windows/config.cpp
+++ b/src/spice2x/overlay/windows/config.cpp
@@ -2539,9 +2539,9 @@ namespace overlay::windows {
}
}
- // hide deadzone for circular analog since it doesn't make any sense
+ // hide deadzone for circular analog since it doesn't make any sense (unless in relative mode)
if ((device->type == rawinput::HID || device->type == rawinput::MIDI) &&
- analog.getType() != GameAPI::Analogs::AnalogType::Circular) {
+ ((analog.getType() != GameAPI::Analogs::AnalogType::Circular) || analog.isRelativeMode())) {
auto deadzone = analog.getDeadzone();
// for back compat (before each analog had a type)
@@ -2585,16 +2585,46 @@ namespace overlay::windows {
if (analog.getType() == GameAPI::Analogs::AnalogType::Circular) {
// smoothing
bool smoothing = analog.getSmoothing();
+ ImGui::BeginDisabled(analog.isRelativeMode());
ImGui::Checkbox("Smooth Axis (adds latency)", &smoothing);
ImGui::SameLine();
ImGui::HelpMarker(
"Apply a moving average algorithm; intended for angular input (knobs, turntables). "
"Adds a slight bit of latency to input as the algorithm averages out recent input. "
"Only use in dire situations where the input is too jittery for the game.");
+ ImGui::EndDisabled();
if (smoothing != analog.getSmoothing()) {
analog.setSmoothing(smoothing);
}
+
+ // relative input mode
+ bool relative_analog = analog.isRelativeMode();
+ ImGui::Checkbox("Relative Axis", &relative_analog);
+ ImGui::SameLine();
+ ImGui::HelpMarker(
+ "Use relative directional input instead of positional values. "
+ "Can be used to translate analog sticks to knob input.\n\n"
+ "At default settings, max speed is one revolution per second.\n\n"
+ "Adjust sensitivity to increase/decrease max speed, and deadzone to prevent jitter.");
+ if (relative_analog != analog.isRelativeMode()) {
+ analog.setRelativeMode(relative_analog);
+ }
}
+
+ // delay
+ int delay = analog.getDelayMs();
+ if (ImGui::InputInt("Delay (ms)", &delay, 1, 1)) {
+ delay = std::clamp(delay, 0, 250);
+ analog.setDelayMs(delay);
+ }
+ ImGui::SameLine();
+ ImGui::HelpMarker(
+ "Adds an artificial delay, in milliseconds.\n\n"
+ "Value read by the game engine will be delayed AT MOST this amount of time (coould be slightly less).\n\n"
+ "To minimize quantization error, delay value should be a multiple of the game's poll rate, "
+ "add 0.5ms of buffer, and then round up to the nearest millisecond.\n\n"
+ "If you aren't sure, assume the game polls for input at the same rate as the display FPS."
+ );
}
}
@@ -5406,6 +5436,8 @@ namespace overlay::windows {
a.setInvert(ta.invert);
a.setSmoothing(ta.smoothing);
a.setMultiplier(ta.multiplier);
+ a.setRelativeMode(ta.relative_mode);
+ a.setDelayMs(ta.delay_ms);
::Config::getInstance().updateBinding(game, a);
break;
}
diff --git a/src/spice2x/overlay/windows/controller_presets.h b/src/spice2x/overlay/windows/controller_presets.h
index fdb82b0..a925fbf 100644
--- a/src/spice2x/overlay/windows/controller_presets.h
+++ b/src/spice2x/overlay/windows/controller_presets.h
@@ -71,6 +71,8 @@ namespace overlay::windows {
bool invert = false;
bool smoothing = false;
int multiplier = 1;
+ bool relative_mode = false;
+ uint32_t delay_ms = 0;
bool is_device() const { return !device_identifier.empty(); }
bool is_unbound() const { return device_identifier.empty() && index == 0xFF; }
@@ -87,6 +89,8 @@ namespace overlay::windows {
invert = a.getInvert();
smoothing = a.getSmoothing();
multiplier = a.getMultiplier();
+ relative_mode = a.isRelativeMode();
+ delay_ms = a.getDelayMs();
}
};