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
This commit is contained in:
bluechilliz3 2026-05-11 00:21:12 +01:00
parent 5e8695a157
commit 26ee1fa3b6
5 changed files with 147 additions and 0 deletions

View file

@ -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<uint32_t>(2000 / fps);
}
class Controller
{
public:
@ -20,6 +32,13 @@ public:
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 @@ public:
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 @@ public:
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)

View file

@ -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)
{

View file

@ -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 },

View file

@ -52,6 +52,12 @@ enum class ECameraRotationMode : uint32_t
Reverse
};
enum class ERightTriggerAction : uint32_t
{
Drift,
Boost
};
enum class EControllerIcons : uint32_t
{
Auto,

View file

@ -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);