From 9dc2a0a4c2666d0f59133d2462241ff7de0443ba Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Sat, 20 Jan 2024 12:50:12 -0600 Subject: [PATCH] Add in input helper text when navigating using keyboard/gamepads. Fix scrollwheel navigation for ScrollingWindowComponent referencing the wrong variable to add to (after the targeted scroll destination change). --- .../Adventures in Lestoria.vcxproj | 12 + .../Adventures in Lestoria.vcxproj.filters | 9 + Adventures in Lestoria/InputHelper.cpp | 125 +++++++ Adventures in Lestoria/InputHelper.h | 58 ++++ Adventures in Lestoria/Key.cpp | 320 +++++++++++------- Adventures in Lestoria/Key.h | 38 ++- Adventures in Lestoria/LoadGameWindow.cpp | 28 +- Adventures in Lestoria/Menu.cpp | 24 +- Adventures in Lestoria/Menu.h | 37 +- Adventures in Lestoria/MenuType.h | 67 ++++ .../ScrollableWindowComponent.h | 4 +- Adventures in Lestoria/Version.h | 2 +- .../assets/config/Interface.txt | 9 + .../assets/config/gfx/gfx.txt | 10 + .../assets/config/gfx/themes.txt | 6 - 15 files changed, 571 insertions(+), 178 deletions(-) create mode 100644 Adventures in Lestoria/InputHelper.cpp create mode 100644 Adventures in Lestoria/InputHelper.h create mode 100644 Adventures in Lestoria/MenuType.h diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj index 0a321bdd..602cbf87 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj @@ -370,6 +370,10 @@ + + + + @@ -382,6 +386,10 @@ + + + + @@ -577,6 +585,10 @@ + + + + diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters index c62661fc..912ec2f1 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters @@ -423,6 +423,12 @@ Header Files\Interface + + Header Files\Interface + + + Header Files\Interface + @@ -725,6 +731,9 @@ Source Files\Monster Strategies + + Source Files\Interface + diff --git a/Adventures in Lestoria/InputHelper.cpp b/Adventures in Lestoria/InputHelper.cpp new file mode 100644 index 00000000..31e818b5 --- /dev/null +++ b/Adventures in Lestoria/InputHelper.cpp @@ -0,0 +1,125 @@ +#pragma region License +/* +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2024 Joshua Sigona + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions or derivations of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions or derivative works in binary form must reproduce the above +copyright notice. This list of conditions and the following disclaimer must be +reproduced in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may +be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +Portions of this software are copyright © 2023 The FreeType +Project (www.freetype.org). Please see LICENSE_FT.txt for more information. +All rights reserved. +*/ +#pragma endregion +#include "InputHelper.h" +#include "AdventuresInLestoria.h" +#include "Menu.h" + +INCLUDE_game +INCLUDE_GFX +INCLUDE_WINDOW_SIZE + +InputHelper::InputHelper(){} + +void InputHelper::Initialize(MenuInputGroups&inputGroups){ + for(auto&data:inputGroups){ + this->inputGroups[data.first.GetGroup()]=data.second.first; + } +} + +const InputType InputHelper::InputMode()const{ + if(Input::UsingGamepad())return CONTROLLER; + if(!Menu::stack.back()->UsingMouseNavigation())return KEY; + return MOUSE; +} + +void InputHelper::Draw(){ + InputType mode=InputMode(); + if(mode==MOUSE)mode=KEY; //We're going to make it so the keyboard controls always show up when navigating using a mouse. + switch(mode){ + case CONTROLLER: + case KEY:{ + float buttonImgWidth=0.f; + float buttonDescriptionWidth=0.f; + std::vector>buttonImgs; //Store decals for buttons that actually have images, and strings for buttons that have labels. + std::vectorbuttonDescriptions; + #pragma region Populate all buttons to display + for(auto&[group,displayName]:inputGroups){ + auto&primaryKey=group.GetPrimaryKey(mode); + if(primaryKey.has_value()){ + if(primaryKey.value().HasIcon()){ + buttonImgWidth+=primaryKey.value().GetIcon().Sprite()->width+"Interface.InputHelperSpacing"_I; + buttonImgs.push_back(primaryKey.value().GetIcon().Decal()); + }else{ + buttonImgs.push_back(primaryKey.value().GetDisplayName()); + } + + vf2d descriptionSize=game->GetTextSizeProp(displayName); + buttonDescriptionWidth+=descriptionSize.x+"Interface.InputHelperSpacing"_I; + buttonDescriptions.push_back(displayName); + } + } + #pragma endregion + + float buttonDescriptionScaleX=1.0f; + + if(buttonImgWidth+buttonDescriptionWidth>WINDOW_SIZE.x){ + buttonDescriptionScaleX=(WINDOW_SIZE.x-buttonImgWidth)/buttonDescriptionWidth; + } + + #pragma region Draw all button inputs and descriptions + float xOffset="Interface.InputHelperSpacing"_I; + for(size_t index=0;auto&button:buttonImgs){ + if(std::holds_alternative(button)){ + Decal*img=std::get(button); + game->DrawDecal(vf2d{xOffset,float(WINDOW_SIZE.y-img->sprite->height)-8},img); + xOffset+=img->sprite->width+"Interface.InputHelperSpacing"_I; + }else + if(std::holds_alternative(button)){ + std::string label=std::get(button); + vf2d textSize=game->GetTextSizeProp(label); + game->FillRectDecal(vf2d{xOffset-2,float(WINDOW_SIZE.y-textSize.y-10)},vf2d{textSize.x+4,textSize.y},"Interface.InputButtonBackCol"_Pixel); + game->FillRectDecal(vf2d{xOffset-1,float(WINDOW_SIZE.y-textSize.y-10)-1.f},vf2d{textSize.x+2,textSize.y},"Interface.InputButtonBackCol"_Pixel); + game->FillRectDecal(vf2d{xOffset-1,float(WINDOW_SIZE.y-textSize.y-10)},vf2d{textSize.x+2,textSize.y+1.f},"Interface.InputButtonBackCol"_Pixel); + game->DrawStringPropDecal(vf2d{xOffset,float(WINDOW_SIZE.y-textSize.y-10)},label,"Interface.InputButtonTextCol"_Pixel); + xOffset+=textSize.x+"Interface.InputHelperSpacing"_I; + }else [[unlikely]]ERR("WARNING! Hit a state where no data is inside of the button! THIS SHOULD NOT BE HAPPENING!"); + + std::string_view description=buttonDescriptions[index]; + vf2d descriptionTextSize=game->GetTextSizeProp(description)*vf2d{buttonDescriptionScaleX,1.f}; + game->DrawShadowStringPropDecal(vf2d{xOffset,float(WINDOW_SIZE.y-descriptionTextSize.y-10)},description,WHITE,BLACK,vf2d{buttonDescriptionScaleX,1.f}); + xOffset+=descriptionTextSize.x+"Interface.InputHelperSpacing"_I; + + index++; + } + #pragma endregion + }break; + [[unlikely]]default:{ + ERR(std::format("Invalid Input Mode detected: {}! THIS SHOULD NOT BE HAPPENING!",int(mode))) + } + } +} \ No newline at end of file diff --git a/Adventures in Lestoria/InputHelper.h b/Adventures in Lestoria/InputHelper.h new file mode 100644 index 00000000..e143f60a --- /dev/null +++ b/Adventures in Lestoria/InputHelper.h @@ -0,0 +1,58 @@ +#pragma region License +/* +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2024 Joshua Sigona + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions or derivations of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions or derivative works in binary form must reproduce the above +copyright notice. This list of conditions and the following disclaimer must be +reproduced in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may +be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +Portions of this software are copyright © 2023 The FreeType +Project (www.freetype.org). Please see LICENSE_FT.txt for more information. +All rights reserved. +*/ +#pragma endregion +#pragma once + +#include +#include +#include "Key.h" +#include "MenuType.h" + +using InputGroupActionDisplayName=std::string; +using ButtonName=std::string; +using MenuInputGroups=std::map>>>; + +class InputHelper{ + std::mapinputGroups; + + const InputType InputMode()const; + +public: + InputHelper(); + void Initialize(MenuInputGroups&inputGroups); + void Draw(); +}; \ No newline at end of file diff --git a/Adventures in Lestoria/Key.cpp b/Adventures in Lestoria/Key.cpp index c739828d..b20d5a0c 100644 --- a/Adventures in Lestoria/Key.cpp +++ b/Adventures in Lestoria/Key.cpp @@ -41,6 +41,7 @@ All rights reserved. #include "olcPGEX_Gamepad.h" INCLUDE_game +INCLUDE_GFX bool Input::usingGamepad; @@ -160,18 +161,43 @@ float Input::Analog(){ return 0.f; } -std::string Input::GetDisplayName(){ - return GenericKey::keyLiteral.at({type,key}); +std::string Input::GetDisplayName()const{ + return GenericKey::keyLiteral.at({type,key}).displayName; +} + +const std::string Input::str()const{ + std::string outputStr=""; + switch(type){ + case KEY:{ + outputStr=std::format("[Type:KEY,Key:{}]",GetDisplayName()); + }break; + case MOUSE:{ + outputStr=std::format("[Type:MOUSE,Key:{}]",GetDisplayName()); + }break; + case CONTROLLER:{ + outputStr=std::format("[Type:CONTROLLER,Key:{}]",GetDisplayName()); + }break; + case ANALOG:{ + outputStr=std::format("[Type:ANALOG,Key:{}]",GetDisplayName()); + }break; + default:{ + ERR(std::format("WARNING! Unhandled input type: {}",int(type))) + } + } + return outputStr; } InputGroup::InputGroup(){} void InputGroup::AddKeybind(Input bind){ keys.insert(bind); + keyOrder.push_back(bind); } void InputGroup::RemoveKeybind(Input bind){ - keys.erase(bind); + size_t erased=keys.erase(bind); + erased+=std::erase(keyOrder,bind); + if(erased!=2)ERR(std::format("WARNING! Could not remove keybind {}",bind.str())); } const bool InputGroup::Pressed()const{ @@ -214,124 +240,139 @@ std::string InputGroup::GetDisplayName(){ return combinationDisplay; } -std::map,std::string> GenericKey::keyLiteral={ - {{KEY, NONE},""}, - {{KEY, A},"A"}, - {{KEY, B},"B"}, - {{KEY, C},"C"}, - {{KEY, D},"D"}, - {{KEY, E},"E"}, - {{KEY, F},"F"}, - {{KEY, G},"G"}, - {{KEY, H},"H"}, - {{KEY, I},"I"}, - {{KEY, J},"J"}, - {{KEY, K},"L"}, - {{KEY, L},"L"}, - {{KEY, M},"M"}, - {{KEY, N},"N"}, - {{KEY, O},"O"}, - {{KEY, P},"P"}, - {{KEY, Q},"Q"}, - {{KEY, R},"R"}, - {{KEY, S},"S"}, - {{KEY, T},"T"}, - {{KEY, U},"U"}, - {{KEY, V},"V"}, - {{KEY, W},"W"}, - {{KEY, X},"X"}, - {{KEY, Y},"Y"}, - {{KEY, Z},"Z"}, - {{KEY, K0},"0"}, - {{KEY, K1},"1"}, - {{KEY, K2},"2"}, - {{KEY, K3},"3"}, - {{KEY, K4},"4"}, - {{KEY, K5},"5"}, - {{KEY, K6},"6"}, - {{KEY, K7},"7"}, - {{KEY, K8},"8"}, - {{KEY, K9},"9"}, - {{KEY, F1},"F1"}, - {{KEY, F2},"F2"}, - {{KEY, F3},"F3"}, - {{KEY, F4},"F4"}, - {{KEY, F5},"F5"}, - {{KEY, F6},"F6"}, - {{KEY, F7},"F7"}, - {{KEY, F8},"F8"}, - {{KEY, F9},"F9"}, - {{KEY, F10},"F10"}, - {{KEY, F11},"F11"}, - {{KEY, F12},"F12"}, - {{KEY, UP},"UP"}, - {{KEY, DOWN},"DOWN"}, - {{KEY, LEFT},"LEFT"}, - {{KEY, RIGHT},"RIGHT"}, - {{KEY, SPACE},"SPACE"}, - {{KEY, TAB},"TAB"}, - {{KEY, SHIFT},"SHIFT"}, - {{KEY, CTRL},"CTRL"}, - {{KEY, INS},"INS"}, - {{KEY, DEL},"DEL"}, - {{KEY, HOME},"HOME"}, - {{KEY, END},"END"}, - {{KEY, PGUP},"PGUP"}, - {{KEY, PGDN},"PGDN"}, - {{KEY, BACK},"BACK"}, - {{KEY, ESCAPE},"ESC"}, - {{KEY, RETURN},"ENTER"}, - {{KEY, ENTER},"ENTER"}, - {{KEY, PAUSE},"PAUSE"}, - {{KEY, SCROLL},"SCR LK"}, - {{KEY, NP0},"NP0"}, - {{KEY, NP1},"NP1"}, - {{KEY, NP2},"NP2"}, - {{KEY, NP3},"NP3"}, - {{KEY, NP4},"NP4"}, - {{KEY, NP5},"NP5"}, - {{KEY, NP6},"NP6"}, - {{KEY, NP7},"NP7"}, - {{KEY, NP8},"NP8"}, - {{KEY, NP9},"NP9"}, - {{KEY, NP_MUL},"NP*"}, - {{KEY, NP_DIV},"NP/"}, - {{KEY, NP_ADD},"NP+"}, - {{KEY, NP_SUB},"NP-"}, - {{KEY, NP_DECIMAL},"NP."}, - {{KEY, PERIOD},"."}, - {{KEY, EQUALS},"="}, - {{KEY, COMMA},","}, - {{KEY, MINUS},"-"}, - {{KEY, OEM_1},";"}, - {{KEY, OEM_2},"/"}, - {{KEY, OEM_3},"~"}, - {{KEY, OEM_4},"["}, - {{KEY, OEM_5},"\\"}, - {{KEY, OEM_6},"]"}, - {{KEY, OEM_7},"\""}, - {{KEY, OEM_8},"\\"}, - {{KEY, CAPS_LOCK},"CAP LK"}, - {{KEY, olc::ENUM_END},""}, - {{MOUSE, Mouse::LEFT},"L.MOUSE"}, - {{MOUSE, Mouse::RIGHT},"R.MOUSE"}, - {{MOUSE, Mouse::MIDDLE},"M.MOUSE"}, - {{CONTROLLER, static_cast(GPButtons::FACE_D)},"A/X"}, - {{CONTROLLER, static_cast(GPButtons::FACE_L)},"X/Square"}, - {{CONTROLLER, static_cast(GPButtons::FACE_R)},"B/Circle"}, - {{CONTROLLER, static_cast(GPButtons::FACE_U)},"Y/Triangle"}, - {{CONTROLLER, static_cast(GPButtons::L1)},"L1"}, - {{CONTROLLER, static_cast(GPButtons::L2)},"L2"}, - {{CONTROLLER, static_cast(GPButtons::L3)},"L3"}, - {{CONTROLLER, static_cast(GPButtons::R1)},"R1"}, - {{CONTROLLER, static_cast(GPButtons::R2)},"R2"}, - {{CONTROLLER, static_cast(GPButtons::R3)},"R3"}, - {{CONTROLLER, static_cast(GPButtons::SELECT)},"SELECT"}, - {{CONTROLLER, static_cast(GPButtons::START)},"START"}, - {{CONTROLLER, static_cast(GPButtons::DPAD_L)},"LEFT"}, - {{CONTROLLER, static_cast(GPButtons::DPAD_R)},"RIGHT"}, - {{CONTROLLER, static_cast(GPButtons::DPAD_U)},"UP"}, - {{CONTROLLER, static_cast(GPButtons::DPAD_D)},"DOWN"}, +const bool Input::HasIcon()const{ + return GenericKey::keyLiteral.at({type,key}).iconName.length()>0; +} +const Renderable&Input::GetIcon()const{ + return GFX.at(GenericKey::keyLiteral.at({type,key}).iconName); +} + +std::map,GenericKey::KeyInfo> GenericKey::keyLiteral={ + {{KEY, NONE},{""}}, + {{KEY, A},{"A"}}, + {{KEY, B},{"B"}}, + {{KEY, C},{"C"}}, + {{KEY, D},{"D"}}, + {{KEY, E},{"E"}}, + {{KEY, F},{"F"}}, + {{KEY, G},{"G"}}, + {{KEY, H},{"H"}}, + {{KEY, I},{"I"}}, + {{KEY, J},{"J"}}, + {{KEY, K},{"L"}}, + {{KEY, L},{"L"}}, + {{KEY, M},{"M"}}, + {{KEY, N},{"N"}}, + {{KEY, O},{"O"}}, + {{KEY, P},{"P"}}, + {{KEY, Q},{"Q"}}, + {{KEY, R},{"R"}}, + {{KEY, S},{"S"}}, + {{KEY, T},{"T"}}, + {{KEY, U},{"U"}}, + {{KEY, V},{"V"}}, + {{KEY, W},{"W"}}, + {{KEY, X},{"X"}}, + {{KEY, Y},{"Y"}}, + {{KEY, Z},{"Z"}}, + {{KEY, K0},{"0"}}, + {{KEY, K1},{"1"}}, + {{KEY, K2},{"2"}}, + {{KEY, K3},{"3"}}, + {{KEY, K4},{"4"}}, + {{KEY, K5},{"5"}}, + {{KEY, K6},{"6"}}, + {{KEY, K7},{"7"}}, + {{KEY, K8},{"8"}}, + {{KEY, K9},{"9"}}, + {{KEY, F1},{"F1"}}, + {{KEY, F2},{"F2"}}, + {{KEY, F3},{"F3"}}, + {{KEY, F4},{"F4"}}, + {{KEY, F5},{"F5"}}, + {{KEY, F6},{"F6"}}, + {{KEY, F7},{"F7"}}, + {{KEY, F8},{"F8"}}, + {{KEY, F9},{"F9"}}, + {{KEY, F10},{"F10"}}, + {{KEY, F11},{"F11"}}, + {{KEY, F12},{"F12"}}, + {{KEY, UP},{"UP"}}, + {{KEY, DOWN},{"DOWN"}}, + {{KEY, LEFT},{"LEFT"}}, + {{KEY, RIGHT},{"RIGHT"}}, + {{KEY, SPACE},{"SPACE"}}, + {{KEY, TAB},{"TAB"}}, + {{KEY, SHIFT},{"SHIFT"}}, + {{KEY, CTRL},{"CTRL"}}, + {{KEY, INS},{"INS"}}, + {{KEY, DEL},{"DEL"}}, + {{KEY, HOME},{"HOME"}}, + {{KEY, END},{"END"}}, + {{KEY, PGUP},{"PGUP"}}, + {{KEY, PGDN},{"PGDN"}}, + {{KEY, BACK},{"BACK"}}, + {{KEY, ESCAPE},{"ESC"}}, + {{KEY, RETURN},{"ENTER"}}, + {{KEY, ENTER},{"ENTER"}}, + {{KEY, PAUSE},{"PAUSE"}}, + {{KEY, SCROLL},{"SCR LK"}}, + {{KEY, NP0},{"NP0"}}, + {{KEY, NP1},{"NP1"}}, + {{KEY, NP2},{"NP2"}}, + {{KEY, NP3},{"NP3"}}, + {{KEY, NP4},{"NP4"}}, + {{KEY, NP5},{"NP5"}}, + {{KEY, NP6},{"NP6"}}, + {{KEY, NP7},{"NP7"}}, + {{KEY, NP8},{"NP8"}}, + {{KEY, NP9},{"NP9"}}, + {{KEY, NP_MUL},{"NP*"}}, + {{KEY, NP_DIV},{"NP/"}}, + {{KEY, NP_ADD},{"NP+"}}, + {{KEY, NP_SUB},{"NP-"}}, + {{KEY, NP_DECIMAL},{"NP."}}, + {{KEY, PERIOD},{"."}}, + {{KEY, EQUALS},{"="}}, + {{KEY, COMMA},{",{"}}, + {{KEY, MINUS},{"-"}}, + {{KEY, OEM_1},{";"}}, + {{KEY, OEM_2},{"/"}}, + {{KEY, OEM_3},{"~"}}, + {{KEY, OEM_4},{"["}}, + {{KEY, OEM_5},{"\\"}}, + {{KEY, OEM_6},{"]"}}, + {{KEY, OEM_7},{"\""}}, + {{KEY, OEM_8},{"\\"}}, + {{KEY, CAPS_LOCK},{"CAP LK"}}, + {{KEY, olc::ENUM_END},{""}}, + {{MOUSE, Mouse::LEFT},{"L.MOUSE"}}, + {{MOUSE, Mouse::RIGHT},{"R.MOUSE"}}, + {{MOUSE, Mouse::MIDDLE},{"M.MOUSE"}}, + {{CONTROLLER, static_cast(GPButtons::FACE_D)},{"A/X","themes/button_d.png"}}, + {{CONTROLLER, static_cast(GPButtons::FACE_L)},{"X/Square","themes/button_l.png"}}, + {{CONTROLLER, static_cast(GPButtons::FACE_R)},{"B/Circle","themes/button_r.png"}}, + {{CONTROLLER, static_cast(GPButtons::FACE_U)},{"Y/Triangle","themes/button_u.png"}}, + {{CONTROLLER, static_cast(GPButtons::L1)},{"L1","themes/button_l1.png"}}, + {{CONTROLLER, static_cast(GPButtons::L2)},{"L2","themes/button_l2.png"}}, + {{CONTROLLER, static_cast(GPButtons::L3)},{"L3"}}, + {{CONTROLLER, static_cast(GPButtons::R1)},{"R1","themes/button_r1.png"}}, + {{CONTROLLER, static_cast(GPButtons::R2)},{"R2","themes/button_r2.png"}}, + {{CONTROLLER, static_cast(GPButtons::R3)},{"R3"}}, + {{CONTROLLER, static_cast(GPButtons::SELECT)},{"SELECT"}}, + {{CONTROLLER, static_cast(GPButtons::START)},{"START"}}, + {{CONTROLLER, static_cast(GPButtons::DPAD_L)},{"LEFT"}}, + {{CONTROLLER, static_cast(GPButtons::DPAD_R)},{"RIGHT"}}, + {{CONTROLLER, static_cast(GPButtons::DPAD_U)},{"UP"}}, + {{CONTROLLER, static_cast(GPButtons::DPAD_D)},{"DOWN"}}, + {{ANALOG, static_cast(GPAxes::LY)},{"Up/Down","themes/button_analogstick_vert.png"}}, + {{ANALOG, static_cast(GPAxes::RY)},{"Up/Down","themes/button_analogstick_vert.png"}}, + {{ANALOG, static_cast(GPAxes::LX)},{"Right/Left","themes/button_analogstick_horz.png"}}, + {{ANALOG, static_cast(GPAxes::RX)},{"Right/Left","themes/button_analogstick_horz.png"}}, + {{ANALOG, static_cast(GPAxes::TL)},{"Left Trigger","themes/button_l2.png"}}, + {{ANALOG, static_cast(GPAxes::TR)},{"Right Trigger","themes/button_r2.png"}}, + {{ANALOG, static_cast(GPAxes::DX)},{"Right/Left","themes/button_analogstick_horz.png"}}, + {{ANALOG, static_cast(GPAxes::DY)},{"Up/Down","themes/button_analogstick_vert.png"}}, }; void Input::SetUsingGamepad(const bool usingGamepad){ @@ -385,4 +426,41 @@ void Input::StopVibration(){ const bool operator<(const InputGroup&group1,const InputGroup&group2){ return &group1<&group2; +} + +const bool operator<(const InputEngageGroup&group1,const InputEngageGroup&group2){ + return &group1<&group2; +} + +const InputType Input::GetType()const{ + return type; +} + +const std::optionalInputGroup::GetPrimaryKey(InputType type)const{ + if(type==ANALOG)ERR("WARNING! Type cannot be ANALOG! Not supported!"); + std::optionalresult; + if(keyOrder.size()>0){ + auto it=std::find_if(keyOrder.begin(),keyOrder.end(),[&](const Input&input){ + return input.GetType()==type + ||(type==CONTROLLER&&input.GetType()==ANALOG); + }); + if(it!=keyOrder.end())return *it; + } + return result; +} + +const bool operator==(const Input&input1,const Input&input2){ + return input1.type==input2.type&&input1.key==input2.key; +} + +InputEngageGroup::InputEngageGroup(InputGroup&group,EngageType type) + :group(group),type(type){} + + +const InputEngageGroup::EngageType InputEngageGroup::GetEngageType()const{ + return type; +} + +InputGroup&InputEngageGroup::GetGroup()const{ + return group; } \ No newline at end of file diff --git a/Adventures in Lestoria/Key.h b/Adventures in Lestoria/Key.h index e2cb259c..4170a4b5 100644 --- a/Adventures in Lestoria/Key.h +++ b/Adventures in Lestoria/Key.h @@ -39,6 +39,7 @@ All rights reserved. #include #include #include +#include "olcPixelGameEngine.h" //Future-proof game controller support. enum InputType{ @@ -61,7 +62,7 @@ public: bool Held(); bool Released(); float Analog(); - std::string GetDisplayName(); + std::string GetDisplayName()const; bool operator<(const Input&rhs)const{ return typekeys; + std::vectorkeyOrder; //A list of keys inserted in order, for primary key mapping. public: InputGroup(); void AddKeybind(Input bind); @@ -82,11 +89,36 @@ public: const bool Released()const; const float Analog()const; std::string GetDisplayName(); + const std::optionalGetPrimaryKey(InputType type)const; +}; + +class InputEngageGroup{ +public: + enum EngageType{ + Pressed, + Held, + Released, + Analog, + }; +private: + InputGroup&group; + EngageType type; +public: + InputEngageGroup(InputGroup&group,EngageType type=Released); + const EngageType GetEngageType()const; + InputGroup&GetGroup()const; }; class GenericKey{ + struct KeyInfo{ + std::string displayName; + std::string iconName; + }; public: - static std::map,std::string>keyLiteral; //The displayed text for a given key for a given input type. + static std::map,KeyInfo>keyLiteral; //The displayed text for a given key for a given input type. }; -const bool operator<(const InputGroup&group1,const InputGroup&group2); \ No newline at end of file +const bool operator<(const InputGroup&group1,const InputGroup&group2); +const bool operator<(const InputEngageGroup&group1,const InputEngageGroup&group2); + +using enum InputEngageGroup::EngageType; \ No newline at end of file diff --git a/Adventures in Lestoria/LoadGameWindow.cpp b/Adventures in Lestoria/LoadGameWindow.cpp index 861f2ca5..e9cb8b7c 100644 --- a/Adventures in Lestoria/LoadGameWindow.cpp +++ b/Adventures in Lestoria/LoadGameWindow.cpp @@ -47,6 +47,20 @@ void Menu::InitializeLoadGameWindow(){ loadGameWindow->ADD("Game Files List",ScrollableWindowComponent)(geom2d::rect{{-8,4},{112,116}})END; loadGameWindow->ADD("Go Back Button",MenuComponent)(geom2d::rect{{24,124},{48,12}},"Back",[](MenuFuncData menu){Menu::CloseMenu();return true;})END; + + #pragma region ScrollWindow macro lambda + #define ScrollWindow(amount) \ + [](MenuType type){ \ + auto scrollWindow=Component(type,"Game Files List"); \ + float scrollAmt=amount*game->GetElapsedTime()*"Interface.AnalogScrollSpeed"_F; \ + \ + scrollWindow->SetScrollAmount(scrollWindow->GetTargetScrollAmount()-vf2d{0,amount*game->GetElapsedTime()*"Interface.AnalogScrollSpeed"_F}); \ + \ + /*Height of these buttons is 48.*/ \ + scrollWindow->IncreaseSelectionIndex(scrollAmt/48.f); \ + } + #pragma endregion + loadGameWindow->SetupKeyboardNavigation( [](MenuType type,Data&returnData){ //On Open if(SaveFile::GetSaveFileCount()>0){ @@ -56,18 +70,12 @@ void Menu::InitializeLoadGameWindow(){ } }, { //Button Key - {game->KEY_BACK,{"Back to Title Screen",[](MenuType type){ + {{game->KEY_BACK},{"Back to Title Screen",[](MenuType type){ Component(type,"Go Back Button")->Click(); }}}, - {game->KEY_SCROLL,{"Scroll Up/Down",[](MenuType type){ - auto scrollWindow=Component(type,"Game Files List"); - float scrollAmt=game->KEY_SCROLL.Analog()*game->GetElapsedTime()*"Interface.AnalogScrollSpeed"_F; - - scrollWindow->SetScrollAmount(scrollWindow->GetTargetScrollAmount()-vf2d{0,game->KEY_SCROLL.Analog()*game->GetElapsedTime()*"Interface.AnalogScrollSpeed"_F}); - - //Height of these buttons is 48. - scrollWindow->IncreaseSelectionIndex(scrollAmt/48.f); - }}}, + {{game->KEY_SCROLL,Analog},{"Scroll",ScrollWindow(game->KEY_SCROLL.Analog())}}, + {{game->KEY_SCROLLUP,Held},{"Scroll Up",ScrollWindow(-1.0f)}}, + {{game->KEY_SCROLLDOWN,Held},{"Scroll Down",ScrollWindow(1.0f)}}, } ,{ //Button Navigation Rules {"Game Files List",{ diff --git a/Adventures in Lestoria/Menu.cpp b/Adventures in Lestoria/Menu.cpp index cb51c63b..19356da8 100644 --- a/Adventures in Lestoria/Menu.cpp +++ b/Adventures in Lestoria/Menu.cpp @@ -281,6 +281,10 @@ void Menu::Draw(AiL*game){ draggingComponent->DrawDecal(window,this==Menu::stack.back()); } } + + if(Menu::stack.back()==this){ + helpDisplay.Draw(); + } }; void Menu::OpenMenu(MenuType menu,bool cover){ @@ -297,7 +301,24 @@ void Menu::KeyboardButtonNavigation(AiL*game,vf2d menuPos){ std::weak_ptrprevSelection=selection; for(auto&[input,data]:inputGroups){ - if(input.Released()||input.Analog()!=0.f){ + bool activated=false; + switch(input.GetEngageType()){ + case Released:{ + activated=input.GetGroup().Released(); + }break; + case Pressed:{ + activated=input.GetGroup().Pressed(); + }break; + case Held:{ + activated=input.GetGroup().Held(); + }break; + case Analog:{ + activated=input.GetGroup().Analog()!=0.f; + }break; + [[unlikely]]default:ERR(std::format("WARNING! Unhandled input engage type {}! THIS SHOULD NOT BE HAPPENING!",int(input.GetEngageType()))); + } + + if(activated){ SetMouseNavigation(false); auto&action=data.second; if(std::holds_alternative(action))Component(type,std::get(action))->Click(); @@ -624,6 +645,7 @@ MenuFuncData::MenuFuncData(Menu&menu,AiL*const game,std::weak_ptr void Menu::SetupKeyboardNavigation(MenuDataFunc onOpen,MenuInputGroups inputGroups,ButtonNavigationGroups navigationGroups){ this->onOpenFunc=onOpen; this->inputGroups=inputGroups; + helpDisplay.Initialize(inputGroups); this->navigationGroups=navigationGroups; } diff --git a/Adventures in Lestoria/Menu.h b/Adventures in Lestoria/Menu.h index 60929559..94745fd9 100644 --- a/Adventures in Lestoria/Menu.h +++ b/Adventures in Lestoria/Menu.h @@ -43,7 +43,7 @@ All rights reserved. #include "Attributable.h" #include "olcUTIL_Geometry2D.h" #include "olcPGEX_ViewPort.h" -#include "Key.h" +#include "InputHelper.h" class AiL; class MenuComponent; @@ -70,41 +70,8 @@ using MenuFunc=std::function; #define DEFAULT_DEPTH -999999 #define STARTING_DEPTH 999999 -enum MenuType{ - /////////////////////////////////////////////////////////// - /*DO NOT REMOVE!!*/ENUM_START,/////////////////////////////// - /////////////////////////////////////////////////////////// - INVENTORY_CONSUMABLES, - CLASS_INFO, - CLASS_SELECTION, - MAIN_MENU, - OVERWORLD_LEVEL_SELECT, - ITEM_LOADOUT, - LEVEL_COMPLETE, - OVERWORLD_MENU, - CHARACTER_MENU, - INVENTORY, - MERCHANT, - BUY_ITEM, - SELL_ITEM, - BLACKSMITH, - CRAFT_ITEM, - CRAFT_CONSUMABLE, - CONSUMABLE_CRAFT_ITEM, - SAVE_FILE_NAME, - LOAD_GAME, - USER_ID, - /////////////////////////////////////////////////////////// - /*DO NOT REMOVE!!*/ENUM_END//////////////////////////////// - /////////////////////////////////////////////////////////// -}; - - -using InputGroupActionDisplayName=std::string; -using ButtonName=std::string; using Data=std::variant>; using MenuDataFunc=std::function; -using MenuInputGroups=std::map>>>; using ButtonNavigationGroups=std::map; @@ -253,6 +220,8 @@ private: std::weak_ptrselection; std::weak_ptrkeyboardSelection; + InputHelper helpDisplay; + static bool MOUSE_NAVIGATION; bool cover; //A black cover for when a menu pops up to fade out the stuff behind it. }; diff --git a/Adventures in Lestoria/MenuType.h b/Adventures in Lestoria/MenuType.h new file mode 100644 index 00000000..7d2295d6 --- /dev/null +++ b/Adventures in Lestoria/MenuType.h @@ -0,0 +1,67 @@ +#pragma region License +/* +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2024 Joshua Sigona + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions or derivations of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions or derivative works in binary form must reproduce the above +copyright notice. This list of conditions and the following disclaimer must be +reproduced in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may +be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +Portions of this software are copyright © 2023 The FreeType +Project (www.freetype.org). Please see LICENSE_FT.txt for more information. +All rights reserved. +*/ +#pragma endregion +#pragma once + +enum MenuType{ + /////////////////////////////////////////////////////////// + /*DO NOT REMOVE!!*/ENUM_START,/////////////////////////////// + /////////////////////////////////////////////////////////// + INVENTORY_CONSUMABLES, + CLASS_INFO, + CLASS_SELECTION, + MAIN_MENU, + OVERWORLD_LEVEL_SELECT, + ITEM_LOADOUT, + LEVEL_COMPLETE, + OVERWORLD_MENU, + CHARACTER_MENU, + INVENTORY, + MERCHANT, + BUY_ITEM, + SELL_ITEM, + BLACKSMITH, + CRAFT_ITEM, + CRAFT_CONSUMABLE, + CONSUMABLE_CRAFT_ITEM, + SAVE_FILE_NAME, + LOAD_GAME, + USER_ID, + /////////////////////////////////////////////////////////// + /*DO NOT REMOVE!!*/ENUM_END//////////////////////////////// + /////////////////////////////////////////////////////////// +}; \ No newline at end of file diff --git a/Adventures in Lestoria/ScrollableWindowComponent.h b/Adventures in Lestoria/ScrollableWindowComponent.h index 946df457..720c083f 100644 --- a/Adventures in Lestoria/ScrollableWindowComponent.h +++ b/Adventures in Lestoria/ScrollableWindowComponent.h @@ -160,9 +160,9 @@ protected: if(game->GetMouseWheel()!=0){ if(game->GetMouseWheel()>0){ - SetScrollAmount(GetScrollAmount()+vf2d{0,"ThemeGlobal.MenuScrollWheelSpeed"_F}); + SetScrollAmount(GetTargetScrollAmount()+vf2d{0,"ThemeGlobal.MenuScrollWheelSpeed"_F}); }else{ - SetScrollAmount(GetScrollAmount()-vf2d{0,"ThemeGlobal.MenuScrollWheelSpeed"_F}); + SetScrollAmount(GetTargetScrollAmount()-vf2d{0,"ThemeGlobal.MenuScrollWheelSpeed"_F}); } } diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index 1111294d..58d5c780 100644 --- a/Adventures in Lestoria/Version.h +++ b/Adventures in Lestoria/Version.h @@ -39,7 +39,7 @@ All rights reserved. #define VERSION_MAJOR 0 #define VERSION_MINOR 3 #define VERSION_PATCH 0 -#define VERSION_BUILD 6119 +#define VERSION_BUILD 6155 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/assets/config/Interface.txt b/Adventures in Lestoria/assets/config/Interface.txt index b38e194d..5ee84070 100644 --- a/Adventures in Lestoria/assets/config/Interface.txt +++ b/Adventures in Lestoria/assets/config/Interface.txt @@ -5,4 +5,13 @@ Interface # The size of each side of the 9-patch menu sprite. 9PatchSize = 24,24 + + # The spacing between inputs and text in the input helper text displayer. + InputHelperSpacing = 4 + + # The back color of input keys. + InputButtonBackCol = 15,8,47,255 + + # The text color of input keys. + InputButtonTextCol = 175,199,191,255 } \ No newline at end of file diff --git a/Adventures in Lestoria/assets/config/gfx/gfx.txt b/Adventures in Lestoria/assets/config/gfx/gfx.txt index b8279ce1..9ca6ef32 100644 --- a/Adventures in Lestoria/assets/config/gfx/gfx.txt +++ b/Adventures in Lestoria/assets/config/gfx/gfx.txt @@ -50,6 +50,16 @@ Images GFX_FrogTongueEnd = tongue_end.png GFX_AimingTarget = aiming_target.png GFX_AimingLine = aiming_line.png + GFX_Button_AnalogStick_Horz = themes/button_analogstick_horz.png + GFX_Button_AnalogStick_Vert = themes/button_analogstick_vert.png + GFX_Button_Face_Down = themes/button_d.png + GFX_Button_Face_Left = themes/button_l.png + GFX_Button_Face_Right = themes/button_r.png + GFX_Button_Face_Up = themes/button_u.png + GFX_Button_Face_L1 = themes/button_l1.png + GFX_Button_Face_L2 = themes/button_l2.png + GFX_Button_Face_R1 = themes/button_r1.png + GFX_Button_Face_R2 = themes/button_r2.png # Ability Icons GFX_Warrior_BattleCry_Icon = Ability Icons/battlecry.png diff --git a/Adventures in Lestoria/assets/config/gfx/themes.txt b/Adventures in Lestoria/assets/config/gfx/themes.txt index 42a38114..59cc4d9e 100644 --- a/Adventures in Lestoria/assets/config/gfx/themes.txt +++ b/Adventures in Lestoria/assets/config/gfx/themes.txt @@ -13,12 +13,6 @@ ThemeGlobal # How far the mouse has to move before mouse mode becomes active from keyboard/controller input mode. MouseActivationDistance = 8 - - # The back color of input keys. - InputButtonBackCol = 15,8,47,255 - - # The text color of input keys. - InputButtonTextCol = 175,199,191 } Themes