From 26ee1fa3b68038f09aa4a802e9ba926d6fc1ff27 Mon Sep 17 00:00:00 2001 From: bluechilliz3 <212103429+bluechilliz3@users.noreply.github.com> Date: Mon, 11 May 2026 00:21:12 +0100 Subject: [PATCH] implemented the option to set right trigger to be the boost button which matches the modern boost sonic games like sonic frontiers and shadow generations. The entry in the config.toml file will be an enum with options for "drift" or "boost" under "RightTriggerAction" the benifits rather than the user using controller remapping software is this ONLY effects the day stages so the trigger will function normally when the using plays the night stages. The option especially works well in Eggmanland. The right trigger seemlessly de/activates when switching between sonics that stage --- UnleashedRecomp/hid/driver/sdl_hid.cpp | 74 ++++++++++++++++++++++++ UnleashedRecomp/locale/config_locale.cpp | 60 +++++++++++++++++++ UnleashedRecomp/user/config.cpp | 6 ++ UnleashedRecomp/user/config.h | 6 ++ UnleashedRecomp/user/config_def.h | 1 + 5 files changed, 147 insertions(+) diff --git a/UnleashedRecomp/hid/driver/sdl_hid.cpp b/UnleashedRecomp/hid/driver/sdl_hid.cpp index 762355242..76489cb3a 100644 --- a/UnleashedRecomp/hid/driver/sdl_hid.cpp +++ b/UnleashedRecomp/hid/driver/sdl_hid.cpp @@ -10,6 +10,18 @@ #define TRANSLATE_INPUT(S, X) SDL_GameControllerGetButton(controller, S) << FirstBitLow(X) #define VIBRATION_TIMEOUT_MS 5000 +static bool IsBoostOnRightTriggerActive() +{ + // true if sonic is werehog and right trigger preference is boost + return !App::s_isWerehog && Config::RightTriggerAction == ERightTriggerAction::Boost; +} + +static uint32_t GetBoostCancelDurationMs() +{ + int32_t fps = Config::FPS > 0 ? Config::FPS : 60; + return static_cast(2000 / fps); +} + class Controller { public: @@ -20,6 +32,13 @@ class Controller XAMINPUT_VIBRATION vibration{ 0, 0 }; int index{}; + // for when user sets Config::RightTriggerAction to ERightTriggerAction this + // increases stability to allow square/X to be recognised by the game while + // right trigger is being pressed down especially when the user is moving + // the thumbsticks + bool xWasHeldLastPoll{}; + uint32_t xCancelUntilTick{}; + Controller() = default; explicit Controller(int index) : Controller(SDL_GameControllerOpen(index)) @@ -99,6 +118,33 @@ class Controller pad.bLeftTrigger = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) >> 7; pad.bRightTrigger = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) >> 7; + + + + if (IsBoostOnRightTriggerActive()) + { + bool xHeldPhysically = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_X) != 0; + bool xRisingEdge = xHeldPhysically && !xWasHeldLastPoll; + bool rtPulled = pad.bRightTrigger >= 30; // TODO: change this to a pressure preference + + // these checks are in place to improve responsiveness of square/X while right trigger is held down + if (xRisingEdge && rtPulled) + xCancelUntilTick = SDL_GetTicks() + GetBoostCancelDurationMs(); + + bool inCancelWindow = SDL_TICKS_PASSED(xCancelUntilTick, SDL_GetTicks()); + + if (inCancelWindow) + pad.wButtons &= ~XAMINPUT_GAMEPAD_X; + else if (xHeldPhysically || rtPulled) + pad.wButtons |= XAMINPUT_GAMEPAD_X; + else + pad.wButtons &= ~XAMINPUT_GAMEPAD_X; + + if (rtPulled) + pad.bRightTrigger = 0; + + xWasHeldLastPoll = xHeldPhysically; + } } void Poll() @@ -129,6 +175,34 @@ class Controller pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_B, XAMINPUT_GAMEPAD_B); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_X, XAMINPUT_GAMEPAD_X); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_Y, XAMINPUT_GAMEPAD_Y); + + // when playing day stages keep the right trigger mirrored onto square/X + // so so the game knows the user is boosting. This will remove the actual + // right trigger from the game so sonic wouldn't drift + if (IsBoostOnRightTriggerActive()) + { + bool xHeldPhysically = (pad.wButtons & XAMINPUT_GAMEPAD_X) != 0; + bool xRisingEdge = xHeldPhysically && !xWasHeldLastPoll; + uint8_t rtRaw = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) >> 7; + bool rtPulled = rtRaw >= 30; // TODO: change this to a pressure preference + + // like in Poll() these checks are in place to improve responsiveness + // of square/X while right trigger is held down + if (xRisingEdge && rtPulled) + xCancelUntilTick = SDL_GetTicks() + GetBoostCancelDurationMs(); + + bool inCancelWindow = SDL_TICKS_PASSED(xCancelUntilTick, SDL_GetTicks()); + + if (inCancelWindow) + pad.wButtons &= ~XAMINPUT_GAMEPAD_X; + else if (rtPulled) + pad.wButtons |= XAMINPUT_GAMEPAD_X; + + if (rtPulled) + pad.bRightTrigger = 0; + + xWasHeldLastPoll = xHeldPhysically; + } } void SetVibration(const XAMINPUT_VIBRATION& vibration) diff --git a/UnleashedRecomp/locale/config_locale.cpp b/UnleashedRecomp/locale/config_locale.cpp index 1242e4eb6..2e03be747 100644 --- a/UnleashedRecomp/locale/config_locale.cpp +++ b/UnleashedRecomp/locale/config_locale.cpp @@ -216,6 +216,66 @@ CONFIG_DEFINE_ENUM_LOCALE(ETimeOfDayTransition) } }; +// Translation required +// Japanese Notes: This localization should include furigana in its description. +CONFIG_DEFINE_LOCALE(RightTriggerAction) +{ + { ELanguage::English, { "Right Trigger Action", "Choose what the right trigger does in day stages." } }, + { ELanguage::Japanese, { "Right Trigger Action", "Choose what the right trigger does in day stages." } }, // Translation required + { ELanguage::German, { "Right Trigger Action", "Choose what the right trigger does in day stages." } }, // Translation required + { ELanguage::French, { "Right Trigger Action", "Choose what the right trigger does in day stages." } }, // Translation required + { ELanguage::Spanish, { "Right Trigger Action", "Choose what the right trigger does in day stages." } }, // Translation required + { ELanguage::Italian, { "Right Trigger Action", "Choose what the right trigger does in day stages." } } // Translation required +}; + +// Translation required +// Japanese Notes: This localization should include furigana in its description. +CONFIG_DEFINE_ENUM_LOCALE(ERightTriggerAction) +{ + { + ELanguage::English, + { + { ERightTriggerAction::Drift, { "DRIFT", "Default: the right trigger acts as drift, matching the original Xbox 360/PS3 controls." } }, + { ERightTriggerAction::Boost, { "BOOST/H.A", "EXPERIMENTAL: The right trigger acts as boost or homing attack. Drift is still available on the left trigger and X/Square still triggers boost too." } } + } + }, + { + ELanguage::Japanese, + { + { ERightTriggerAction::Drift, { "DRIFT", "" } }, + { ERightTriggerAction::Boost, { "BOOST", "" } } + } + }, + { + ELanguage::German, + { + { ERightTriggerAction::Drift, { "DRIFT", "" } }, + { ERightTriggerAction::Boost, { "BOOST", "" } } + } + }, + { + ELanguage::French, + { + { ERightTriggerAction::Drift, { "DRIFT", "" } }, + { ERightTriggerAction::Boost, { "BOOST", "" } } + } + }, + { + ELanguage::Spanish, + { + { ERightTriggerAction::Drift, { "DRIFT", "" } }, + { ERightTriggerAction::Boost, { "BOOST", "" } } + } + }, + { + ELanguage::Italian, + { + { ERightTriggerAction::Drift, { "DRIFT", "" } }, + { ERightTriggerAction::Boost, { "BOOST", "" } } + } + } +}; + // Japanese Notes: This localization should include furigana. CONFIG_DEFINE_LOCALE(ControllerIcons) { diff --git a/UnleashedRecomp/user/config.cpp b/UnleashedRecomp/user/config.cpp index bd622b7f8..1de78e327 100644 --- a/UnleashedRecomp/user/config.cpp +++ b/UnleashedRecomp/user/config.cpp @@ -30,6 +30,12 @@ CONFIG_DEFINE_ENUM_TEMPLATE(ECameraRotationMode) { "Reverse", ECameraRotationMode::Reverse }, }; +CONFIG_DEFINE_ENUM_TEMPLATE(ERightTriggerAction) +{ + { "Drift", ERightTriggerAction::Drift }, + { "Boost", ERightTriggerAction::Boost } +}; + CONFIG_DEFINE_ENUM_TEMPLATE(EControllerIcons) { { "Auto", EControllerIcons::Auto }, diff --git a/UnleashedRecomp/user/config.h b/UnleashedRecomp/user/config.h index 9e87d12e2..ee106d0a3 100644 --- a/UnleashedRecomp/user/config.h +++ b/UnleashedRecomp/user/config.h @@ -52,6 +52,12 @@ enum class ECameraRotationMode : uint32_t Reverse }; +enum class ERightTriggerAction : uint32_t +{ + Drift, + Boost +}; + enum class EControllerIcons : uint32_t { Auto, diff --git a/UnleashedRecomp/user/config_def.h b/UnleashedRecomp/user/config_def.h index 80eb29c93..96e909c5c 100644 --- a/UnleashedRecomp/user/config_def.h +++ b/UnleashedRecomp/user/config_def.h @@ -12,6 +12,7 @@ CONFIG_DEFINE("System", bool, ShowConsole, false); CONFIG_DEFINE_ENUM_LOCALISED("Input", ECameraRotationMode, HorizontalCamera, ECameraRotationMode::Normal); CONFIG_DEFINE_ENUM_LOCALISED("Input", ECameraRotationMode, VerticalCamera, ECameraRotationMode::Normal); +CONFIG_DEFINE_ENUM_LOCALISED("Input", ERightTriggerAction, RightTriggerAction, ERightTriggerAction::Drift); CONFIG_DEFINE_LOCALISED("Input", bool, Vibration, true); CONFIG_DEFINE_LOCALISED("Input", bool, AllowBackgroundInput, false); CONFIG_DEFINE_ENUM_LOCALISED("Input", EControllerIcons, ControllerIcons, EControllerIcons::Auto);