diff --git a/Source/controls/game_controls.cpp b/Source/controls/game_controls.cpp index 1bd32123d..1cfb51040 100644 --- a/Source/controls/game_controls.cpp +++ b/Source/controls/game_controls.cpp @@ -126,6 +126,16 @@ bool GetGameAction(const SDL_Event &event, ControllerButtonEvent ctrlEvent, Game *action = GameActionSendKey { DVL_VK_SPACE, false }; return true; } + if (VirtualGamepadState.healthButton.isHeld && VirtualGamepadState.healthButton.didStateChange) { + if (!QuestLogIsOpen && !sbookflag && stextflag == STORE_NONE) + *action = GameAction(GameActionType_USE_HEALTH_POTION); + return true; + } + if (VirtualGamepadState.manaButton.isHeld && VirtualGamepadState.manaButton.didStateChange) { + if (!QuestLogIsOpen && !sbookflag && stextflag == STORE_NONE) + *action = GameAction(GameActionType_USE_MANA_POTION); + return true; + } } #endif diff --git a/Source/controls/touch/event_handlers.cpp b/Source/controls/touch/event_handlers.cpp index 31f7bd69e..9e0c092c2 100644 --- a/Source/controls/touch/event_handlers.cpp +++ b/Source/controls/touch/event_handlers.cpp @@ -125,6 +125,12 @@ bool VirtualGamepadEventHandler::Handle(const SDL_Event &event) if (cancelButtonEventHandler.Handle(event)) return true; + if (healthButtonEventHandler.Handle(event)) + return true; + + if (manaButtonEventHandler.Handle(event)) + return true; + return false; } diff --git a/Source/controls/touch/event_handlers.h b/Source/controls/touch/event_handlers.h index 1df9d24cd..af701d34d 100644 --- a/Source/controls/touch/event_handlers.h +++ b/Source/controls/touch/event_handlers.h @@ -58,6 +58,8 @@ public: , secondaryActionButtonEventHandler(&virtualGamepad->secondaryActionButton) , spellActionButtonEventHandler(&virtualGamepad->spellActionButton) , cancelButtonEventHandler(&virtualGamepad->cancelButton) + , healthButtonEventHandler(&virtualGamepad->healthButton) + , manaButtonEventHandler(&virtualGamepad->manaButton) { } @@ -65,10 +67,14 @@ public: private: VirtualDirectionPadEventHandler directionPadEventHandler; + VirtualPadButtonEventHandler primaryActionButtonEventHandler; VirtualPadButtonEventHandler secondaryActionButtonEventHandler; VirtualPadButtonEventHandler spellActionButtonEventHandler; VirtualPadButtonEventHandler cancelButtonEventHandler; + + VirtualPadButtonEventHandler healthButtonEventHandler; + VirtualPadButtonEventHandler manaButtonEventHandler; }; void HandleTouchEvent(const SDL_Event &event); diff --git a/Source/controls/touch/gamepad.cpp b/Source/controls/touch/gamepad.cpp index c97c46ede..a1468ae05 100644 --- a/Source/controls/touch/gamepad.cpp +++ b/Source/controls/touch/gamepad.cpp @@ -102,6 +102,16 @@ void InitializeVirtualGamepad() cancelButton.area.position.x = (padButtonLeft + padButtonRight) / 2; cancelButton.area.position.y = padButtonBottom; cancelButton.area.radius = padButtonSize / 2; + + VirtualPadButton &healthButton = VirtualGamepadState.healthButton; + healthButton.area.position.x = padButtonRight - padButtonSize - padButtonSpacing; + healthButton.area.position.y = padButtonTop - padButtonSize - padButtonSpacing; + healthButton.area.radius = padButtonSize / 2; + + VirtualPadButton &manaButton = VirtualGamepadState.manaButton; + manaButton.area.position.x = padButtonRight; + manaButton.area.position.y = padButtonTop - padButtonSize - padButtonSpacing; + manaButton.area.radius = padButtonSize / 2; } void VirtualDirectionPad::UpdatePosition(Point touchCoordinates) diff --git a/Source/controls/touch/gamepad.h b/Source/controls/touch/gamepad.h index 6b9533ca5..9cc32d813 100644 --- a/Source/controls/touch/gamepad.h +++ b/Source/controls/touch/gamepad.h @@ -44,11 +44,15 @@ struct VirtualPadButton { struct VirtualGamepad { VirtualDirectionPad directionPad; + VirtualPadButton primaryActionButton; VirtualPadButton secondaryActionButton; VirtualPadButton spellActionButton; VirtualPadButton cancelButton; + VirtualPadButton healthButton; + VirtualPadButton manaButton; + VirtualGamepad() { } diff --git a/Source/controls/touch/renderers.cpp b/Source/controls/touch/renderers.cpp index 96834f7d5..711593ba6 100644 --- a/Source/controls/touch/renderers.cpp +++ b/Source/controls/touch/renderers.cpp @@ -1,15 +1,17 @@ #include "control.h" -#include "controls/plrctrls.h" #include "controls/touch/renderers.h" #include "cursor.h" #include "doom.h" #include "engine.h" +#include "engine/render/cel_render.hpp" #include "gendung.h" #include "init.h" #include "inv.h" #include "minitext.h" #include "stores.h" #include "towners.h" +#include "utils/sdl_compat.h" +#include "utils/sdl_wrap.h" namespace devilution { @@ -52,6 +54,75 @@ VirtualGamepadButtonType GetBlankButtonType(bool isPressed) return isPressed ? GAMEPAD_BLANKDOWN : GAMEPAD_BLANK; } +void LoadButtonArt(Art *buttonArt, SDL_Renderer *renderer) +{ + const int Frames = 14; + buttonArt->surface.reset(LoadPNG("ui_art\\button.png")); + if (buttonArt->surface == nullptr) + return; + + buttonArt->logical_width = buttonArt->surface->w; + buttonArt->frame_height = buttonArt->surface->h / Frames; + buttonArt->frames = Frames; + + if (renderer != nullptr) { + buttonArt->texture.reset(SDL_CreateTextureFromSurface(renderer, buttonArt->surface.get())); + buttonArt->surface = nullptr; + } +} + +void LoadPotionArt(Art *potionArt, SDL_Renderer *renderer) +{ + item_cursor_graphic potionGraphics[] { + ICURS_POTION_OF_HEALING, + ICURS_POTION_OF_MANA, + ICURS_POTION_OF_REJUVENATION, + ICURS_POTION_OF_FULL_HEALING, + ICURS_POTION_OF_FULL_MANA, + ICURS_POTION_OF_FULL_REJUVENATION, + ICURS_SCROLL_OF + }; + + int potionFrame = CURSOR_FIRSTITEM + ICURS_POTION_OF_HEALING; + Size potionSize = GetInvItemSize(potionFrame); + + auto surface = SDLWrap::CreateRGBSurfaceWithFormat( + /*flags=*/0, + /*width=*/potionSize.width, + /*height=*/potionSize.height * sizeof(potionGraphics), + /*depth=*/8, + SDL_PIXELFORMAT_INDEX8); + + auto palette = SDLWrap::AllocPalette(); + if (SDLC_SetSurfaceAndPaletteColors(surface.get(), palette.get(), orig_palette, 0, 256) < 0) + ErrSdl(); + + Uint32 bgColor = SDL_MapRGB(surface->format, orig_palette[1].r, orig_palette[1].g, orig_palette[1].b); + if (SDL_FillRect(surface.get(), nullptr, bgColor) < 0) + ErrSdl(); + if (SDL_SetColorKey(surface.get(), SDL_TRUE, bgColor) < 0) + ErrSdl(); + + Point position { 0, 0 }; + for (item_cursor_graphic graphic : potionGraphics) { + const int frame = CURSOR_FIRSTITEM + graphic; + const CelSprite &potionSprite = GetInvItemSprite(frame); + position.y += potionSize.height; + CelClippedDrawTo(Surface(surface.get()), position, potionSprite, frame); + } + + potionArt->logical_width = potionSize.width; + potionArt->frame_height = potionSize.height; + potionArt->frames = sizeof(potionGraphics); + + if (renderer == nullptr) { + potionArt->surface.reset(SDL_ConvertSurfaceFormat(surface.get(), SDL_PIXELFORMAT_ARGB8888, 0)); + } else { + potionArt->texture.reset(SDL_CreateTextureFromSurface(renderer, surface.get())); + potionArt->surface = nullptr; + } +} + } // namespace void RenderVirtualGamepad(SDL_Renderer *renderer) @@ -83,20 +154,8 @@ void RenderVirtualGamepad(SDL_Surface *surface) void VirtualGamepadRenderer::LoadArt(SDL_Renderer *renderer) { directionPadRenderer.LoadArt(renderer); - - const int Frames = 14; - buttonArt.surface.reset(LoadPNG("ui_art\\button.png")); - if (buttonArt.surface == nullptr) - return; - - buttonArt.logical_width = buttonArt.surface->w; - buttonArt.frame_height = buttonArt.surface->h / Frames; - buttonArt.frames = Frames; - - if (renderer != nullptr) { - buttonArt.texture.reset(SDL_CreateTextureFromSurface(renderer, buttonArt.surface.get())); - buttonArt.surface = nullptr; - } + LoadButtonArt(&buttonArt, renderer); + LoadPotionArt(&potionArt, renderer); } void VirtualDirectionPadRenderer::LoadArt(SDL_Renderer *renderer) @@ -119,10 +178,16 @@ void VirtualGamepadRenderer::Render(RenderFunction renderFunction) return; directionPadRenderer.Render(renderFunction); + primaryActionButtonRenderer.Render(renderFunction, buttonArt); secondaryActionButtonRenderer.Render(renderFunction, buttonArt); spellActionButtonRenderer.Render(renderFunction, buttonArt); cancelButtonRenderer.Render(renderFunction, buttonArt); + healthButtonRenderer.Render(renderFunction, buttonArt); + manaButtonRenderer.Render(renderFunction, buttonArt); + + healthButtonRenderer.RenderPotion(renderFunction, potionArt); + manaButtonRenderer.RenderPotion(renderFunction, potionArt); } void VirtualDirectionPadRenderer::Render(RenderFunction renderFunction) @@ -179,6 +244,59 @@ void VirtualPadButtonRenderer::Render(RenderFunction renderFunction, Art &button renderFunction(buttonArt, &src, &dst); } +void PotionButtonRenderer::RenderPotion(RenderFunction renderFunction, Art &potionArt) +{ + VirtualGamepadPotionType potionType = GetPotionType(); + int frame = potionType; + int offset = potionArt.h() * frame; + + auto center = virtualPadButton->area.position; + auto radius = virtualPadButton->area.radius * 8 / 10; + int diameter = 2 * radius; + + int x = center.x - radius; + int y = center.y - radius; + int width = diameter; + int height = diameter; + + SDL_Rect src { 0, offset, potionArt.w(), potionArt.h() }; + SDL_Rect dst { x, y, width, height }; + renderFunction(potionArt, &src, &dst); +} + +VirtualGamepadPotionType PotionButtonRenderer::GetPotionType() +{ + for (int i = 0; i < MAXBELTITEMS; i++) { + auto &myPlayer = Players[MyPlayerId]; + const int id = AllItemsList[myPlayer.SpdList[i].IDidx].iMiscId; + const int spellId = AllItemsList[myPlayer.SpdList[i].IDidx].iSpell; + + if (myPlayer.SpdList[i].isEmpty()) + continue; + + if (potionType == BLT_HEALING) { + if (id == IMISC_HEAL) + return GAMEPAD_HEALING; + if (id == IMISC_FULLHEAL) + return GAMEPAD_FULL_HEALING; + if (id == IMISC_SCROLL && spellId == SPL_HEAL) + return GAMEPAD_SCROLL_OF_HEALING; + } + + if (potionType == BLT_MANA) { + if (id == IMISC_MANA) + return GAMEPAD_MANA; + if (id == IMISC_FULLMANA) + return GAMEPAD_FULL_MANA; + } + + if (id == IMISC_REJUV) + return GAMEPAD_REJUVENATION; + if (id == IMISC_FULLREJUV) + return GAMEPAD_FULL_REJUVENATION; + } +} + VirtualGamepadButtonType PrimaryActionButtonRenderer::GetButtonType() { // NEED: Confirm surface @@ -243,10 +361,16 @@ VirtualGamepadButtonType CancelButtonRenderer::GetButtonType() return GetBlankButtonType(virtualPadButton->isHeld); } +VirtualGamepadButtonType PotionButtonRenderer::GetButtonType() +{ + return GetBlankButtonType(virtualPadButton->isHeld); +} + void VirtualGamepadRenderer::UnloadArt() { directionPadRenderer.UnloadArt(); buttonArt.Unload(); + potionArt.Unload(); } void VirtualDirectionPadRenderer::UnloadArt() diff --git a/Source/controls/touch/renderers.h b/Source/controls/touch/renderers.h index 8adbbf4ba..c9200774c 100644 --- a/Source/controls/touch/renderers.h +++ b/Source/controls/touch/renderers.h @@ -2,6 +2,7 @@ #if defined(VIRTUAL_GAMEPAD) && !defined(USE_SDL1) +#include "controls/plrctrls.h" #include "controls/touch/gamepad.h" #include "engine/surface.hpp" #include "utils/png.h" @@ -26,6 +27,16 @@ enum VirtualGamepadButtonType { GAMEPAD_BLANKDOWN, }; +enum VirtualGamepadPotionType { + GAMEPAD_HEALING, + GAMEPAD_MANA, + GAMEPAD_REJUVENATION, + GAMEPAD_FULL_HEALING, + GAMEPAD_FULL_MANA, + GAMEPAD_FULL_REJUVENATION, + GAMEPAD_SCROLL_OF_HEALING, +}; + typedef std::function RenderFunction; class VirtualDirectionPadRenderer { @@ -110,6 +121,23 @@ private: VirtualGamepadButtonType GetButtonType(); }; +class PotionButtonRenderer : public VirtualPadButtonRenderer { +public: + PotionButtonRenderer(VirtualPadButton *potionButton, belt_item_type potionType) + : VirtualPadButtonRenderer(potionButton) + , potionType(potionType) + { + } + + void RenderPotion(RenderFunction renderFunction, Art &potionArt); + +private: + belt_item_type potionType; + + VirtualGamepadButtonType GetButtonType(); + VirtualGamepadPotionType GetPotionType(); +}; + class VirtualGamepadRenderer { public: VirtualGamepadRenderer(VirtualGamepad *virtualGamepad) @@ -118,6 +146,8 @@ public: , secondaryActionButtonRenderer(&virtualGamepad->secondaryActionButton) , spellActionButtonRenderer(&virtualGamepad->spellActionButton) , cancelButtonRenderer(&virtualGamepad->cancelButton) + , healthButtonRenderer(&virtualGamepad->healthButton, BLT_HEALING) + , manaButtonRenderer(&virtualGamepad->manaButton, BLT_MANA) { } @@ -127,11 +157,17 @@ public: private: VirtualDirectionPadRenderer directionPadRenderer; + PrimaryActionButtonRenderer primaryActionButtonRenderer; SecondaryActionButtonRenderer secondaryActionButtonRenderer; SpellActionButtonRenderer spellActionButtonRenderer; CancelButtonRenderer cancelButtonRenderer; + + PotionButtonRenderer healthButtonRenderer; + PotionButtonRenderer manaButtonRenderer; + Art buttonArt; + Art potionArt; }; void InitVirtualGamepadGFX(SDL_Renderer *renderer);