diff --git a/CMake/Assets.cmake b/CMake/Assets.cmake index 5033dea10..c791c032d 100644 --- a/CMake/Assets.cmake +++ b/CMake/Assets.cmake @@ -111,6 +111,8 @@ set(devilutionx_assets fonts/24-20.bin fonts/24-20.clx fonts/24-26.clx + fonts/24-e0.bin + fonts/24-e0.clx fonts/30-00.bin fonts/30-00.clx fonts/30-01.bin diff --git a/Packaging/resources/assets/fonts/24-e0.bin b/Packaging/resources/assets/fonts/24-e0.bin new file mode 100644 index 000000000..aa83226a2 Binary files /dev/null and b/Packaging/resources/assets/fonts/24-e0.bin differ diff --git a/Packaging/resources/assets/fonts/24-e0.clx b/Packaging/resources/assets/fonts/24-e0.clx new file mode 100644 index 000000000..41ac5c4b3 Binary files /dev/null and b/Packaging/resources/assets/fonts/24-e0.clx differ diff --git a/Source/DiabloUI/settingsmenu.cpp b/Source/DiabloUI/settingsmenu.cpp index 70a0a749d..88c9fd3b5 100644 --- a/Source/DiabloUI/settingsmenu.cpp +++ b/Source/DiabloUI/settingsmenu.cpp @@ -5,6 +5,8 @@ #include "DiabloUI/diabloui.h" #include "DiabloUI/scrollbar.h" #include "control.h" +#include "controls/controller_motion.h" +#include "controls/plrctrls.h" #include "controls/remap_keyboard.h" #include "engine/render/text_render.hpp" #include "hwcursor.hpp" @@ -17,7 +19,8 @@ namespace devilution { namespace { -constexpr size_t IndexKeyInput = 1; +constexpr size_t IndexKeyOrPadInput = 1; +constexpr size_t IndexPadTimerText = 2; bool endMenu = false; bool backToMain = false; @@ -31,6 +34,7 @@ enum class ShownMenuType : uint8_t { Settings, ListOption, KeyInput, + PadInput, }; ShownMenuType shownMenu; @@ -44,8 +48,14 @@ enum class SpecialMenuEntry : int8_t { None = -1, PreviousMenu = -2, UnbindKey = -3, + BindPadButton = -4, + UnbindPadButton = -5, }; +ControllerButtonCombo padEntryCombo {}; +Uint32 padEntryStartTime = 0; +std::string padEntryTimerText; + bool IsValidEntry(OptionEntryBase *pOptionEntry) { auto flags = pOptionEntry->GetFlags(); @@ -90,6 +100,41 @@ void GoBackOneMenuLevel() shownMenu = ShownMenuType::Settings; } +void StartPadEntryTimer() +{ + padEntryCombo = ControllerButton_NONE; + padEntryStartTime = SDL_GetTicks(); + if (padEntryStartTime == 0) + padEntryStartTime++; + // Removes access to these dialog items while entering bindings + for (size_t i = IndexPadTimerText + 1; i < vecDialogItems.size(); i++) + vecDialogItems[i]->uiFlags |= UiFlags::ElementHidden; +} + +void StopPadEntryTimer() +{ + padEntryCombo = ControllerButton_NONE; + padEntryStartTime = 0; + padEntryTimerText = ""; + vecDialogItems[IndexPadTimerText]->m_text = padEntryTimerText; + // Restores access to these dialog items after binding is complete + for (size_t i = IndexPadTimerText + 1; i < vecDialogItems.size(); i++) + vecDialogItems[i]->uiFlags &= ~UiFlags::ElementHidden; +} + +void UpdatePadEntryTimerText() +{ + if (shownMenu != ShownMenuType::PadInput) + return; + Uint32 elapsed = SDL_GetTicks() - padEntryStartTime; + if (padEntryStartTime == 0 || elapsed > 10000) { + StopPadEntryTimer(); + return; + } + padEntryTimerText = StrCat(_("Press gamepad buttons to change."), " ", 10 - elapsed / 1000); + vecDialogItems[IndexPadTimerText]->m_text = padEntryTimerText; +} + void UpdateDescription(const OptionEntryBase &option) { auto paragraphs = WordWrapString(option.GetDescription(), rectDescription.size.width, GameFont12, 1); @@ -159,10 +204,19 @@ void ItemSelected(int value) case SpecialMenuEntry::PreviousMenu: GoBackOneMenuLevel(); break; - case SpecialMenuEntry::UnbindKey: + case SpecialMenuEntry::UnbindKey: { auto *pOptionKey = static_cast(selectedOption); pOptionKey->SetValue(SDLK_UNKNOWN); - vecDialogItems[IndexKeyInput]->m_text = selectedOption->GetValueDescription().data(); + vecDialogItems[IndexKeyOrPadInput]->m_text = selectedOption->GetValueDescription().data(); + break; + } + case SpecialMenuEntry::BindPadButton: + StartPadEntryTimer(); + break; + case SpecialMenuEntry::UnbindPadButton: + auto *pOptionPad = static_cast(selectedOption); + pOptionPad->SetValue(ControllerButton_NONE); + vecDialogItems[IndexKeyOrPadInput]->m_text = selectedOption->GetValueDescription().data(); break; } return; @@ -189,6 +243,10 @@ void ItemSelected(int value) selectedOption = pOption; endMenu = true; shownMenu = ShownMenuType::KeyInput; + } else if (pOption->GetType() == OptionEntryType::PadButton) { + selectedOption = pOption; + endMenu = true; + shownMenu = ShownMenuType::PadInput; } else { updateValueDescription = ChangeOptionValue(pOption, 0); } @@ -213,6 +271,7 @@ void ItemSelected(int value) GoBackOneMenuLevel(); } break; case ShownMenuType::KeyInput: + case ShownMenuType::PadInput: break; } } @@ -312,10 +371,10 @@ void UiSettingsMenu() case ShownMenuType::KeyInput: { vecDialogItems.push_back(std::make_unique(_("Bound key:"), static_cast(SpecialMenuEntry::None), UiFlags::ColorWhitegold | UiFlags::ElementDisabled)); vecDialogItems.push_back(std::make_unique(selectedOption->GetValueDescription(), static_cast(SpecialMenuEntry::None), UiFlags::ColorUiGold)); - assert(IndexKeyInput == vecDialogItems.size() - 1); - itemToSelect = IndexKeyInput; + assert(IndexKeyOrPadInput == vecDialogItems.size() - 1); + itemToSelect = IndexKeyOrPadInput; eventHandler = [](SDL_Event &event) { - if (SelectedItem != IndexKeyInput) + if (SelectedItem != IndexKeyOrPadInput) return false; uint32_t key = SDLK_UNKNOWN; switch (event.type) { @@ -343,7 +402,7 @@ void UiSettingsMenu() auto *pOptionKey = static_cast(selectedOption); if (!pOptionKey->SetValue(key)) return false; - vecDialogItems[IndexKeyInput]->m_text = selectedOption->GetValueDescription().data(); + vecDialogItems[IndexKeyOrPadInput]->m_text = selectedOption->GetValueDescription().data(); return true; }; vecDialogItems.push_back(std::make_unique(_("Press any key to change."), static_cast(SpecialMenuEntry::None), UiFlags::ColorUiSilver | UiFlags::ElementDisabled)); @@ -351,6 +410,53 @@ void UiSettingsMenu() vecDialogItems.push_back(std::make_unique(_("Unbind key"), static_cast(SpecialMenuEntry::UnbindKey), UiFlags::ColorUiGold)); UpdateDescription(*selectedOption); } break; + case ShownMenuType::PadInput: { + vecDialogItems.push_back(std::make_unique(_("Bound button combo:"), static_cast(SpecialMenuEntry::None), UiFlags::ColorWhitegold | UiFlags::ElementDisabled)); + vecDialogItems.push_back(std::make_unique(selectedOption->GetValueDescription(), static_cast(SpecialMenuEntry::BindPadButton), UiFlags::ColorUiGold)); + assert(IndexKeyOrPadInput == vecDialogItems.size() - 1); + itemToSelect = IndexKeyOrPadInput; + + vecDialogItems.push_back(std::make_unique(padEntryTimerText, static_cast(SpecialMenuEntry::None), UiFlags::ColorUiSilver | UiFlags::ElementDisabled)); + assert(IndexPadTimerText == vecDialogItems.size() - 1); + + vecDialogItems.push_back(std::make_unique("", static_cast(SpecialMenuEntry::None), UiFlags::ElementDisabled)); + vecDialogItems.push_back(std::make_unique(_("Unbind button combo"), static_cast(SpecialMenuEntry::UnbindPadButton), UiFlags::ColorUiGold)); + + padEntryStartTime = 0; + eventHandler = [](SDL_Event &event) { + if (padEntryStartTime == 0) + return false; + + ControllerButtonEvent ctrlEvent = ToControllerButtonEvent(event); + bool isGamepadMotion = ProcessControllerMotion(event, ctrlEvent); + DetectInputMethod(event, ctrlEvent); + if (event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE) { + StopPadEntryTimer(); + return true; + } + if (isGamepadMotion || IsAnyOf(ctrlEvent.button, ControllerButton_NONE, ControllerButton_IGNORE)) { + return true; + } + + bool modifierPressed = padEntryCombo.modifier != ControllerButton_NONE && IsControllerButtonPressed(padEntryCombo.modifier); + bool buttonPressed = padEntryCombo.button != ControllerButton_NONE && IsControllerButtonPressed(padEntryCombo.button); + if (ctrlEvent.up) { + // When the player has released all relevant inputs, assume the binding is finished and stop the timer + if (padEntryCombo.button != ControllerButton_NONE && !modifierPressed && !buttonPressed) + StopPadEntryTimer(); + return true; + } + + auto *pOptionPad = static_cast(selectedOption); + if (!modifierPressed && buttonPressed) + padEntryCombo.modifier = padEntryCombo.button; + padEntryCombo.button = ctrlEvent.button; + if (pOptionPad->SetValue(padEntryCombo)) + vecDialogItems[IndexKeyOrPadInput]->m_text = selectedOption->GetValueDescription().data(); + return true; + }; + UpdateDescription(*selectedOption); + } break; } vecDialogItems.push_back(std::make_unique("", static_cast(SpecialMenuEntry::None), UiFlags::ElementDisabled)); @@ -362,7 +468,7 @@ void UiSettingsMenu() while (!endMenu) { UiClearScreen(); - UiRenderItems(vecDialog); + UpdatePadEntryTimerText(); UiPollAndRender(eventHandler); } diff --git a/Source/controls/controller_buttons.cpp b/Source/controls/controller_buttons.cpp index 69299ee70..4da80c5be 100644 --- a/Source/controls/controller_buttons.cpp +++ b/Source/controls/controller_buttons.cpp @@ -1,5 +1,7 @@ #include "controller_buttons.h" +#include "plrctrls.h" + namespace devilution { namespace controller_button_icon { const string_view Playstation_Triangle = "\uE000"; @@ -56,7 +58,7 @@ const string_view Nintendo_LStick_W = "\uE032"; const string_view Nintendo_LStick_SW = "\uE033"; const string_view Nintendo_LStick_N = "\uE034"; const string_view Nintendo_LStick = "\uE035"; -const string_view Nintendoo_LStick_S = "\uE036"; +const string_view Nintendo_LStick_S = "\uE036"; const string_view Nintendo_LStick_NE = "\uE037"; const string_view Nintendo_LStick_E = "\uE038"; const string_view Nintendo_LStick_SE = "\uE039"; @@ -86,9 +88,9 @@ const string_view Xbox_RT = "\uE050"; const string_view Xbox_LB = "\uE051"; const string_view Xbox_RB = "\uE052"; const string_view Xbox_DPad_Up = "\uE053"; -const string_view Xbox_Dpad_Right = "\uE054"; -const string_view Xbox_Dpad_Down = "\uE055"; -const string_view Xbox_Dpad_Left = "\uE056"; +const string_view Xbox_DPad_Right = "\uE054"; +const string_view Xbox_DPad_Down = "\uE055"; +const string_view Xbox_DPad_Left = "\uE056"; const string_view Xbox_LStick_NW = "\uE057"; const string_view Xbox_LStick_W = "\uE058"; const string_view Xbox_LStick_SW = "\uE059"; @@ -110,4 +112,184 @@ const string_view Xbox_RStick_SE = "\uE068"; const string_view Xbox_RStick_Click = "\uE069"; const string_view Xbox_Xbox = "\uE06A"; } // namespace controller_button_icon + +string_view ToPlayStationIcon(ControllerButton button) +{ + switch (button) { + case devilution::ControllerButton_BUTTON_A: + return controller_button_icon::Playstation_X; + case devilution::ControllerButton_BUTTON_B: + return controller_button_icon::Playstation_Circle; + case devilution::ControllerButton_BUTTON_X: + return controller_button_icon::Playstation_Square; + case devilution::ControllerButton_BUTTON_Y: + return controller_button_icon::Playstation_Triangle; + case devilution::ControllerButton_BUTTON_START: + return controller_button_icon::Playstation_Options; + case devilution::ControllerButton_BUTTON_BACK: + return controller_button_icon::Playstation_Share; + case devilution::ControllerButton_AXIS_TRIGGERLEFT: + return controller_button_icon::Playstation_L2; + case devilution::ControllerButton_AXIS_TRIGGERRIGHT: + return controller_button_icon::Playstation_R2; + case devilution::ControllerButton_BUTTON_LEFTSHOULDER: + return controller_button_icon::Playstation_L1; + case devilution::ControllerButton_BUTTON_RIGHTSHOULDER: + return controller_button_icon::Playstation_R1; + case devilution::ControllerButton_BUTTON_LEFTSTICK: + return controller_button_icon::Playstation_L3; + case devilution::ControllerButton_BUTTON_RIGHTSTICK: + return controller_button_icon::Playstation_R3; + case devilution::ControllerButton_BUTTON_DPAD_UP: + return controller_button_icon::Playstation_DPad_Up; + case devilution::ControllerButton_BUTTON_DPAD_DOWN: + return controller_button_icon::Playstation_DPad_Down; + case devilution::ControllerButton_BUTTON_DPAD_LEFT: + return controller_button_icon::Playstation_DPad_Left; + case devilution::ControllerButton_BUTTON_DPAD_RIGHT: + return controller_button_icon::Playstation_DPad_Right; + default: + return ToGenericButtonText(button); + } +} + +string_view ToNintendoIcon(ControllerButton button) +{ + switch (button) { + case devilution::ControllerButton_BUTTON_A: + return controller_button_icon::Nintendo_B; + case devilution::ControllerButton_BUTTON_B: + return controller_button_icon::Nintendo_A; + case devilution::ControllerButton_BUTTON_X: + return controller_button_icon::Nintendo_Y; + case devilution::ControllerButton_BUTTON_Y: + return controller_button_icon::Nintendo_X; + case devilution::ControllerButton_BUTTON_START: + return controller_button_icon::Nintendo_Plus; + case devilution::ControllerButton_BUTTON_BACK: + return controller_button_icon::Nintendo_Minus; + case devilution::ControllerButton_AXIS_TRIGGERLEFT: + return controller_button_icon::Nintendo_ZL; + case devilution::ControllerButton_AXIS_TRIGGERRIGHT: + return controller_button_icon::Nintendo_ZR; + case devilution::ControllerButton_BUTTON_LEFTSHOULDER: + return controller_button_icon::Nintendo_L; + case devilution::ControllerButton_BUTTON_RIGHTSHOULDER: + return controller_button_icon::Nintendo_R; + case devilution::ControllerButton_BUTTON_LEFTSTICK: + return controller_button_icon::Nintendo_LStick_Click; + case devilution::ControllerButton_BUTTON_RIGHTSTICK: + return controller_button_icon::Nintendo_RStick_Click; + case devilution::ControllerButton_BUTTON_DPAD_UP: + return controller_button_icon::Nintendo_DPad_Up; + case devilution::ControllerButton_BUTTON_DPAD_DOWN: + return controller_button_icon::Nintendo_DPad_Down; + case devilution::ControllerButton_BUTTON_DPAD_LEFT: + return controller_button_icon::Nintendo_DPad_Left; + case devilution::ControllerButton_BUTTON_DPAD_RIGHT: + return controller_button_icon::Nintendo_DPad_Right; + default: + return ToGenericButtonText(button); + } +} + +string_view ToXboxIcon(ControllerButton button) +{ + switch (button) { + case devilution::ControllerButton_BUTTON_A: + return controller_button_icon::Xbox_A; + case devilution::ControllerButton_BUTTON_B: + return controller_button_icon::Xbox_B; + case devilution::ControllerButton_BUTTON_X: + return controller_button_icon::Xbox_X; + case devilution::ControllerButton_BUTTON_Y: + return controller_button_icon::Xbox_Y; + case devilution::ControllerButton_BUTTON_START: + return controller_button_icon::Xbox_Menu; + case devilution::ControllerButton_BUTTON_BACK: + return controller_button_icon::Xbox_View; + case devilution::ControllerButton_AXIS_TRIGGERLEFT: + return controller_button_icon::Xbox_LT; + case devilution::ControllerButton_AXIS_TRIGGERRIGHT: + return controller_button_icon::Xbox_RT; + case devilution::ControllerButton_BUTTON_LEFTSHOULDER: + return controller_button_icon::Xbox_LB; + case devilution::ControllerButton_BUTTON_RIGHTSHOULDER: + return controller_button_icon::Xbox_RB; + case devilution::ControllerButton_BUTTON_LEFTSTICK: + return controller_button_icon::Xbox_LStick_Click; + case devilution::ControllerButton_BUTTON_RIGHTSTICK: + return controller_button_icon::Xbox_RStick_Click; + case devilution::ControllerButton_BUTTON_DPAD_UP: + return controller_button_icon::Xbox_DPad_Up; + case devilution::ControllerButton_BUTTON_DPAD_DOWN: + return controller_button_icon::Xbox_DPad_Down; + case devilution::ControllerButton_BUTTON_DPAD_LEFT: + return controller_button_icon::Xbox_DPad_Left; + case devilution::ControllerButton_BUTTON_DPAD_RIGHT: + return controller_button_icon::Xbox_DPad_Right; + default: + return ToGenericButtonText(button); + } +} + +string_view ToGenericButtonText(ControllerButton button) +{ + switch (button) { + case devilution::ControllerButton_BUTTON_A: + return "A"; + case devilution::ControllerButton_BUTTON_B: + return "B"; + case devilution::ControllerButton_BUTTON_X: + return "X"; + case devilution::ControllerButton_BUTTON_Y: + return "Y"; + case devilution::ControllerButton_BUTTON_START: + return "Start"; + case devilution::ControllerButton_BUTTON_BACK: + return "Select"; + case devilution::ControllerButton_AXIS_TRIGGERLEFT: + return "LT"; + case devilution::ControllerButton_AXIS_TRIGGERRIGHT: + return "RT"; + case devilution::ControllerButton_BUTTON_LEFTSHOULDER: + return "LB"; + case devilution::ControllerButton_BUTTON_RIGHTSHOULDER: + return "RB"; + case devilution::ControllerButton_BUTTON_LEFTSTICK: + return "LS"; + case devilution::ControllerButton_BUTTON_RIGHTSTICK: + return "RS"; + case devilution::ControllerButton_BUTTON_DPAD_UP: + return "Up"; + case devilution::ControllerButton_BUTTON_DPAD_DOWN: + return "Down"; + case devilution::ControllerButton_BUTTON_DPAD_LEFT: + return "Left"; + case devilution::ControllerButton_BUTTON_DPAD_RIGHT: + return "Right"; + case devilution::ControllerButton_NONE: + return "None"; + case devilution::ControllerButton_IGNORE: + return "Ignored"; + default: + return "Unknown"; + } +} + +string_view ToString(ControllerButton button) +{ + switch (GamepadType) { + case devilution::GamepadLayout::PlayStation: + return ToPlayStationIcon(button); + case devilution::GamepadLayout::Nintendo: + return ToNintendoIcon(button); + case devilution::GamepadLayout::Xbox: + return ToXboxIcon(button); + default: + case devilution::GamepadLayout::Generic: + return ToGenericButtonText(button); + } +} + } // namespace devilution diff --git a/Source/controls/controller_buttons.h b/Source/controls/controller_buttons.h index 2dc2ef676..a65bee5dd 100644 --- a/Source/controls/controller_buttons.h +++ b/Source/controls/controller_buttons.h @@ -119,7 +119,7 @@ extern const string_view Nintendo_LStick_W; extern const string_view Nintendo_LStick_SW; extern const string_view Nintendo_LStick_N; extern const string_view Nintendo_LStick; -extern const string_view Nintendoo_LStick_S; +extern const string_view Nintendo_LStick_S; extern const string_view Nintendo_LStick_NE; extern const string_view Nintendo_LStick_E; extern const string_view Nintendo_LStick_SE; @@ -149,9 +149,9 @@ extern const string_view Xbox_RT; extern const string_view Xbox_LB; extern const string_view Xbox_RB; extern const string_view Xbox_DPad_Up; -extern const string_view Xbox_Dpad_Right; -extern const string_view Xbox_Dpad_Down; -extern const string_view Xbox_Dpad_Left; +extern const string_view Xbox_DPad_Right; +extern const string_view Xbox_DPad_Down; +extern const string_view Xbox_DPad_Left; extern const string_view Xbox_LStick_NW; extern const string_view Xbox_LStick_W; extern const string_view Xbox_LStick_SW; @@ -173,4 +173,11 @@ extern const string_view Xbox_RStick_SE; extern const string_view Xbox_RStick_Click; extern const string_view Xbox_Xbox; } // namespace controller_button_icon + +string_view ToPlayStationIcon(ControllerButton button); +string_view ToNintendoIcon(ControllerButton button); +string_view ToXboxIcon(ControllerButton button); +string_view ToGenericButtonText(ControllerButton button); +string_view ToString(ControllerButton button); + } // namespace devilution diff --git a/Source/controls/devices/game_controller.cpp b/Source/controls/devices/game_controller.cpp index 060c5f843..f9baaabcd 100644 --- a/Source/controls/devices/game_controller.cpp +++ b/Source/controls/devices/game_controller.cpp @@ -25,8 +25,9 @@ ControllerButton GameController::ToControllerButton(const SDL_Event &event) case SDL_CONTROLLERAXISMOTION: switch (event.caxis.axis) { case SDL_CONTROLLER_AXIS_TRIGGERLEFT: - if (event.caxis.value < 8192) { // 25% pressed + if (event.caxis.value < 8192 && trigger_left_is_down_) { // 25% pressed trigger_left_is_down_ = false; + trigger_left_state_ = ControllerButton_AXIS_TRIGGERLEFT; } if (event.caxis.value > 16384 && !trigger_left_is_down_) { // 50% pressed trigger_left_is_down_ = true; @@ -34,8 +35,9 @@ ControllerButton GameController::ToControllerButton(const SDL_Event &event) } return trigger_left_state_; case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: - if (event.caxis.value < 8192) { // 25% pressed + if (event.caxis.value < 8192 && trigger_right_is_down_) { // 25% pressed trigger_right_is_down_ = false; + trigger_right_state_ = ControllerButton_AXIS_TRIGGERRIGHT; } if (event.caxis.value > 16384 && !trigger_right_is_down_) { // 50% pressed trigger_right_is_down_ = true; diff --git a/Source/options.cpp b/Source/options.cpp index bd18aac97..b1392fafa 100644 --- a/Source/options.cpp +++ b/Source/options.cpp @@ -16,6 +16,7 @@ #include "control.h" #include "controls/controller.h" #include "controls/game_controls.h" +#include "controls/plrctrls.h" #include "discord/discord.h" #include "engine/demomode.h" #include "engine/sound_defs.hpp" @@ -1462,7 +1463,7 @@ std::vector PadmapperOptions::GetEntries() } PadmapperOptions::Action::Action(string_view key, const char *name, const char *description, ControllerButtonCombo defaultInput, std::function actionPressed, std::function actionReleased, std::function enable, unsigned index) - : OptionEntryBase(key, OptionEntryFlags::Invisible, name, description) + : OptionEntryBase(key, OptionEntryFlags::None, name, description) , defaultInput(defaultInput) , actionPressed(std::move(actionPressed)) , actionReleased(std::move(actionReleased)) @@ -1553,37 +1554,36 @@ void PadmapperOptions::Action::SaveToIni(string_view category) const SetIniValue(category.data(), key.data(), inputName.data()); } +void PadmapperOptions::Action::UpdateValueDescription() const +{ + boundInputDescriptionType = GamepadType; + if (boundInput.button == ControllerButton_NONE) { + boundInputDescription = ""; + return; + } + string_view buttonName = ToString(boundInput.button); + if (boundInput.modifier == ControllerButton_NONE) { + boundInputDescription = std::string(buttonName); + return; + } + string_view modifierName = ToString(boundInput.modifier); + boundInputDescription = StrCat(modifierName, "+", buttonName); +} + string_view PadmapperOptions::Action::GetValueDescription() const { + if (GamepadType != boundInputDescriptionType) + UpdateValueDescription(); return boundInputDescription; } bool PadmapperOptions::Action::SetValue(ControllerButtonCombo value) { - const std::string &modifierName = sgOptions.Padmapper.buttonToButtonName[static_cast(value.modifier)]; - const std::string &buttonName = sgOptions.Padmapper.buttonToButtonName[static_cast(value.button)]; - if ((value.modifier != ControllerButton_NONE && modifierName.empty()) - || (value.button != ControllerButton_NONE && buttonName.empty())) { - // Ignore invalid button combos - return false; - } - - // Remove old button combo - if (boundInput.button != ControllerButton_NONE) { + if (boundInput.button != ControllerButton_NONE) boundInput = {}; - boundInputDescription = ""; - } - - // Add new button combo - if (value.button != ControllerButton_NONE) { + if (value.button != ControllerButton_NONE) boundInput = value; - boundInputDescription = buttonName; - - if (!modifierName.empty()) { - boundInputDescription = StrCat(modifierName, "+", boundInputDescription); - } - } - + UpdateValueDescription(); return true; } diff --git a/Source/options.h b/Source/options.h index 7091a907e..d5d445db8 100644 --- a/Source/options.h +++ b/Source/options.h @@ -10,6 +10,7 @@ #include "controls/controller.h" #include "controls/controller_buttons.h" +#include "controls/game_controls.h" #include "engine/sound_defs.hpp" #include "miniwin/misc_msg.h" #include "pack.h" @@ -722,11 +723,14 @@ struct PadmapperOptions : OptionCategoryBase { std::function actionReleased; std::function enable; ControllerButtonCombo boundInput {}; - std::string boundInputDescription; + mutable GamepadLayout boundInputDescriptionType = GamepadLayout::Generic; + mutable std::string boundInputDescription; unsigned dynamicIndex; std::string dynamicKey; mutable std::string dynamicName; + void UpdateValueDescription() const; + friend struct PadmapperOptions; };