From 0f46e9c4c1a02ae646dc50ecd4f2dc5020a35664 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sat, 20 Mar 2021 21:14:48 +0000 Subject: [PATCH] Thumb stick / DPad repeating navigation in DiabloUI, gmenu, and quest log (#1206) --- CMakeLists.txt | 1 + Source/gmenu.cpp | 60 ++++---- SourceX/DiabloUI/diabloui.cpp | 64 +++++---- SourceX/controls/axis_direction.cpp | 55 ++++++++ SourceX/controls/axis_direction.h | 48 +++++++ SourceX/controls/controller.h | 2 +- SourceX/controls/controller_buttons.h | 2 - SourceX/controls/controller_motion.cpp | 30 +++- SourceX/controls/controller_motion.h | 9 +- SourceX/controls/devices/game_controller.cpp | 3 + SourceX/controls/devices/joystick.cpp | 3 + SourceX/controls/game_controls.cpp | 55 ++++---- SourceX/controls/game_controls.h | 25 +--- SourceX/controls/menu_controls.cpp | 25 +++- SourceX/controls/menu_controls.h | 3 + SourceX/controls/plrctrls.cpp | 138 ++++++++++--------- 16 files changed, 348 insertions(+), 175 deletions(-) create mode 100644 SourceX/controls/axis_direction.cpp create mode 100644 SourceX/controls/axis_direction.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b8f90e036..41f372b62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -283,6 +283,7 @@ set(devilutionx_SRCS SourceX/controls/devices/game_controller.cpp SourceX/controls/devices/joystick.cpp SourceX/controls/devices/kbcontroller.cpp + SourceX/controls/axis_direction.cpp SourceX/controls/controller.cpp SourceX/controls/controller_motion.cpp SourceX/controls/game_controls.cpp diff --git a/Source/gmenu.cpp b/Source/gmenu.cpp index 8ce983277..6976da071 100644 --- a/Source/gmenu.cpp +++ b/Source/gmenu.cpp @@ -5,6 +5,9 @@ */ #include "all.h" +#include "../SourceX/controls/axis_direction.h" +#include "../SourceX/controls/controller_motion.h" + DEVILUTION_BEGIN_NAMESPACE BYTE *optbar_cel; @@ -133,6 +136,29 @@ static void gmenu_up_down(BOOL isDown) } } +static void gmenu_left_right(BOOL isRight) +{ + int step, steps; + + if (!(sgpCurrItem->dwFlags & GMENU_SLIDER)) + return; + + step = sgpCurrItem->dwFlags & 0xFFF; + steps = (int)(sgpCurrItem->dwFlags & 0xFFF000) >> 12; + if (isRight) { + if (step == steps) + return; + step++; + } else { + if (step == 0) + return; + step--; + } + sgpCurrItem->dwFlags &= 0xFFFFF000; + sgpCurrItem->dwFlags |= step; + sgpCurrItem->fnMenu(FALSE); +} + void gmenu_set_items(TMenuItem *pItem, void (*gmFunc)(TMenuItem *)) { int i; @@ -206,6 +232,16 @@ static void gmenu_draw_menu_item(CelOutputBuffer out, TMenuItem *pItem, int y) } } +static void GameMenuMove() +{ + static AxisDirectionRepeater repeater; + const AxisDirection move_dir = repeater.Get(GetLeftStickOrDpadDirection()); + if (move_dir.x != AxisDirectionX_NONE) + gmenu_left_right(move_dir.x == AxisDirectionX_RIGHT); + if (move_dir.y != AxisDirectionY_NONE) + gmenu_up_down(move_dir.y == AxisDirectionY_DOWN); +} + void gmenu_draw(CelOutputBuffer out) { int y; @@ -213,6 +249,7 @@ void gmenu_draw(CelOutputBuffer out) DWORD ticks; if (sgpCurrentMenu) { + GameMenuMove(); if (gmenu_current_option) gmenu_current_option(sgpCurrentMenu); if (gbIsHellfire) { @@ -239,29 +276,6 @@ void gmenu_draw(CelOutputBuffer out) } } -static void gmenu_left_right(BOOL isRight) -{ - int step, steps; - - if (!(sgpCurrItem->dwFlags & GMENU_SLIDER)) - return; - - step = sgpCurrItem->dwFlags & 0xFFF; - steps = (int)(sgpCurrItem->dwFlags & 0xFFF000) >> 12; - if (isRight) { - if (step == steps) - return; - step++; - } else { - if (step == 0) - return; - step--; - } - sgpCurrItem->dwFlags &= 0xFFFFF000; - sgpCurrItem->dwFlags |= step; - sgpCurrItem->fnMenu(FALSE); -} - BOOL gmenu_presskeys(int vkey) { if (!sgpCurrentMenu) diff --git a/SourceX/DiabloUI/diabloui.cpp b/SourceX/DiabloUI/diabloui.cpp index f4d5ad2d0..61fd7c867 100644 --- a/SourceX/DiabloUI/diabloui.cpp +++ b/SourceX/DiabloUI/diabloui.cpp @@ -145,6 +145,8 @@ void UiPlaySelectSound() gfnSoundFunction("sfx\\items\\titlslct.wav"); } +namespace { + void UiFocus(std::size_t itemIndex) { if (SelectedItem == itemIndex) @@ -214,6 +216,39 @@ void selhero_CatToName(char *in_buf, char *out_buf, int cnt) strncat(out_buf, output.c_str(), cnt - strlen(out_buf)); } +bool HandleMenuAction(MenuAction menu_action) +{ + switch (menu_action) { + case MenuAction_SELECT: + UiFocusNavigationSelect(); + return true; + case MenuAction_UP: + UiFocusUp(); + return true; + case MenuAction_DOWN: + UiFocusDown(); + return true; + case MenuAction_PAGE_UP: + UiFocusPageUp(); + return true; + case MenuAction_PAGE_DOWN: + UiFocusPageDown(); + return true; + case MenuAction_DELETE: + UiFocusNavigationYesNo(); + return true; + case MenuAction_BACK: + if (!gfnListEsc) + return false; + UiFocusNavigationEsc(); + return true; + default: + return false; + } +} + +} // namespace + void UiFocusNavigation(SDL_Event *event) { switch (event->type) { @@ -239,33 +274,7 @@ void UiFocusNavigation(SDL_Event *event) break; } - switch (GetMenuAction(*event)) { - case MenuAction_SELECT: - UiFocusNavigationSelect(); - return; - case MenuAction_UP: - UiFocusUp(); - return; - case MenuAction_DOWN: - UiFocusDown(); - return; - case MenuAction_PAGE_UP: - UiFocusPageUp(); - return; - case MenuAction_PAGE_DOWN: - UiFocusPageDown(); - return; - case MenuAction_DELETE: - UiFocusNavigationYesNo(); - return; - case MenuAction_BACK: - if (!gfnListEsc) - break; - UiFocusNavigationEsc(); - return; - default: - break; - } + if (HandleMenuAction(GetMenuAction(*event))) return; #ifndef USE_SDL1 if (event->type == SDL_MOUSEWHEEL) { @@ -617,6 +626,7 @@ void UiPollAndRender() UiFocusNavigation(&event); UiHandleEvents(&event); } + HandleMenuAction(GetMenuHeldUpDownAction()); UiRenderItems(gUiItems); DrawMouse(); UiFadeIn(); diff --git a/SourceX/controls/axis_direction.cpp b/SourceX/controls/axis_direction.cpp new file mode 100644 index 000000000..55cafb420 --- /dev/null +++ b/SourceX/controls/axis_direction.cpp @@ -0,0 +1,55 @@ +#include "axis_direction.h" + +#include + +namespace dvl { + +AxisDirection AxisDirectionRepeater::Get(AxisDirection axis_direction) +{ + const int now = SDL_GetTicks(); + switch (axis_direction.x) { + case AxisDirectionX_LEFT: + last_right_ = 0; + if (now - last_left_ < min_interval_ms_) { + axis_direction.x = AxisDirectionX_NONE; + } else { + last_left_ = now; + } + break; + case AxisDirectionX_RIGHT: + last_left_ = 0; + if (now - last_right_ < min_interval_ms_) { + axis_direction.x = AxisDirectionX_NONE; + } else { + last_right_ = now; + } + break; + case AxisDirectionX_NONE: + last_left_ = last_right_ = 0; + break; + } + switch (axis_direction.y) { + case AxisDirectionY_UP: + last_down_ = 0; + if (now - last_up_ < min_interval_ms_) { + axis_direction.y = AxisDirectionY_NONE; + } else { + last_up_ = now; + } + break; + case AxisDirectionY_DOWN: + last_up_ = 0; + if (now - last_down_ < min_interval_ms_) { + axis_direction.y = AxisDirectionY_NONE; + } else { + last_down_ = now; + } + break; + case AxisDirectionY_NONE: + last_up_ = last_down_ = 0; + break; + } + return axis_direction; +} + +} // namespace diff --git a/SourceX/controls/axis_direction.h b/SourceX/controls/axis_direction.h new file mode 100644 index 000000000..861513beb --- /dev/null +++ b/SourceX/controls/axis_direction.h @@ -0,0 +1,48 @@ +#pragma once + +namespace dvl { + +enum AxisDirectionX { + AxisDirectionX_NONE = 0, + AxisDirectionX_LEFT, + AxisDirectionX_RIGHT +}; +enum AxisDirectionY { + AxisDirectionY_NONE = 0, + AxisDirectionY_UP, + AxisDirectionY_DOWN +}; + +/** + * @brief 8-way direction of a D-Pad or a thumb stick. + */ +struct AxisDirection { + AxisDirectionX x; + AxisDirectionY y; +}; + +/** + * @brief Returns a non-empty AxisDirection at most once per the given time interval. + */ +class AxisDirectionRepeater { +public: + AxisDirectionRepeater(int min_interval_ms = 200) + : last_left_(0) + , last_right_(0) + , last_up_(0) + , last_down_(0) + , min_interval_ms_(min_interval_ms) + { + } + + AxisDirection Get(AxisDirection axis_direction); + +private: + int last_left_; + int last_right_; + int last_up_; + int last_down_; + int min_interval_ms_; +}; + +} // namespace dvl diff --git a/SourceX/controls/controller.h b/SourceX/controls/controller.h index 7b62f6aab..acf443f56 100644 --- a/SourceX/controls/controller.h +++ b/SourceX/controls/controller.h @@ -2,7 +2,7 @@ #include -#include "controls/controller_buttons.h" +#include "./controller_buttons.h" namespace dvl { diff --git a/SourceX/controls/controller_buttons.h b/SourceX/controls/controller_buttons.h index fe4c95a83..ed4363ee0 100644 --- a/SourceX/controls/controller_buttons.h +++ b/SourceX/controls/controller_buttons.h @@ -1,8 +1,6 @@ #pragma once // Unifies joystick, gamepad, and keyboard controller APIs. -#include "all.h" - namespace dvl { // NOTE: A, B, X, Y refer to physical positions on an XBox 360 controller. diff --git a/SourceX/controls/controller_motion.cpp b/SourceX/controls/controller_motion.cpp index cee604feb..0b27c3f60 100644 --- a/SourceX/controls/controller_motion.cpp +++ b/SourceX/controls/controller_motion.cpp @@ -1,5 +1,7 @@ #include "controls/controller_motion.h" +#include + #include "controls/devices/game_controller.h" #include "controls/devices/joystick.h" #include "controls/devices/kbcontroller.h" @@ -30,7 +32,7 @@ void ScaleJoystickAxes(float *x, float *y, float deadzone) float analog_y = *y; float dead_zone = deadzone * maximum; - float magnitude = sqrtf(analog_x * analog_x + analog_y * analog_y); + float magnitude = std::sqrt(analog_x * analog_x + analog_y * analog_y); if (magnitude >= dead_zone) { // find scaled axis values with magnitudes between zero and maximum float scalingFactor = 1.0 / magnitude * (magnitude - dead_zone) / (maximum - dead_zone); @@ -39,8 +41,8 @@ void ScaleJoystickAxes(float *x, float *y, float deadzone) // clamp to ensure results will never exceed the max_axis value float clamping_factor = 1.0f; - float abs_analog_x = fabs(analog_x); - float abs_analog_y = fabs(analog_y); + float abs_analog_x = std::fabs(analog_x); + float abs_analog_y = std::fabs(analog_y); if (abs_analog_x > 1.0 || abs_analog_y > 1.0) { if (abs_analog_x > abs_analog_y) { clamping_factor = 1 / abs_analog_x; @@ -144,4 +146,26 @@ bool ProcessControllerMotion(const SDL_Event &event, ControllerButtonEvent ctrl_ return SimulateRightStickWithDpad(event, ctrl_event); } +AxisDirection GetLeftStickOrDpadDirection(bool allow_dpad) +{ + const float stickX = leftStickX; + const float stickY = leftStickY; + + AxisDirection result { AxisDirectionX_NONE, AxisDirectionY_NONE }; + + if (stickY >= 0.5 || (allow_dpad && IsControllerButtonPressed(ControllerButton_BUTTON_DPAD_UP))) { + result.y = AxisDirectionY_UP; + } else if (stickY <= -0.5 || (allow_dpad && IsControllerButtonPressed(ControllerButton_BUTTON_DPAD_DOWN))) { + result.y = AxisDirectionY_DOWN; + } + + if (stickX <= -0.5 || (allow_dpad && IsControllerButtonPressed(ControllerButton_BUTTON_DPAD_LEFT))) { + result.x = AxisDirectionX_LEFT; + } else if (stickX >= 0.5 || (allow_dpad && IsControllerButtonPressed(ControllerButton_BUTTON_DPAD_RIGHT))) { + result.x = AxisDirectionX_RIGHT; + } + + return result; +} + } // namespace dvl diff --git a/SourceX/controls/controller_motion.h b/SourceX/controls/controller_motion.h index b705e0857..482fc9a82 100644 --- a/SourceX/controls/controller_motion.h +++ b/SourceX/controls/controller_motion.h @@ -2,8 +2,10 @@ // Processes and stores mouse and joystick motion. -#include "all.h" -#include "controls/controller.h" +#include + +#include "./axis_direction.h" +#include "./controller.h" namespace dvl { @@ -19,4 +21,7 @@ extern bool leftStickNeedsScaling, rightStickNeedsScaling; // Updates motion state for mouse and joystick sticks. bool ProcessControllerMotion(const SDL_Event &event, ControllerButtonEvent ctrl_event); +// Returns direction of the left thumb stick or DPad (if allow_dpad = true). +AxisDirection GetLeftStickOrDpadDirection(bool allow_dpad = true); + } // namespace dvl diff --git a/SourceX/controls/devices/game_controller.cpp b/SourceX/controls/devices/game_controller.cpp index 7822db172..37c21a0ec 100644 --- a/SourceX/controls/devices/game_controller.cpp +++ b/SourceX/controls/devices/game_controller.cpp @@ -8,6 +8,9 @@ #include "controls/devices/joystick.h" #include "stubs.h" +// Defined in SourceX/controls/plctrls.cpp +extern "C" bool sgbControllerActive; + namespace dvl { std::vector *const GameController::controllers_ = new std::vector; diff --git a/SourceX/controls/devices/joystick.cpp b/SourceX/controls/devices/joystick.cpp index f1bebf948..fe2f63d93 100644 --- a/SourceX/controls/devices/joystick.cpp +++ b/SourceX/controls/devices/joystick.cpp @@ -6,6 +6,9 @@ #include "controls/controller_motion.h" #include "stubs.h" +// Defined in SourceX/controls/plctrls.cpp +extern "C" bool sgbControllerActive; + namespace dvl { std::vector *const Joystick::joysticks_ = new std::vector; diff --git a/SourceX/controls/game_controls.cpp b/SourceX/controls/game_controls.cpp index da83f20c1..11e68a035 100644 --- a/SourceX/controls/game_controls.cpp +++ b/SourceX/controls/game_controls.cpp @@ -14,10 +14,10 @@ namespace dvl { bool start_modifier_active = false; bool select_modifier_active = false; - -// gamepad dpad acts as hotkeys without holding "start" + +/** Gamepad dpad acts as hotkeys without holding "start" */ bool dpad_hotkeys = false; -// shoulder gamepad buttons act as potions by default +/** Shoulder gamepad buttons act as potions by default */ bool switch_potions_and_clicks = false; namespace { @@ -152,33 +152,33 @@ bool GetGameAction(const SDL_Event &event, ControllerButtonEvent ctrl_event, Gam case ControllerButton_BUTTON_DPAD_UP: if (IsControllerButtonPressed(ControllerButton_BUTTON_BACK)) *action = GameActionSendKey{ DVL_VK_F6, ctrl_event.up }; - else + else *action = GameActionSendKey{ DVL_VK_ESCAPE, ctrl_event.up }; return true; case ControllerButton_BUTTON_DPAD_RIGHT: if (IsControllerButtonPressed(ControllerButton_BUTTON_BACK)) *action = GameActionSendKey{ DVL_VK_F8, ctrl_event.up }; - else + else if (!ctrl_event.up) *action = GameAction(GameActionType_TOGGLE_INVENTORY); return true; case ControllerButton_BUTTON_DPAD_DOWN: if (IsControllerButtonPressed(ControllerButton_BUTTON_BACK)) *action = GameActionSendKey{ DVL_VK_F7, ctrl_event.up }; - else + else *action = GameActionSendKey{ DVL_VK_TAB, ctrl_event.up }; return true; case ControllerButton_BUTTON_DPAD_LEFT: if (IsControllerButtonPressed(ControllerButton_BUTTON_BACK)) *action = GameActionSendKey{ DVL_VK_F5, ctrl_event.up }; - else + else if (!ctrl_event.up) *action = GameAction(GameActionType_TOGGLE_CHARACTER_INFO); return true; - default: - break; + default: + break; } - } + } if (start_modifier_active) { switch (ctrl_event.button) { case ControllerButton_BUTTON_DPAD_UP: @@ -308,6 +308,20 @@ bool GetGameAction(const SDL_Event &event, ControllerButtonEvent ctrl_event, Gam } + // DPad navigation is handled separately for these. + if (gmenu_is_active() || questlog) + { + switch (ctrl_event.button) { + case ControllerButton_BUTTON_DPAD_UP: + case ControllerButton_BUTTON_DPAD_DOWN: + case ControllerButton_BUTTON_DPAD_LEFT: + case ControllerButton_BUTTON_DPAD_RIGHT: + return true; + default: + break; + } + } + // By default, map to a keyboard key. if (ctrl_event.button != ControllerButton_NONE) { *action = GameActionSendKey{ translate_controller_button_to_key(ctrl_event.button), @@ -330,26 +344,9 @@ bool GetGameAction(const SDL_Event &event, ControllerButtonEvent ctrl_event, Gam return false; } -MoveDirection GetMoveDirection() +AxisDirection GetMoveDirection() { - const float stickX = leftStickX; - const float stickY = leftStickY; - - MoveDirection result{ MoveDirectionX_NONE, MoveDirectionY_NONE }; - - if (stickY >= 0.5 || (!dpad_hotkeys && IsControllerButtonPressed(ControllerButton_BUTTON_DPAD_UP))) { - result.y = MoveDirectionY_UP; - } else if (stickY <= -0.5 || (!dpad_hotkeys && IsControllerButtonPressed(ControllerButton_BUTTON_DPAD_DOWN))) { - result.y = MoveDirectionY_DOWN; - } - - if (stickX <= -0.5 || (!dpad_hotkeys && IsControllerButtonPressed(ControllerButton_BUTTON_DPAD_LEFT))) { - result.x = MoveDirectionX_LEFT; - } else if (stickX >= 0.5 || (!dpad_hotkeys && IsControllerButtonPressed(ControllerButton_BUTTON_DPAD_RIGHT))) { - result.x = MoveDirectionX_RIGHT; - } - - return result; + return GetLeftStickOrDpadDirection(/*allow_dpad=*/!dpad_hotkeys); } } // namespace dvl diff --git a/SourceX/controls/game_controls.h b/SourceX/controls/game_controls.h index 7b6551767..6f07f0909 100644 --- a/SourceX/controls/game_controls.h +++ b/SourceX/controls/game_controls.h @@ -1,7 +1,10 @@ #pragma once -#include "all.h" -#include "controls/controller.h" +#include +#include + +#include "./axis_direction.h" +#include "./controller.h" namespace dvl { @@ -22,7 +25,7 @@ enum GameActionType { }; struct GameActionSendKey { - DWORD vk_code; + Uint32 vk_code; bool up; }; @@ -68,21 +71,7 @@ struct GameAction { bool GetGameAction(const SDL_Event &event, ControllerButtonEvent ctrl_event, GameAction *action); -enum MoveDirectionX { - MoveDirectionX_NONE = 0, - MoveDirectionX_LEFT, - MoveDirectionX_RIGHT -}; -enum MoveDirectionY { - MoveDirectionY_NONE = 0, - MoveDirectionY_UP, - MoveDirectionY_DOWN -}; -struct MoveDirection { - MoveDirectionX x; - MoveDirectionY y; -}; -MoveDirection GetMoveDirection(); +AxisDirection GetMoveDirection(); extern bool start_modifier_active; extern bool select_modifier_active; diff --git a/SourceX/controls/menu_controls.cpp b/SourceX/controls/menu_controls.cpp index cef39a6ab..026d5367a 100644 --- a/SourceX/controls/menu_controls.cpp +++ b/SourceX/controls/menu_controls.cpp @@ -1,14 +1,36 @@ #include "controls/menu_controls.h" +#include "controls/axis_direction.h" #include "controls/controller.h" +#include "controls/controller_motion.h" #include "controls/remap_keyboard.h" #include "DiabloUI/diabloui.h" namespace dvl { +MenuAction GetMenuHeldUpDownAction() +{ + static AxisDirectionRepeater repeater; + const AxisDirection dir = repeater.Get(GetLeftStickOrDpadDirection()); + switch (dir.y) { + case AxisDirectionY_UP: + return MenuAction_UP; + case AxisDirectionY_DOWN: + return MenuAction_DOWN; + default: + return MenuAction_NONE; + } +} + MenuAction GetMenuAction(const SDL_Event &event) { const ControllerButtonEvent ctrl_event = ToControllerButtonEvent(event); + + if (ProcessControllerMotion(event, ctrl_event)) { + sgbControllerActive = true; + return GetMenuHeldUpDownAction(); + } + if (ctrl_event.button != ControllerButton_NONE) sgbControllerActive = true; @@ -25,9 +47,8 @@ MenuAction GetMenuAction(const SDL_Event &event) case ControllerButton_BUTTON_X: // Left button return MenuAction_DELETE; case ControllerButton_BUTTON_DPAD_UP: - return MenuAction_UP; case ControllerButton_BUTTON_DPAD_DOWN: - return MenuAction_DOWN; + return GetMenuHeldUpDownAction(); case ControllerButton_BUTTON_DPAD_LEFT: return MenuAction_LEFT; case ControllerButton_BUTTON_DPAD_RIGHT: diff --git a/SourceX/controls/menu_controls.h b/SourceX/controls/menu_controls.h index 9c911b58c..08be9d519 100644 --- a/SourceX/controls/menu_controls.h +++ b/SourceX/controls/menu_controls.h @@ -21,4 +21,7 @@ enum MenuAction { MenuAction GetMenuAction(const SDL_Event &event); +/** Menu action from holding the left stick or DPad. */ +MenuAction GetMenuHeldUpDownAction(); + } // namespace dvl diff --git a/SourceX/controls/plrctrls.cpp b/SourceX/controls/plrctrls.cpp index b42ae5aa6..b23bf9119 100644 --- a/SourceX/controls/plrctrls.cpp +++ b/SourceX/controls/plrctrls.cpp @@ -14,7 +14,6 @@ namespace dvl { bool sgbControllerActive = false; coords speedspellscoords[50]; -const int repeatRate = 100; int speedspellcount = 0; /** @@ -33,7 +32,6 @@ bool InGameMenu() namespace { -DWORD invmove = 0; int slot = SLOTXY_INV_FIRST; /** @@ -446,22 +444,16 @@ void Interact() } } -void AttrIncBtnSnap(MoveDirectionY dir) +void AttrIncBtnSnap(AxisDirection dir) { - if (dir == MoveDirectionY_NONE) { - invmove = 0; + static AxisDirectionRepeater repeater; + dir = repeater.Get(dir); + if (dir.y == AxisDirectionY_NONE) return; - } if (chrbtnactive && plr[myplr]._pStatPts <= 0) return; - DWORD ticks = SDL_GetTicks(); - if (ticks - invmove < repeatRate) { - return; - } - invmove = ticks; - // first, find our cursor location int slot = 0; for (int i = 0; i < 4; i++) { @@ -474,10 +466,10 @@ void AttrIncBtnSnap(MoveDirectionY dir) } } - if (dir == MoveDirectionY_UP) { + if (dir.y == AxisDirectionY_UP) { if (slot > 0) --slot; - } else if (dir == MoveDirectionY_DOWN) { + } else if (dir.y == AxisDirectionY_DOWN) { if (slot < 3) ++slot; } @@ -493,18 +485,13 @@ void AttrIncBtnSnap(MoveDirectionY dir) * If mouse coords are at SLOTXY_CHEST_LAST, consider this center of equipment * small inventory squares are 29x29 (roughly) */ -void InvMove(MoveDirection dir) +void InvMove(AxisDirection dir) { - if (dir.x == MoveDirectionX_NONE && dir.y == MoveDirectionY_NONE) { - invmove = 0; + static AxisDirectionRepeater repeater(/*min_interval_ms=*/100); + dir = repeater.Get(dir); + if (dir.x == AxisDirectionX_NONE && dir.y == AxisDirectionY_NONE) return; - } - DWORD ticks = SDL_GetTicks(); - if (ticks - invmove < repeatRate) { - return; - } - invmove = ticks; int x = MouseX; int y = MouseY; @@ -529,7 +516,7 @@ void InvMove(MoveDirection dir) slot = SLOTXY_BELT_LAST; // when item is on cursor, this is the real cursor XY - if (dir.x == MoveDirectionX_LEFT) { + if (dir.x == AxisDirectionX_LEFT) { if (slot >= SLOTXY_HAND_RIGHT_FIRST && slot <= SLOTXY_HAND_RIGHT_LAST) { x = InvRect[SLOTXY_CHEST_FIRST].X + RIGHT_PANEL + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_CHEST_FIRST].Y - (INV_SLOT_SIZE_PX / 2); @@ -561,7 +548,7 @@ void InvMove(MoveDirection dir) x = InvRect[slot].X + PANEL_LEFT + (INV_SLOT_SIZE_PX / 2); y = InvRect[slot].Y + PANEL_TOP - (INV_SLOT_SIZE_PX / 2); } - } else if (dir.x == MoveDirectionX_RIGHT) { + } else if (dir.x == AxisDirectionX_RIGHT) { if (slot == SLOTXY_RING_LEFT) { x = InvRect[SLOTXY_RING_RIGHT].X + RIGHT_PANEL + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_RING_RIGHT].Y - (INV_SLOT_SIZE_PX / 2); @@ -592,7 +579,7 @@ void InvMove(MoveDirection dir) y = InvRect[slot].Y + PANEL_TOP - (INV_SLOT_SIZE_PX / 2); } } - if (dir.y == MoveDirectionY_UP) { + if (dir.y == AxisDirectionY_UP) { if (slot > 24 && slot <= 27) { // first 3 general slots x = InvRect[SLOTXY_RING_LEFT].X + RIGHT_PANEL + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_RING_LEFT].Y - (INV_SLOT_SIZE_PX / 2); @@ -626,7 +613,7 @@ void InvMove(MoveDirection dir) x = InvRect[slot].X + RIGHT_PANEL + (INV_SLOT_SIZE_PX / 2); y = InvRect[slot].Y - (INV_SLOT_SIZE_PX / 2); } - } else if (dir.y == MoveDirectionY_DOWN) { + } else if (dir.y == AxisDirectionY_DOWN) { if (slot >= SLOTXY_HEAD_FIRST && slot <= SLOTXY_HEAD_LAST) { x = InvRect[SLOTXY_CHEST_FIRST].X + RIGHT_PANEL + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_CHEST_FIRST].Y - (INV_SLOT_SIZE_PX / 2); @@ -688,18 +675,12 @@ bool HSExists(int x, int y) return false; } -void HotSpellMove(MoveDirection dir) +void HotSpellMove(AxisDirection dir) { - if (dir.x == MoveDirectionX_NONE && dir.y == MoveDirectionY_NONE) { - invmove = 0; - return; - } - - DWORD ticks = SDL_GetTicks(); - if (ticks - invmove < repeatRate) { + static AxisDirectionRepeater repeater; + dir = repeater.Get(dir); + if (dir.x == AxisDirectionX_NONE && dir.y == AxisDirectionY_NONE) return; - } - invmove = ticks; int spbslot = plr[myplr]._pRSpell; for (int r = 0; r < speedspellcount; r++) { @@ -715,23 +696,23 @@ void HotSpellMove(MoveDirection dir) int x = speedspellscoords[spbslot].x; int y = speedspellscoords[spbslot].y; - if (dir.x == MoveDirectionX_LEFT) { + if (dir.x == AxisDirectionX_LEFT) { if (spbslot < speedspellcount - 1) { x = speedspellscoords[spbslot + 1].x; y = speedspellscoords[spbslot + 1].y; } - } else if (dir.x == MoveDirectionX_RIGHT) { + } else if (dir.x == AxisDirectionX_RIGHT) { if (spbslot > 0) { x = speedspellscoords[spbslot - 1].x; y = speedspellscoords[spbslot - 1].y; } } - if (dir.y == MoveDirectionY_UP) { + if (dir.y == AxisDirectionY_UP) { if (HSExists(x, y - SPLICONLENGTH)) { y -= SPLICONLENGTH; } - } else if (dir.y == MoveDirectionY_DOWN) { + } else if (dir.y == AxisDirectionY_DOWN) { if (HSExists(x, y + SPLICONLENGTH)) { y += SPLICONLENGTH; } @@ -742,23 +723,15 @@ void HotSpellMove(MoveDirection dir) } } -void SpellBookMove(MoveDirection dir) +void SpellBookMove(AxisDirection dir) { - if (dir.x == MoveDirectionX_NONE && dir.y == MoveDirectionY_NONE) { - invmove = 0; - return; - } + static AxisDirectionRepeater repeater; + dir = repeater.Get(dir); - DWORD ticks = SDL_GetTicks(); - if (ticks - invmove < repeatRate) { - return; - } - invmove = ticks; - - if (dir.x == MoveDirectionX_LEFT) { + if (dir.x == AxisDirectionX_LEFT) { if (sbooktab > 0) sbooktab--; - } else if (dir.x == MoveDirectionX_RIGHT) { + } else if (dir.x == AxisDirectionX_RIGHT) { if ((gbIsHellfire && sbooktab < 4) || (!gbIsHellfire && sbooktab < 3)) sbooktab++; } @@ -827,12 +800,12 @@ bool IsPathBlocked(int x, int y, int dir) return !PosOkPlayer(myplr, d1x, d1y) && !PosOkPlayer(myplr, d2x, d2y); } -void WalkInDir(MoveDirection dir) +void WalkInDir(AxisDirection dir) { const int x = plr[myplr]._pfutx; const int y = plr[myplr]._pfuty; - if (dir.x == MoveDirectionX_NONE && dir.y == MoveDirectionY_NONE) { + if (dir.x == AxisDirectionX_NONE && dir.y == AxisDirectionY_NONE) { if (sgbControllerActive && plr[myplr].walkpath[0] != WALK_NONE && plr[myplr].destAction == ACTION_NONE) NetSendCmdLoc(true, CMD_WALKXY, x, y); // Stop walking return; @@ -849,27 +822,56 @@ void WalkInDir(MoveDirection dir) NetSendCmdLoc(true, CMD_WALKXY, dx, dy); } +void QuestLogMove(AxisDirection move_dir) +{ + static AxisDirectionRepeater repeater; + move_dir = repeater.Get(move_dir); + if (move_dir.y == AxisDirectionY_UP) + QuestlogUp(); + else if (move_dir.y == AxisDirectionY_DOWN) + QuestlogDown(); +} + +typedef void (*HandleLeftStickOrDPadFn)(dvl::AxisDirection); + +HandleLeftStickOrDPadFn GetLeftStickOrDPadGameUIHandler() +{ + if (invflag) { + return &InvMove; + } else if (chrflag && plr[myplr]._pStatPts > 0) { + return &AttrIncBtnSnap; + } else if (spselflag) { + return &HotSpellMove; + } else if (sbookflag) { + return &SpellBookMove; + } else if (questlog) { + return &QuestLogMove; + } + return NULL; +} + +void ProcessLeftStickOrDPadGameUI() { + HandleLeftStickOrDPadFn handler = GetLeftStickOrDPadGameUIHandler(); + if (handler != NULL) + handler(GetLeftStickOrDpadDirection(true)); +} + void Movement() { - if (InGameMenu() || questlog + if (InGameMenu() || IsControllerButtonPressed(ControllerButton_BUTTON_START) || IsControllerButtonPressed(ControllerButton_BUTTON_BACK)) return; - MoveDirection move_dir = GetMoveDirection(); - if (move_dir.x != MoveDirectionX_NONE || move_dir.y != MoveDirectionY_NONE) { + AxisDirection move_dir = GetMoveDirection(); + if (move_dir.x != AxisDirectionX_NONE || move_dir.y != AxisDirectionY_NONE) { sgbControllerActive = true; } - if (invflag) { - InvMove(move_dir); - } else if (chrflag && plr[myplr]._pStatPts > 0) { - AttrIncBtnSnap(move_dir.y); - } else if (spselflag) { - HotSpellMove(move_dir); - } else if (sbookflag) { - SpellBookMove(move_dir); - } else { + // TODO: This should be called independently of game logic / tick rate. + ProcessLeftStickOrDPadGameUI(); + + if (GetLeftStickOrDPadGameUIHandler() == NULL) { WalkInDir(move_dir); } }