From 889cc04f95f8ee1289201c8dec288d7f4a2f5ea9 Mon Sep 17 00:00:00 2001 From: Felipe Wannmacher Date: Mon, 4 Apr 2022 13:19:16 +0100 Subject: [PATCH] Enhanced attack using controllers (#4019) --- Source/controls/controller.cpp | 91 ++++++++++++++++++++- Source/controls/controller.h | 11 +++ Source/controls/devices/game_controller.cpp | 10 ++- Source/controls/devices/game_controller.h | 5 +- Source/controls/devices/joystick.h | 2 +- Source/controls/game_controls.cpp | 18 ++++ Source/controls/game_controls.h | 2 + Source/controls/input.h | 7 +- Source/controls/plrctrls.cpp | 10 +++ Source/controls/plrctrls.h | 2 + 10 files changed, 148 insertions(+), 10 deletions(-) diff --git a/Source/controls/controller.cpp b/Source/controls/controller.cpp index 960eea77f..0d84d3243 100644 --- a/Source/controls/controller.cpp +++ b/Source/controls/controller.cpp @@ -7,9 +7,54 @@ #endif #include "controls/devices/joystick.h" #include "controls/devices/kbcontroller.h" +#include "controls/game_controls.h" +#include "controls/plrctrls.h" +#include "player.h" namespace devilution { +namespace { + +bool skipTick = false; +bool heldControllerButtonEventsLocked = false; +ControllerButton actionButtons[2] = { ControllerButton_ATTACK, ControllerButton_CAST_SPELL }; + +bool CreateActionButtonEvent(SDL_Event *event, ControllerButton button) +{ + SDL_JoystickID which; + +#ifndef USE_SDL1 + if (GameController::IsPressedOnAnyController(button, &which)) { + event->type = SDL_CONTROLLERBUTTONDOWN; + event->cbutton.button = GameController::ToSdlGameControllerButton(button); + event->cbutton.state = ControllerButtonState_HELD; + event->cbutton.which = which; + + return true; + } +#endif +#if HAS_KBCTRL == 1 + if (IsKbCtrlButtonPressed(button)) { + event->type = SDL_KEYDOWN; + event->key.keysym.sym = ControllerButtonToKbCtrlKeyCode(button); + event->key.state = ControllerButtonState_HELD; + + return true; + } +#endif + if (Joystick::IsPressedOnAnyJoystick(button)) { + event->type = SDL_JOYBUTTONDOWN; + event->jbutton.button = Joystick::ToSdlJoyButton(button); + event->jbutton.state = ControllerButtonState_HELD; + + return true; + } + + return false; +} + +} // namespace + void UnlockControllerState(const SDL_Event &event) { #ifndef USE_SDL1 @@ -22,7 +67,7 @@ void UnlockControllerState(const SDL_Event &event) ControllerButtonEvent ToControllerButtonEvent(const SDL_Event &event) { - ControllerButtonEvent result { ControllerButton_NONE, false }; + ControllerButtonEvent result { ControllerButton_NONE, false, ControllerButtonState_RELEASED }; switch (event.type) { #ifndef USE_SDL1 case SDL_CONTROLLERBUTTONUP: @@ -36,6 +81,8 @@ ControllerButtonEvent ToControllerButtonEvent(const SDL_Event &event) } #if HAS_KBCTRL == 1 result.button = KbCtrlToControllerButton(event); + result.state = event.key.state; + if (result.button != ControllerButton_NONE) return result; #endif @@ -43,14 +90,18 @@ ControllerButtonEvent ToControllerButtonEvent(const SDL_Event &event) GameController *const controller = GameController::Get(event); if (controller != nullptr) { result.button = controller->ToControllerButton(event); + result.state = event.cbutton.state; + if (result.button != ControllerButton_NONE) return result; } #endif const Joystick *joystick = Joystick::Get(event); - if (joystick != nullptr) + if (joystick != nullptr) { result.button = devilution::Joystick::ToControllerButton(event); + result.state = event.jbutton.state; + } return result; } @@ -93,4 +144,40 @@ bool HandleControllerAddedOrRemovedEvent(const SDL_Event &event) #endif } +void UnlockHeldControllerButtonEvents(const SDL_Event &event) +{ + ControllerButtonEvent ctrlEvent = ToControllerButtonEvent(event); + + for (int i = 0; i < 2; ++i) + if (actionButtons[i] == ctrlEvent.button) { + heldControllerButtonEventsLocked = !CanControlHero(); + + break; + } +} + +int PollActionButtonPressed(SDL_Event *event) +{ + if (heldControllerButtonEventsLocked) + return 0; + + if (skipTick) { + skipTick = false; + + return 0; + } + + if (!Players[MyPlayerId].CanChangeAction()) + return 0; + + for (int i = 0; i < 2; ++i) + if (CreateActionButtonEvent(event, actionButtons[i])) { + skipTick = true; // 1 tick is skipped for allowing the player animation to change + + return 1; + } + + return 0; +} + } // namespace devilution diff --git a/Source/controls/controller.h b/Source/controls/controller.h index 8186a16fe..35b642fab 100644 --- a/Source/controls/controller.h +++ b/Source/controls/controller.h @@ -6,9 +6,16 @@ namespace devilution { +enum ControllerButtonState : uint8_t { + ControllerButtonState_RELEASED = SDL_RELEASED, + ControllerButtonState_PRESSED = SDL_PRESSED, + ControllerButtonState_HELD = SDL_PRESSED + SDL_RELEASED + 1 // for making it sure that the value is different +}; + struct ControllerButtonEvent { ControllerButton button; bool up; + uint8_t state; }; // Must be called exactly once at the start of each SDL input event. @@ -20,4 +27,8 @@ bool IsControllerButtonPressed(ControllerButton button); bool HandleControllerAddedOrRemovedEvent(const SDL_Event &event); +void UnlockHeldControllerButtonEvents(const SDL_Event &event); + +int PollActionButtonPressed(SDL_Event *event); + } // namespace devilution diff --git a/Source/controls/devices/game_controller.cpp b/Source/controls/devices/game_controller.cpp index 23d3bcedb..6b0499816 100644 --- a/Source/controls/devices/game_controller.cpp +++ b/Source/controls/devices/game_controller.cpp @@ -205,7 +205,7 @@ GameController *GameController::Get(const SDL_Event &event) return Get(event.caxis.which); case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONUP: - return Get(event.jball.which); + return Get(event.cbutton.which); default: return nullptr; } @@ -216,11 +216,15 @@ const std::vector &GameController::All() return controllers_; } -bool GameController::IsPressedOnAnyController(ControllerButton button) +bool GameController::IsPressedOnAnyController(ControllerButton button, SDL_JoystickID *which) { for (auto &controller : controllers_) - if (controller.IsPressed(button)) + if (controller.IsPressed(button)) { + if (which != nullptr) + *which = controller.instance_id_; + return true; + } return false; } diff --git a/Source/controls/devices/game_controller.h b/Source/controls/devices/game_controller.h index 314dea024..3c2a37941 100644 --- a/Source/controls/devices/game_controller.h +++ b/Source/controls/devices/game_controller.h @@ -17,7 +17,7 @@ public: static GameController *Get(SDL_JoystickID instanceId); static GameController *Get(const SDL_Event &event); static const std::vector &All(); - static bool IsPressedOnAnyController(ControllerButton button); + static bool IsPressedOnAnyController(ControllerButton button, SDL_JoystickID *which = nullptr); // Must be called exactly once at the start of each SDL input event. void UnlockTriggerState(); @@ -26,10 +26,9 @@ public: bool IsPressed(ControllerButton button) const; static bool ProcessAxisMotion(const SDL_Event &event); - -private: static SDL_GameControllerButton ToSdlGameControllerButton(ControllerButton button); +private: SDL_GameController *sdl_game_controller_ = NULL; SDL_JoystickID instance_id_ = -1; diff --git a/Source/controls/devices/joystick.h b/Source/controls/devices/joystick.h index 43038f359..a7e46855f 100644 --- a/Source/controls/devices/joystick.h +++ b/Source/controls/devices/joystick.h @@ -34,9 +34,9 @@ public: return instance_id_; } -private: static int ToSdlJoyButton(ControllerButton button); +private: bool IsHatButtonPressed(ControllerButton button) const; SDL_Joystick *sdl_joystick_ = NULL; diff --git a/Source/controls/game_controls.cpp b/Source/controls/game_controls.cpp index b48fd1b1d..5b488f5a7 100644 --- a/Source/controls/game_controls.cpp +++ b/Source/controls/game_controls.cpp @@ -15,6 +15,7 @@ #include "doom.h" #include "gmenu.h" #include "options.h" +#include "plrctrls.h" #include "qol/stash.h" #include "stores.h" @@ -22,6 +23,8 @@ namespace devilution { bool start_modifier_active = false; bool select_modifier_active = false; +const ControllerButton ControllerButton_ATTACK = ControllerButton_BUTTON_B; +const ControllerButton ControllerButton_CAST_SPELL = ControllerButton_BUTTON_X; namespace { @@ -194,6 +197,21 @@ bool GetGameAction(const SDL_Event &event, ControllerButtonEvent ctrlEvent, Game break; } + if (ctrlEvent.state == ControllerButtonState_HELD) { + if (CanControlHero() && !select_modifier_active && !start_modifier_active) { + switch (ctrlEvent.button) { + case ControllerButton_ATTACK: + *action = GameAction(GameActionType_PRIMARY_ACTION); + break; + case ControllerButton_CAST_SPELL: + *action = GameAction(GameActionType_CAST_SPELL); + break; + } + } + + return true; + } + if (!inGameMenu) { switch (ctrlEvent.button) { case ControllerButton_BUTTON_LEFTSHOULDER: diff --git a/Source/controls/game_controls.h b/Source/controls/game_controls.h index 3d22e15cc..a3eefe7a1 100644 --- a/Source/controls/game_controls.h +++ b/Source/controls/game_controls.h @@ -75,5 +75,7 @@ AxisDirection GetMoveDirection(); extern bool start_modifier_active; extern bool select_modifier_active; +extern const ControllerButton ControllerButton_ATTACK; +extern const ControllerButton ControllerButton_CAST_SPELL; } // namespace devilution diff --git a/Source/controls/input.h b/Source/controls/input.h index 0e4b728b8..de0f7c367 100644 --- a/Source/controls/input.h +++ b/Source/controls/input.h @@ -9,8 +9,13 @@ namespace devilution { inline int PollEvent(SDL_Event *event) { int result = SDL_PollEvent(event); - if (result != 0) + if (result != 0) { UnlockControllerState(*event); + UnlockHeldControllerButtonEvents(*event); + } else { + result = PollActionButtonPressed(event); + } + return result; } diff --git a/Source/controls/plrctrls.cpp b/Source/controls/plrctrls.cpp index fcae1c1bf..7ae7d507b 100644 --- a/Source/controls/plrctrls.cpp +++ b/Source/controls/plrctrls.cpp @@ -61,6 +61,16 @@ bool InGameMenu() || Players[MyPlayerId]._pInvincible; } +bool CanControlHero() +{ + return !InGameMenu() + && !invflag + && !spselflag + && !QuestLogIsOpen + && !sbookflag + && !DoomFlag; +} + namespace { int Slot = SLOTXY_INV_FIRST; diff --git a/Source/controls/plrctrls.h b/Source/controls/plrctrls.h index 34543b1ca..3b257e478 100644 --- a/Source/controls/plrctrls.h +++ b/Source/controls/plrctrls.h @@ -59,6 +59,8 @@ void HandleRightStickMotion(); // Whether we're in a dialog menu that the game handles natively with keyboard controls. bool InGameMenu(); +bool CanControlHero(); + void SetPointAndClick(bool value); bool IsPointAndClick();