From ccd8f114f8dcb2de174229d4a797be3538cfc641 Mon Sep 17 00:00:00 2001 From: staphen Date: Mon, 25 Oct 2021 22:14:10 -0400 Subject: [PATCH] [Virtual Gamepad] Add menu panel to top-right corner --- Source/controls/game_controls.cpp | 16 +++++ Source/controls/touch/event_handlers.cpp | 46 ++++++++----- Source/controls/touch/event_handlers.h | 31 ++++++--- Source/controls/touch/gamepad.cpp | 86 ++++++++++++++++++------ Source/controls/touch/gamepad.h | 53 +++++++++++++-- Source/controls/touch/renderers.cpp | 28 ++++++++ Source/controls/touch/renderers.h | 20 +++++- 7 files changed, 224 insertions(+), 56 deletions(-) diff --git a/Source/controls/game_controls.cpp b/Source/controls/game_controls.cpp index a40de6af1..14e091f58 100644 --- a/Source/controls/game_controls.cpp +++ b/Source/controls/game_controls.cpp @@ -100,6 +100,22 @@ bool GetGameAction(const SDL_Event &event, ControllerButtonEvent ctrlEvent, Game #if defined(VIRTUAL_GAMEPAD) && !defined(USE_SDL1) if (event.type == SDL_FINGERDOWN) { + if (VirtualGamepadState.menuPanel.charButton.isHeld && VirtualGamepadState.menuPanel.charButton.didStateChange) { + *action = GameAction(GameActionType_TOGGLE_CHARACTER_INFO); + return true; + } + if (VirtualGamepadState.menuPanel.questsButton.isHeld && VirtualGamepadState.menuPanel.questsButton.didStateChange) { + *action = GameAction(GameActionType_TOGGLE_QUEST_LOG); + return true; + } + if (VirtualGamepadState.menuPanel.inventoryButton.isHeld && VirtualGamepadState.menuPanel.inventoryButton.didStateChange) { + *action = GameAction(GameActionType_TOGGLE_INVENTORY); + return true; + } + if (VirtualGamepadState.menuPanel.mapButton.isHeld && VirtualGamepadState.menuPanel.mapButton.didStateChange) { + *action = GameActionSendKey { DVL_VK_TAB, false }; + return true; + } if (VirtualGamepadState.primaryActionButton.isHeld && VirtualGamepadState.primaryActionButton.didStateChange) { if (!inGameMenu && !QuestLogIsOpen && !sbookflag) *action = GameAction(GameActionType_PRIMARY_ACTION); diff --git a/Source/controls/touch/event_handlers.cpp b/Source/controls/touch/event_handlers.cpp index 8b2ce17cf..4e9059a89 100644 --- a/Source/controls/touch/event_handlers.cpp +++ b/Source/controls/touch/event_handlers.cpp @@ -127,6 +127,18 @@ bool VirtualGamepadEventHandler::Handle(const SDL_Event &event) return false; } + if (charMenuButtonEventHandler.Handle(event)) + return true; + + if (questsMenuButtonEventHandler.Handle(event)) + return true; + + if (inventoryMenuButtonEventHandler.Handle(event)) + return true; + + if (mapMenuButtonEventHandler.Handle(event)) + return true; + if (directionPadEventHandler.Handle(event)) return true; @@ -210,15 +222,15 @@ bool VirtualDirectionPadEventHandler::HandleFingerMotion(const SDL_TouchFingerEv return true; } -bool VirtualPadButtonEventHandler::Handle(const SDL_Event &event) +bool VirtualButtonEventHandler::Handle(const SDL_Event &event) { - if (!virtualPadButton->isUsable()) { - virtualPadButton->didStateChange = virtualPadButton->isHeld; - virtualPadButton->isHeld = false; + if (!virtualButton->isUsable()) { + virtualButton->didStateChange = virtualButton->isHeld; + virtualButton->isHeld = false; return false; } - virtualPadButton->didStateChange = false; + virtualButton->didStateChange = false; switch (event.type) { case SDL_FINGERDOWN: @@ -235,7 +247,7 @@ bool VirtualPadButtonEventHandler::Handle(const SDL_Event &event) } } -bool VirtualPadButtonEventHandler::HandleFingerDown(const SDL_TouchFingerEvent &event) +bool VirtualButtonEventHandler::HandleFingerDown(const SDL_TouchFingerEvent &event) { if (isActive) return false; @@ -244,30 +256,30 @@ bool VirtualPadButtonEventHandler::HandleFingerDown(const SDL_TouchFingerEvent & float y = event.y; Point touchCoordinates = ScaleToScreenCoordinates(x, y); - if (!virtualPadButton->area.Contains(touchCoordinates)) + if (!virtualButton->Contains(touchCoordinates)) return false; - virtualPadButton->isHeld = true; - virtualPadButton->didStateChange = true; + virtualButton->isHeld = true; + virtualButton->didStateChange = true; activeFinger = event.fingerId; isActive = true; return true; } -bool VirtualPadButtonEventHandler::HandleFingerUp(const SDL_TouchFingerEvent &event) +bool VirtualButtonEventHandler::HandleFingerUp(const SDL_TouchFingerEvent &event) { if (!isActive || event.fingerId != activeFinger) return false; - if (virtualPadButton->isHeld) - virtualPadButton->didStateChange = true; + if (virtualButton->isHeld) + virtualButton->didStateChange = true; - virtualPadButton->isHeld = false; + virtualButton->isHeld = false; isActive = false; return true; } -bool VirtualPadButtonEventHandler::HandleFingerMotion(const SDL_TouchFingerEvent &event) +bool VirtualButtonEventHandler::HandleFingerMotion(const SDL_TouchFingerEvent &event) { if (!isActive || event.fingerId != activeFinger) return false; @@ -276,9 +288,9 @@ bool VirtualPadButtonEventHandler::HandleFingerMotion(const SDL_TouchFingerEvent float y = event.y; Point touchCoordinates = ScaleToScreenCoordinates(x, y); - bool wasHeld = virtualPadButton->isHeld; - virtualPadButton->isHeld = virtualPadButton->area.Contains(touchCoordinates); - virtualPadButton->didStateChange = virtualPadButton->isHeld != wasHeld; + bool wasHeld = virtualButton->isHeld; + virtualButton->isHeld = virtualButton->Contains(touchCoordinates); + virtualButton->didStateChange = virtualButton->isHeld != wasHeld; return true; } diff --git a/Source/controls/touch/event_handlers.h b/Source/controls/touch/event_handlers.h index af701d34d..316bae3dd 100644 --- a/Source/controls/touch/event_handlers.h +++ b/Source/controls/touch/event_handlers.h @@ -29,10 +29,10 @@ private: bool HandleFingerMotion(const SDL_TouchFingerEvent &event); }; -class VirtualPadButtonEventHandler { +class VirtualButtonEventHandler { public: - VirtualPadButtonEventHandler(VirtualPadButton *virtualPadButton) - : virtualPadButton(virtualPadButton) + VirtualButtonEventHandler(VirtualButton *virtualButton) + : virtualButton(virtualButton) , activeFinger(0) , isActive(false) { @@ -41,7 +41,7 @@ public: bool Handle(const SDL_Event &event); private: - VirtualPadButton *virtualPadButton; + VirtualButton *virtualButton; SDL_FingerID activeFinger; bool isActive; @@ -53,7 +53,11 @@ private: class VirtualGamepadEventHandler { public: VirtualGamepadEventHandler(VirtualGamepad *virtualGamepad) - : directionPadEventHandler(&virtualGamepad->directionPad) + : charMenuButtonEventHandler(&virtualGamepad->menuPanel.charButton) + , questsMenuButtonEventHandler(&virtualGamepad->menuPanel.questsButton) + , inventoryMenuButtonEventHandler(&virtualGamepad->menuPanel.inventoryButton) + , mapMenuButtonEventHandler(&virtualGamepad->menuPanel.mapButton) + , directionPadEventHandler(&virtualGamepad->directionPad) , primaryActionButtonEventHandler(&virtualGamepad->primaryActionButton) , secondaryActionButtonEventHandler(&virtualGamepad->secondaryActionButton) , spellActionButtonEventHandler(&virtualGamepad->spellActionButton) @@ -66,15 +70,20 @@ public: bool Handle(const SDL_Event &event); private: + VirtualButtonEventHandler charMenuButtonEventHandler; + VirtualButtonEventHandler questsMenuButtonEventHandler; + VirtualButtonEventHandler inventoryMenuButtonEventHandler; + VirtualButtonEventHandler mapMenuButtonEventHandler; + VirtualDirectionPadEventHandler directionPadEventHandler; - VirtualPadButtonEventHandler primaryActionButtonEventHandler; - VirtualPadButtonEventHandler secondaryActionButtonEventHandler; - VirtualPadButtonEventHandler spellActionButtonEventHandler; - VirtualPadButtonEventHandler cancelButtonEventHandler; + VirtualButtonEventHandler primaryActionButtonEventHandler; + VirtualButtonEventHandler secondaryActionButtonEventHandler; + VirtualButtonEventHandler spellActionButtonEventHandler; + VirtualButtonEventHandler cancelButtonEventHandler; - VirtualPadButtonEventHandler healthButtonEventHandler; - VirtualPadButtonEventHandler manaButtonEventHandler; + VirtualButtonEventHandler healthButtonEventHandler; + VirtualButtonEventHandler manaButtonEventHandler; }; void HandleTouchEvent(const SDL_Event &event); diff --git a/Source/controls/touch/gamepad.cpp b/Source/controls/touch/gamepad.cpp index 1025e870c..c1a83aa0e 100644 --- a/Source/controls/touch/gamepad.cpp +++ b/Source/controls/touch/gamepad.cpp @@ -51,6 +51,7 @@ void InitializeVirtualGamepad() { int screenPixels = std::min(gnScreenWidth, gnScreenHeight); int inputMargin = screenPixels / 10; + int menuButtonWidth = screenPixels / 10; int directionPadSize = screenPixels / 4; int padButtonSize = round(1.1 * screenPixels / 10); int padButtonSpacing = inputMargin / 3; @@ -67,11 +68,20 @@ void InitializeVirtualGamepad() float dpi = std::min(hdpi, vdpi); inputMargin = round(0.25 * dpi); + menuButtonWidth = round(0.2 * dpi); directionPadSize = round(dpi); padButtonSize = round(0.3 * dpi); padButtonSpacing = round(0.1 * dpi); } + int menuPanelTopMargin = 30; + int menuPanelButtonSpacing = 4; + Size menuPanelButtonSize = { 64, 62 }; + int rightMarginMenuButton4 = menuPanelButtonSpacing + menuPanelButtonSize.width; + int rightMarginMenuButton3 = rightMarginMenuButton4 + menuPanelButtonSpacing + menuPanelButtonSize.width; + int rightMarginMenuButton2 = rightMarginMenuButton3 + menuPanelButtonSpacing + menuPanelButtonSize.width; + int rightMarginMenuButton1 = rightMarginMenuButton2 + menuPanelButtonSpacing + menuPanelButtonSize.width; + int padButtonAreaWidth = round(std::sqrt(2) * (padButtonSize + padButtonSpacing)); int padButtonRight = gnScreenWidth - inputMargin - padButtonSize / 2; @@ -79,42 +89,74 @@ void InitializeVirtualGamepad() int padButtonBottom = gnScreenHeight - inputMargin - padButtonSize / 2; int padButtonTop = padButtonBottom - padButtonAreaWidth; + Rectangle &charButtonArea = VirtualGamepadState.menuPanel.charButton.area; + charButtonArea.position.x = gnScreenWidth - rightMarginMenuButton1 * menuButtonWidth / menuPanelButtonSize.width; + charButtonArea.position.y = menuPanelTopMargin * menuButtonWidth / menuPanelButtonSize.width; + charButtonArea.size.width = menuButtonWidth; + charButtonArea.size.height = menuPanelButtonSize.height * menuButtonWidth / menuPanelButtonSize.width; + + Rectangle &questsButtonArea = VirtualGamepadState.menuPanel.questsButton.area; + questsButtonArea.position.x = gnScreenWidth - rightMarginMenuButton2 * menuButtonWidth / menuPanelButtonSize.width; + questsButtonArea.position.y = menuPanelTopMargin * menuButtonWidth / menuPanelButtonSize.width; + questsButtonArea.size.width = menuButtonWidth; + questsButtonArea.size.height = menuPanelButtonSize.height * menuButtonWidth / menuPanelButtonSize.width; + + Rectangle &inventoryButtonArea = VirtualGamepadState.menuPanel.inventoryButton.area; + inventoryButtonArea.position.x = gnScreenWidth - rightMarginMenuButton3 * menuButtonWidth / menuPanelButtonSize.width; + inventoryButtonArea.position.y = menuPanelTopMargin * menuButtonWidth / menuPanelButtonSize.width; + inventoryButtonArea.size.width = menuButtonWidth; + inventoryButtonArea.size.height = menuPanelButtonSize.height * menuButtonWidth / menuPanelButtonSize.width; + + Rectangle &mapButtonArea = VirtualGamepadState.menuPanel.mapButton.area; + mapButtonArea.position.x = gnScreenWidth - rightMarginMenuButton4 * menuButtonWidth / menuPanelButtonSize.width; + mapButtonArea.position.y = menuPanelTopMargin * menuButtonWidth / menuPanelButtonSize.width; + mapButtonArea.size.width = menuButtonWidth; + mapButtonArea.size.height = menuPanelButtonSize.height * menuButtonWidth / menuPanelButtonSize.width; + + Rectangle &menuPanelArea = VirtualGamepadState.menuPanel.area; + menuPanelArea.position.x = gnScreenWidth - 399 * menuButtonWidth / menuPanelButtonSize.width; + menuPanelArea.position.y = 0; + menuPanelArea.size.width = 399 * menuButtonWidth / menuPanelButtonSize.width; + menuPanelArea.size.height = 162 * menuButtonWidth / menuPanelButtonSize.width; + VirtualDirectionPad &directionPad = VirtualGamepadState.directionPad; directionPad.area.position.x = inputMargin + directionPadSize / 2; directionPad.area.position.y = gnScreenHeight - inputMargin - directionPadSize / 2; directionPad.area.radius = directionPadSize / 2; directionPad.position = directionPad.area.position; - VirtualPadButton &primaryActionButton = VirtualGamepadState.primaryActionButton; - primaryActionButton.area.position.x = padButtonRight; - primaryActionButton.area.position.y = (padButtonTop + padButtonBottom) / 2; - primaryActionButton.area.radius = padButtonSize / 2; + Circle &primaryActionButtonArea = VirtualGamepadState.primaryActionButton.area; + primaryActionButtonArea.position.x = padButtonRight; + primaryActionButtonArea.position.y = (padButtonTop + padButtonBottom) / 2; + primaryActionButtonArea.radius = padButtonSize / 2; - VirtualPadButton &secondaryActionButton = VirtualGamepadState.secondaryActionButton; - secondaryActionButton.area.position.x = (padButtonLeft + padButtonRight) / 2; - secondaryActionButton.area.position.y = padButtonTop; - secondaryActionButton.area.radius = padButtonSize / 2; + Circle &secondaryActionButtonArea = VirtualGamepadState.secondaryActionButton.area; + secondaryActionButtonArea.position.x = (padButtonLeft + padButtonRight) / 2; + secondaryActionButtonArea.position.y = padButtonTop; + secondaryActionButtonArea.radius = padButtonSize / 2; - VirtualPadButton &spellActionButton = VirtualGamepadState.spellActionButton; - spellActionButton.area.position.x = padButtonLeft; - spellActionButton.area.position.y = (padButtonTop + padButtonBottom) / 2; - spellActionButton.area.radius = padButtonSize / 2; + Circle &spellActionButtonArea = VirtualGamepadState.spellActionButton.area; + spellActionButtonArea.position.x = padButtonLeft; + spellActionButtonArea.position.y = (padButtonTop + padButtonBottom) / 2; + spellActionButtonArea.radius = padButtonSize / 2; - VirtualPadButton &cancelButton = VirtualGamepadState.cancelButton; - cancelButton.area.position.x = (padButtonLeft + padButtonRight) / 2; - cancelButton.area.position.y = padButtonBottom; - cancelButton.area.radius = padButtonSize / 2; + Circle &cancelButtonArea = VirtualGamepadState.cancelButton.area; + cancelButtonArea.position.x = (padButtonLeft + padButtonRight) / 2; + cancelButtonArea.position.y = padButtonBottom; + cancelButtonArea.radius = padButtonSize / 2; VirtualPadButton &healthButton = VirtualGamepadState.healthButton; - healthButton.area.position.x = directionPad.area.position.x - (padButtonSize + padButtonSpacing) / 2; - healthButton.area.position.y = directionPad.area.position.y - (directionPadSize + padButtonSize + padButtonSpacing) / 2; - healthButton.area.radius = padButtonSize / 2; + Circle &healthButtonArea = healthButton.area; + healthButtonArea.position.x = directionPad.area.position.x - (padButtonSize + padButtonSpacing) / 2; + healthButtonArea.position.y = directionPad.area.position.y - (directionPadSize + padButtonSize + padButtonSpacing) / 2; + healthButtonArea.radius = padButtonSize / 2; healthButton.isUsable = []() { return !chrflag && !QuestLogIsOpen; }; VirtualPadButton &manaButton = VirtualGamepadState.manaButton; - manaButton.area.position.x = directionPad.area.position.x + (padButtonSize + padButtonSpacing) / 2; - manaButton.area.position.y = directionPad.area.position.y - (directionPadSize + padButtonSize + padButtonSpacing) / 2; - manaButton.area.radius = padButtonSize / 2; + Circle &manaButtonArea = manaButton.area; + manaButtonArea.position.x = directionPad.area.position.x + (padButtonSize + padButtonSpacing) / 2; + manaButtonArea.position.y = directionPad.area.position.y - (directionPadSize + padButtonSize + padButtonSpacing) / 2; + manaButtonArea.radius = padButtonSize / 2; manaButton.isUsable = []() { return !chrflag && !QuestLogIsOpen; }; } diff --git a/Source/controls/touch/gamepad.h b/Source/controls/touch/gamepad.h index ba2bba8db..31b4d2ed7 100644 --- a/Source/controls/touch/gamepad.h +++ b/Source/controls/touch/gamepad.h @@ -7,6 +7,7 @@ #include "controls/controller_buttons.h" #include "engine/circle.hpp" #include "engine/point.hpp" +#include "engine/rectangle.hpp" namespace devilution { @@ -31,22 +32,64 @@ struct VirtualDirectionPad { void UpdatePosition(Point touchCoordinates); }; -struct VirtualPadButton { - Circle area; +struct VirtualButton { bool isHeld; bool didStateChange; std::function isUsable; - VirtualPadButton() - : area({ { 0, 0 }, 0 }) - , isHeld(false) + VirtualButton() + : isHeld(false) , didStateChange(false) , isUsable([]() { return true; }) { } + + virtual bool Contains(Point point) = 0; +}; + +struct VirtualMenuButton : VirtualButton { + Rectangle area; + + VirtualMenuButton() + : area({ { 0, 0 }, { 0, 0 } }) + { + } + + bool Contains(Point point) override + { + return area.Contains(point); + } +}; + +struct VirtualPadButton : VirtualButton { + Circle area; + + VirtualPadButton() + : area({ { 0, 0 }, 0 }) + { + } + + bool Contains(Point point) override + { + return area.Contains(point); + } +}; + +struct VirtualMenuPanel { + VirtualMenuButton charButton; + VirtualMenuButton questsButton; + VirtualMenuButton inventoryButton; + VirtualMenuButton mapButton; + Rectangle area; + + VirtualMenuPanel() + : area({ { 0, 0 }, { 0, 0 } }) + { + } }; struct VirtualGamepad { + VirtualMenuPanel menuPanel; VirtualDirectionPad directionPad; VirtualPadButton primaryActionButton; diff --git a/Source/controls/touch/renderers.cpp b/Source/controls/touch/renderers.cpp index 2ef9c5e24..3ffb6534f 100644 --- a/Source/controls/touch/renderers.cpp +++ b/Source/controls/touch/renderers.cpp @@ -190,11 +190,22 @@ void RenderVirtualGamepad(SDL_Surface *surface) void VirtualGamepadRenderer::LoadArt(SDL_Renderer *renderer) { + menuPanelRenderer.LoadArt(renderer); directionPadRenderer.LoadArt(renderer); LoadButtonArt(&buttonArt, renderer); LoadPotionArt(&potionArt, renderer); } +void VirtualMenuPanelRenderer::LoadArt(SDL_Renderer *renderer) +{ + menuArt.surface.reset(LoadPNG("ui_art\\menu.png")); + + if (renderer != nullptr) { + menuArt.texture.reset(SDL_CreateTextureFromSurface(renderer, menuArt.surface.get())); + menuArt.surface = nullptr; + } +} + void VirtualDirectionPadRenderer::LoadArt(SDL_Renderer *renderer) { padArt.surface.reset(LoadPNG("ui_art\\directions.png")); @@ -225,6 +236,17 @@ void VirtualGamepadRenderer::Render(RenderFunction renderFunction) manaButtonRenderer.RenderPotion(renderFunction, potionArt); directionPadRenderer.Render(renderFunction); + menuPanelRenderer.Render(renderFunction); +} + +void VirtualMenuPanelRenderer::Render(RenderFunction renderFunction) +{ + int x = virtualMenuPanel->area.position.x; + int y = virtualMenuPanel->area.position.y; + int width = virtualMenuPanel->area.size.width; + int height = virtualMenuPanel->area.size.height; + SDL_Rect rect { x, y, width, height }; + renderFunction(menuArt, nullptr, &rect); } void VirtualDirectionPadRenderer::Render(RenderFunction renderFunction) @@ -450,11 +472,17 @@ VirtualGamepadButtonType PotionButtonRenderer::GetButtonType() void VirtualGamepadRenderer::UnloadArt() { + menuPanelRenderer.UnloadArt(); directionPadRenderer.UnloadArt(); buttonArt.Unload(); potionArt.Unload(); } +void VirtualMenuPanelRenderer::UnloadArt() +{ + menuArt.Unload(); +} + void VirtualDirectionPadRenderer::UnloadArt() { padArt.Unload(); diff --git a/Source/controls/touch/renderers.h b/Source/controls/touch/renderers.h index 09d833b49..c920764a9 100644 --- a/Source/controls/touch/renderers.h +++ b/Source/controls/touch/renderers.h @@ -51,6 +51,22 @@ enum VirtualGamepadPotionType { typedef std::function RenderFunction; +class VirtualMenuPanelRenderer { +public: + VirtualMenuPanelRenderer(VirtualMenuPanel *virtualMenuPanel) + : virtualMenuPanel(virtualMenuPanel) + { + } + + void LoadArt(SDL_Renderer *renderer); + void Render(RenderFunction renderFunction); + void UnloadArt(); + +private: + VirtualMenuPanel *virtualMenuPanel; + Art menuArt; +}; + class VirtualDirectionPadRenderer { public: VirtualDirectionPadRenderer(VirtualDirectionPad *virtualDirectionPad) @@ -153,7 +169,8 @@ private: class VirtualGamepadRenderer { public: VirtualGamepadRenderer(VirtualGamepad *virtualGamepad) - : directionPadRenderer(&virtualGamepad->directionPad) + : menuPanelRenderer(&virtualGamepad->menuPanel) + , directionPadRenderer(&virtualGamepad->directionPad) , primaryActionButtonRenderer(&virtualGamepad->primaryActionButton) , secondaryActionButtonRenderer(&virtualGamepad->secondaryActionButton) , spellActionButtonRenderer(&virtualGamepad->spellActionButton) @@ -168,6 +185,7 @@ public: void UnloadArt(); private: + VirtualMenuPanelRenderer menuPanelRenderer; VirtualDirectionPadRenderer directionPadRenderer; PrimaryActionButtonRenderer primaryActionButtonRenderer;