diff --git a/Source/DiabloUI/settingsmenu.cpp b/Source/DiabloUI/settingsmenu.cpp index cedf19e74e2..3e6be08b1a5 100644 --- a/Source/DiabloUI/settingsmenu.cpp +++ b/Source/DiabloUI/settingsmenu.cpp @@ -445,6 +445,19 @@ void UiSettingsMenu() break; } break; +#if SDL_VERSION_ATLEAST(2, 0, 0) + case SDL_MOUSEWHEEL: + if (event.wheel.y > 0) { + key = MouseScrollUpButton; + } else if (event.wheel.y < 0) { + key = MouseScrollDownButton; + } else if (event.wheel.x > 0) { + key = MouseScrollLeftButton; + } else if (event.wheel.x < 0) { + key = MouseScrollRightButton; + } + break; +#endif } // Ignore unknown keys if (key == SDLK_UNKNOWN) diff --git a/Source/control.h b/Source/control.h index 591e40c549f..13e71afda1d 100644 --- a/Source/control.h +++ b/Source/control.h @@ -77,10 +77,6 @@ inline bool CanPanelsCoverView() const Rectangle &mainPanel = GetMainPanel(); return GetScreenWidth() <= mainPanel.size.width && GetScreenHeight() <= SidePanelSize.height + mainPanel.size.height; } -void DrawSpellList(const Surface &out); -void SetSpell(); -void SetSpeedSpell(size_t slot); -void ToggleSpell(size_t slot); void AddPanelString(std::string_view str); void AddPanelString(std::string &&str); @@ -127,12 +123,6 @@ void DrawFlaskValues(const Surface &out, Point pos, int currValue, int maxValue) */ void control_update_life_mana(); -/** - * @brief draws the current right mouse button spell. - * @param out screen buffer representing the main UI panel - */ -void DrawSpell(const Surface &out); - void InitControlPan(); void DrawCtrlPan(const Surface &out); diff --git a/Source/controls/game_controls.cpp b/Source/controls/game_controls.cpp index 1c19b5fe09c..9538e1032ce 100644 --- a/Source/controls/game_controls.cpp +++ b/Source/controls/game_controls.cpp @@ -13,6 +13,7 @@ #include "gamemenu.h" #include "gmenu.h" #include "options.h" +#include "panels/spell_list.hpp" #include "qol/stash.h" #include "stores.h" diff --git a/Source/controls/touch/event_handlers.cpp b/Source/controls/touch/event_handlers.cpp index 748e6056b1a..9b688c70964 100644 --- a/Source/controls/touch/event_handlers.cpp +++ b/Source/controls/touch/event_handlers.cpp @@ -9,6 +9,7 @@ #include "gmenu.h" #include "inv.h" #include "panels/spell_book.hpp" +#include "panels/spell_list.hpp" #include "qol/stash.h" #include "stores.h" #include "utils/ui_fwd.h" diff --git a/Source/diablo.cpp b/Source/diablo.cpp index f7b256b61c0..fe3a610e6ea 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -746,6 +746,43 @@ void GameEventHandler(const SDL_Event &event, uint16_t modState) MousePosition = { event.button.x, event.button.y }; HandleMouseButtonUp(event.button.button, modState); return; +#if SDL_VERSION_ATLEAST(2, 0, 0) + case SDL_MOUSEWHEEL: + if (event.wheel.y > 0) { // Up + if (stextflag != TalkID::None) { + StoreUp(); + } else if (QuestLogIsOpen) { + QuestlogUp(); + } else if (HelpFlag) { + HelpScrollUp(); + } else if (ChatLogFlag) { + ChatLogScrollUp(); + } else if (IsStashOpen) { + Stash.PreviousPage(); + } else { + sgOptions.Keymapper.KeyPressed(MouseScrollUpButton); + } + } else if (event.wheel.y < 0) { // down + if (stextflag != TalkID::None) { + StoreDown(); + } else if (QuestLogIsOpen) { + QuestlogDown(); + } else if (HelpFlag) { + HelpScrollDown(); + } else if (ChatLogFlag) { + ChatLogScrollDown(); + } else if (IsStashOpen) { + Stash.NextPage(); + } else { + sgOptions.Keymapper.KeyPressed(MouseScrollDownButton); + } + } else if (event.wheel.x > 0) { // left + sgOptions.Keymapper.KeyPressed(MouseScrollLeftButton); + } else if (event.wheel.x < 0) { // right + sgOptions.Keymapper.KeyPressed(MouseScrollRightButton); + } + break; +#endif default: if (IsCustomEvent(event.type)) { if (gbIsMultiplayer) @@ -1577,6 +1614,33 @@ void SpellBookKeyPressed() CloseInventory(); } +void CycleSpellHotkeys(bool next) +{ + StaticVector validHotKeyIndexes; + std::optional currentIndex; + for (size_t slot = 0; slot < NumHotkeys; slot++) { + if (!IsValidSpeedSpell(slot)) + continue; + if (MyPlayer->_pRSpell == MyPlayer->_pSplHotKey[slot] && MyPlayer->_pRSplType == MyPlayer->_pSplTHotKey[slot]) { + // found current + currentIndex = validHotKeyIndexes.size(); + } + validHotKeyIndexes.emplace_back(slot); + } + if (validHotKeyIndexes.size() == 0) + return; + + size_t newIndex; + if (!currentIndex) { + newIndex = next ? 0 : (validHotKeyIndexes.size() - 1); + } else if (next) { + newIndex = (*currentIndex == validHotKeyIndexes.size() - 1) ? 0 : (*currentIndex + 1); + } else { + newIndex = *currentIndex == 0 ? (validHotKeyIndexes.size() - 1) : (*currentIndex - 1); + } + ToggleSpell(validHotKeyIndexes[newIndex]); +} + bool IsPlayerDead() { return MyPlayer->_pmode == PM_DEATH || MyPlayerIsDead; @@ -1643,6 +1707,22 @@ void InitKeymapActions() CanPlayerTakeAction, i + 1); } + sgOptions.Keymapper.AddAction( + "QuickSpellPrevious", + N_("Previous quick spell"), + N_("Selects the previous quick spell (cycles)."), + MouseScrollUpButton, + [] { CycleSpellHotkeys(false); }, + nullptr, + CanPlayerTakeAction); + sgOptions.Keymapper.AddAction( + "QuickSpellNext", + N_("Next quick spell"), + N_("Selects the next quick spell (cycles)."), + MouseScrollDownButton, + [] { CycleSpellHotkeys(true); }, + nullptr, + CanPlayerTakeAction); sgOptions.Keymapper.AddAction( "UseHealthPotion", N_("Use health potion"), diff --git a/Source/engine/events.cpp b/Source/engine/events.cpp index 2c37306a62f..7bf6051485b 100644 --- a/Source/engine/events.cpp +++ b/Source/engine/events.cpp @@ -101,6 +101,7 @@ bool FetchMessage_Real(SDL_Event *event, uint16_t *modState) case SDL_TEXTEDITING: case SDL_TEXTINPUT: case SDL_WINDOWEVENT: + case SDL_MOUSEWHEEL: #else case SDL_ACTIVEEVENT: #endif @@ -120,25 +121,6 @@ bool FetchMessage_Real(SDL_Event *event, uint16_t *modState) *event = e; break; #ifndef USE_SDL1 - case SDL_MOUSEWHEEL: -#ifdef _DEBUG - if (IsConsoleOpen()) { - *event = e; - break; - } -#endif - // This is a hack, mousewheel events should be handled directly by their consumers instead. - event->type = SDL_KEYDOWN; - if (e.wheel.y > 0) { - event->key.keysym.sym = (SDL_GetModState() & KMOD_CTRL) != 0 ? SDLK_KP_PLUS : SDLK_UP; - } else if (e.wheel.y < 0) { - event->key.keysym.sym = (SDL_GetModState() & KMOD_CTRL) != 0 ? SDLK_KP_MINUS : SDLK_DOWN; - } else if (e.wheel.x > 0) { - event->key.keysym.sym = SDLK_LEFT; - } else if (e.wheel.x < 0) { - event->key.keysym.sym = SDLK_RIGHT; - } - break; #if SDL_VERSION_ATLEAST(2, 0, 4) case SDL_AUDIODEVICEADDED: return FalseAvail("SDL_AUDIODEVICEADDED", e.adevice.which); diff --git a/Source/engine/render/scrollrt.cpp b/Source/engine/render/scrollrt.cpp index b474a923058..3cae45325fd 100644 --- a/Source/engine/render/scrollrt.cpp +++ b/Source/engine/render/scrollrt.cpp @@ -35,6 +35,7 @@ #include "options.h" #include "panels/charpanel.hpp" #include "panels/console.hpp" +#include "panels/spell_list.hpp" #include "plrmsg.h" #include "qol/chatlog.h" #include "qol/floatingnumbers.h" diff --git a/Source/options.cpp b/Source/options.cpp index 396e06bcc32..bc8b74eea7e 100644 --- a/Source/options.cpp +++ b/Source/options.cpp @@ -1326,6 +1326,10 @@ KeymapperOptions::KeymapperOptions() keyIDToKeyName.emplace(SDL_BUTTON_MIDDLE | KeymapperMouseButtonMask, "MMOUSE"); keyIDToKeyName.emplace(SDL_BUTTON_X1 | KeymapperMouseButtonMask, "X1MOUSE"); keyIDToKeyName.emplace(SDL_BUTTON_X2 | KeymapperMouseButtonMask, "X2MOUSE"); + keyIDToKeyName.emplace(MouseScrollUpButton, "SCROLlUPMOUSE"); + keyIDToKeyName.emplace(MouseScrollDownButton, "SCROLLDOWNMOUSE"); + keyIDToKeyName.emplace(MouseScrollLeftButton, "SCROLlLEFTMOUSE"); + keyIDToKeyName.emplace(MouseScrollRightButton, "SCROLLRIGHTMOUSE"); keyIDToKeyName.emplace(SDLK_BACKQUOTE, "`"); keyIDToKeyName.emplace(SDLK_LEFTBRACKET, "["); diff --git a/Source/options.h b/Source/options.h index f59afe270f8..6f265838382 100644 --- a/Source/options.h +++ b/Source/options.h @@ -642,6 +642,10 @@ struct LanguageOptions : OptionCategoryBase { }; constexpr uint32_t KeymapperMouseButtonMask = 1 << 31; +constexpr uint32_t MouseScrollUpButton = 65536 | KeymapperMouseButtonMask; +constexpr uint32_t MouseScrollDownButton = 65537 | KeymapperMouseButtonMask; +constexpr uint32_t MouseScrollLeftButton = 65538 | KeymapperMouseButtonMask; +constexpr uint32_t MouseScrollRightButton = 65539 | KeymapperMouseButtonMask; /** The Keymapper maps keys to actions. */ struct KeymapperOptions : OptionCategoryBase { diff --git a/Source/panels/spell_list.cpp b/Source/panels/spell_list.cpp index f28077349f1..26da82c5452 100644 --- a/Source/panels/spell_list.cpp +++ b/Source/panels/spell_list.cpp @@ -274,6 +274,13 @@ void SetSpeedSpell(size_t slot) return; } Player &myPlayer = *MyPlayer; + + if (myPlayer._pSplHotKey[slot] == pSpell && myPlayer._pSplTHotKey[slot] == pSplType) { + // Unset spell hotkey + myPlayer._pSplHotKey[slot] = SpellID::Invalid; + return; + } + for (size_t i = 0; i < NumHotkeys; ++i) { if (myPlayer._pSplHotKey[i] == pSpell && myPlayer._pSplTHotKey[i] == pSplType) myPlayer._pSplHotKey[i] = SpellID::Invalid; @@ -282,7 +289,7 @@ void SetSpeedSpell(size_t slot) myPlayer._pSplTHotKey[slot] = pSplType; } -void ToggleSpell(size_t slot) +bool IsValidSpeedSpell(size_t slot) { uint64_t spells; @@ -290,7 +297,7 @@ void ToggleSpell(size_t slot) const SpellID spellId = myPlayer._pSplHotKey[slot]; if (!IsValidSpell(spellId)) { - return; + return false; } switch (myPlayer._pSplTHotKey[slot]) { @@ -307,11 +314,17 @@ void ToggleSpell(size_t slot) spells = myPlayer._pISpells; break; case SpellType::Invalid: - return; + return false; } - if ((spells & GetSpellBitmask(spellId)) != 0) { - myPlayer._pRSpell = spellId; + return (spells & GetSpellBitmask(spellId)) != 0; +} + +void ToggleSpell(size_t slot) +{ + if (IsValidSpeedSpell(slot)) { + Player &myPlayer = *MyPlayer; + myPlayer._pRSpell = myPlayer._pSplHotKey[slot]; myPlayer._pRSplType = myPlayer._pSplTHotKey[slot]; RedrawEverything(); } diff --git a/Source/panels/spell_list.hpp b/Source/panels/spell_list.hpp index 9491639f6bd..8dba57c0229 100644 --- a/Source/panels/spell_list.hpp +++ b/Source/panels/spell_list.hpp @@ -16,11 +16,16 @@ struct SpellListItem { bool isSelected; }; +/** + * @brief draws the current right mouse button spell. + * @param out screen buffer representing the main UI panel + */ void DrawSpell(const Surface &out); void DrawSpellList(const Surface &out); std::vector GetSpellListItems(); void SetSpell(); void SetSpeedSpell(size_t slot); +bool IsValidSpeedSpell(size_t slot); void ToggleSpell(size_t slot); /**