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).

pull/35/head
sigonasr2 11 months ago
parent a97bc7b2f0
commit 9dc2a0a4c2
  1. 12
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  2. 9
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  3. 125
      Adventures in Lestoria/InputHelper.cpp
  4. 58
      Adventures in Lestoria/InputHelper.h
  5. 320
      Adventures in Lestoria/Key.cpp
  6. 36
      Adventures in Lestoria/Key.h
  7. 28
      Adventures in Lestoria/LoadGameWindow.cpp
  8. 24
      Adventures in Lestoria/Menu.cpp
  9. 37
      Adventures in Lestoria/Menu.h
  10. 67
      Adventures in Lestoria/MenuType.h
  11. 4
      Adventures in Lestoria/ScrollableWindowComponent.h
  12. 2
      Adventures in Lestoria/Version.h
  13. 9
      Adventures in Lestoria/assets/config/Interface.txt
  14. 10
      Adventures in Lestoria/assets/config/gfx/gfx.txt
  15. 6
      Adventures in Lestoria/assets/config/gfx/themes.txt

@ -370,6 +370,10 @@
</SubType> </SubType>
</ClInclude> </ClInclude>
<ClInclude Include="GameState.h" /> <ClInclude Include="GameState.h" />
<ClInclude Include="InputHelper.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="InventoryCreator.h"> <ClInclude Include="InventoryCreator.h">
<SubType> <SubType>
</SubType> </SubType>
@ -382,6 +386,10 @@
<SubType> <SubType>
</SubType> </SubType>
</ClInclude> </ClInclude>
<ClInclude Include="MenuType.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="olcPGEX_SplashScreen.h" /> <ClInclude Include="olcPGEX_SplashScreen.h" />
<ClInclude Include="PlayerMoneyLabel.h"> <ClInclude Include="PlayerMoneyLabel.h">
<SubType> <SubType>
@ -577,6 +585,10 @@
</SubType> </SubType>
</ClCompile> </ClCompile>
<ClCompile Include="GameState.cpp" /> <ClCompile Include="GameState.cpp" />
<ClCompile Include="InputHelper.cpp">
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="InventoryConsumableWindow.cpp" /> <ClCompile Include="InventoryConsumableWindow.cpp" />
<ClCompile Include="InventoryCreator.cpp"> <ClCompile Include="InventoryCreator.cpp">
<SubType> <SubType>

@ -423,6 +423,12 @@
<ClInclude Include="Slider.h"> <ClInclude Include="Slider.h">
<Filter>Header Files\Interface</Filter> <Filter>Header Files\Interface</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="InputHelper.h">
<Filter>Header Files\Interface</Filter>
</ClInclude>
<ClInclude Include="MenuType.h">
<Filter>Header Files\Interface</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Player.cpp"> <ClCompile Include="Player.cpp">
@ -725,6 +731,9 @@
<ClCompile Include="Bear.cpp"> <ClCompile Include="Bear.cpp">
<Filter>Source Files\Monster Strategies</Filter> <Filter>Source Files\Monster Strategies</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="InputHelper.cpp">
<Filter>Source Files\Interface</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="cpp.hint" /> <None Include="cpp.hint" />

@ -0,0 +1,125 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
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<std::variant<Decal*,std::string>>buttonImgs; //Store decals for buttons that actually have images, and strings for buttons that have labels.
std::vector<std::string>buttonDescriptions;
#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<Decal*>(button)){
Decal*img=std::get<Decal*>(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<std::string>(button)){
std::string label=std::get<std::string>(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)))
}
}
}

@ -0,0 +1,58 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
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 <variant>
#include <functional>
#include "Key.h"
#include "MenuType.h"
using InputGroupActionDisplayName=std::string;
using ButtonName=std::string;
using MenuInputGroups=std::map<InputEngageGroup,std::pair<InputGroupActionDisplayName,std::variant<ButtonName,std::function<void(MenuType)>>>>;
class InputHelper{
std::map<InputGroup,InputGroupActionDisplayName>inputGroups;
const InputType InputMode()const;
public:
InputHelper();
void Initialize(MenuInputGroups&inputGroups);
void Draw();
};

@ -41,6 +41,7 @@ All rights reserved.
#include "olcPGEX_Gamepad.h" #include "olcPGEX_Gamepad.h"
INCLUDE_game INCLUDE_game
INCLUDE_GFX
bool Input::usingGamepad; bool Input::usingGamepad;
@ -160,18 +161,43 @@ float Input::Analog(){
return 0.f; return 0.f;
} }
std::string Input::GetDisplayName(){ std::string Input::GetDisplayName()const{
return GenericKey::keyLiteral.at({type,key}); 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(){} InputGroup::InputGroup(){}
void InputGroup::AddKeybind(Input bind){ void InputGroup::AddKeybind(Input bind){
keys.insert(bind); keys.insert(bind);
keyOrder.push_back(bind);
} }
void InputGroup::RemoveKeybind(Input 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{ const bool InputGroup::Pressed()const{
@ -214,124 +240,139 @@ std::string InputGroup::GetDisplayName(){
return combinationDisplay; return combinationDisplay;
} }
std::map<std::pair<InputType,int>,std::string> GenericKey::keyLiteral={ const bool Input::HasIcon()const{
{{KEY, NONE},""}, return GenericKey::keyLiteral.at({type,key}).iconName.length()>0;
{{KEY, A},"A"}, }
{{KEY, B},"B"}, const Renderable&Input::GetIcon()const{
{{KEY, C},"C"}, return GFX.at(GenericKey::keyLiteral.at({type,key}).iconName);
{{KEY, D},"D"}, }
{{KEY, E},"E"},
{{KEY, F},"F"}, std::map<std::pair<InputType,int>,GenericKey::KeyInfo> GenericKey::keyLiteral={
{{KEY, G},"G"}, {{KEY, NONE},{""}},
{{KEY, H},"H"}, {{KEY, A},{"A"}},
{{KEY, I},"I"}, {{KEY, B},{"B"}},
{{KEY, J},"J"}, {{KEY, C},{"C"}},
{{KEY, K},"L"}, {{KEY, D},{"D"}},
{{KEY, L},"L"}, {{KEY, E},{"E"}},
{{KEY, M},"M"}, {{KEY, F},{"F"}},
{{KEY, N},"N"}, {{KEY, G},{"G"}},
{{KEY, O},"O"}, {{KEY, H},{"H"}},
{{KEY, P},"P"}, {{KEY, I},{"I"}},
{{KEY, Q},"Q"}, {{KEY, J},{"J"}},
{{KEY, R},"R"}, {{KEY, K},{"L"}},
{{KEY, S},"S"}, {{KEY, L},{"L"}},
{{KEY, T},"T"}, {{KEY, M},{"M"}},
{{KEY, U},"U"}, {{KEY, N},{"N"}},
{{KEY, V},"V"}, {{KEY, O},{"O"}},
{{KEY, W},"W"}, {{KEY, P},{"P"}},
{{KEY, X},"X"}, {{KEY, Q},{"Q"}},
{{KEY, Y},"Y"}, {{KEY, R},{"R"}},
{{KEY, Z},"Z"}, {{KEY, S},{"S"}},
{{KEY, K0},"0"}, {{KEY, T},{"T"}},
{{KEY, K1},"1"}, {{KEY, U},{"U"}},
{{KEY, K2},"2"}, {{KEY, V},{"V"}},
{{KEY, K3},"3"}, {{KEY, W},{"W"}},
{{KEY, K4},"4"}, {{KEY, X},{"X"}},
{{KEY, K5},"5"}, {{KEY, Y},{"Y"}},
{{KEY, K6},"6"}, {{KEY, Z},{"Z"}},
{{KEY, K7},"7"}, {{KEY, K0},{"0"}},
{{KEY, K8},"8"}, {{KEY, K1},{"1"}},
{{KEY, K9},"9"}, {{KEY, K2},{"2"}},
{{KEY, F1},"F1"}, {{KEY, K3},{"3"}},
{{KEY, F2},"F2"}, {{KEY, K4},{"4"}},
{{KEY, F3},"F3"}, {{KEY, K5},{"5"}},
{{KEY, F4},"F4"}, {{KEY, K6},{"6"}},
{{KEY, F5},"F5"}, {{KEY, K7},{"7"}},
{{KEY, F6},"F6"}, {{KEY, K8},{"8"}},
{{KEY, F7},"F7"}, {{KEY, K9},{"9"}},
{{KEY, F8},"F8"}, {{KEY, F1},{"F1"}},
{{KEY, F9},"F9"}, {{KEY, F2},{"F2"}},
{{KEY, F10},"F10"}, {{KEY, F3},{"F3"}},
{{KEY, F11},"F11"}, {{KEY, F4},{"F4"}},
{{KEY, F12},"F12"}, {{KEY, F5},{"F5"}},
{{KEY, UP},"UP"}, {{KEY, F6},{"F6"}},
{{KEY, DOWN},"DOWN"}, {{KEY, F7},{"F7"}},
{{KEY, LEFT},"LEFT"}, {{KEY, F8},{"F8"}},
{{KEY, RIGHT},"RIGHT"}, {{KEY, F9},{"F9"}},
{{KEY, SPACE},"SPACE"}, {{KEY, F10},{"F10"}},
{{KEY, TAB},"TAB"}, {{KEY, F11},{"F11"}},
{{KEY, SHIFT},"SHIFT"}, {{KEY, F12},{"F12"}},
{{KEY, CTRL},"CTRL"}, {{KEY, UP},{"UP"}},
{{KEY, INS},"INS"}, {{KEY, DOWN},{"DOWN"}},
{{KEY, DEL},"DEL"}, {{KEY, LEFT},{"LEFT"}},
{{KEY, HOME},"HOME"}, {{KEY, RIGHT},{"RIGHT"}},
{{KEY, END},"END"}, {{KEY, SPACE},{"SPACE"}},
{{KEY, PGUP},"PGUP"}, {{KEY, TAB},{"TAB"}},
{{KEY, PGDN},"PGDN"}, {{KEY, SHIFT},{"SHIFT"}},
{{KEY, BACK},"BACK"}, {{KEY, CTRL},{"CTRL"}},
{{KEY, ESCAPE},"ESC"}, {{KEY, INS},{"INS"}},
{{KEY, RETURN},"ENTER"}, {{KEY, DEL},{"DEL"}},
{{KEY, ENTER},"ENTER"}, {{KEY, HOME},{"HOME"}},
{{KEY, PAUSE},"PAUSE"}, {{KEY, END},{"END"}},
{{KEY, SCROLL},"SCR LK"}, {{KEY, PGUP},{"PGUP"}},
{{KEY, NP0},"NP0"}, {{KEY, PGDN},{"PGDN"}},
{{KEY, NP1},"NP1"}, {{KEY, BACK},{"BACK"}},
{{KEY, NP2},"NP2"}, {{KEY, ESCAPE},{"ESC"}},
{{KEY, NP3},"NP3"}, {{KEY, RETURN},{"ENTER"}},
{{KEY, NP4},"NP4"}, {{KEY, ENTER},{"ENTER"}},
{{KEY, NP5},"NP5"}, {{KEY, PAUSE},{"PAUSE"}},
{{KEY, NP6},"NP6"}, {{KEY, SCROLL},{"SCR LK"}},
{{KEY, NP7},"NP7"}, {{KEY, NP0},{"NP0"}},
{{KEY, NP8},"NP8"}, {{KEY, NP1},{"NP1"}},
{{KEY, NP9},"NP9"}, {{KEY, NP2},{"NP2"}},
{{KEY, NP_MUL},"NP*"}, {{KEY, NP3},{"NP3"}},
{{KEY, NP_DIV},"NP/"}, {{KEY, NP4},{"NP4"}},
{{KEY, NP_ADD},"NP+"}, {{KEY, NP5},{"NP5"}},
{{KEY, NP_SUB},"NP-"}, {{KEY, NP6},{"NP6"}},
{{KEY, NP_DECIMAL},"NP."}, {{KEY, NP7},{"NP7"}},
{{KEY, PERIOD},"."}, {{KEY, NP8},{"NP8"}},
{{KEY, EQUALS},"="}, {{KEY, NP9},{"NP9"}},
{{KEY, COMMA},","}, {{KEY, NP_MUL},{"NP*"}},
{{KEY, MINUS},"-"}, {{KEY, NP_DIV},{"NP/"}},
{{KEY, OEM_1},";"}, {{KEY, NP_ADD},{"NP+"}},
{{KEY, OEM_2},"/"}, {{KEY, NP_SUB},{"NP-"}},
{{KEY, OEM_3},"~"}, {{KEY, NP_DECIMAL},{"NP."}},
{{KEY, OEM_4},"["}, {{KEY, PERIOD},{"."}},
{{KEY, OEM_5},"\\"}, {{KEY, EQUALS},{"="}},
{{KEY, OEM_6},"]"}, {{KEY, COMMA},{",{"}},
{{KEY, OEM_7},"\""}, {{KEY, MINUS},{"-"}},
{{KEY, OEM_8},"\\"}, {{KEY, OEM_1},{";"}},
{{KEY, CAPS_LOCK},"CAP LK"}, {{KEY, OEM_2},{"/"}},
{{KEY, olc::ENUM_END},""}, {{KEY, OEM_3},{"~"}},
{{MOUSE, Mouse::LEFT},"L.MOUSE"}, {{KEY, OEM_4},{"["}},
{{MOUSE, Mouse::RIGHT},"R.MOUSE"}, {{KEY, OEM_5},{"\\"}},
{{MOUSE, Mouse::MIDDLE},"M.MOUSE"}, {{KEY, OEM_6},{"]"}},
{{CONTROLLER, static_cast<int>(GPButtons::FACE_D)},"A/X"}, {{KEY, OEM_7},{"\""}},
{{CONTROLLER, static_cast<int>(GPButtons::FACE_L)},"X/Square"}, {{KEY, OEM_8},{"\\"}},
{{CONTROLLER, static_cast<int>(GPButtons::FACE_R)},"B/Circle"}, {{KEY, CAPS_LOCK},{"CAP LK"}},
{{CONTROLLER, static_cast<int>(GPButtons::FACE_U)},"Y/Triangle"}, {{KEY, olc::ENUM_END},{""}},
{{CONTROLLER, static_cast<int>(GPButtons::L1)},"L1"}, {{MOUSE, Mouse::LEFT},{"L.MOUSE"}},
{{CONTROLLER, static_cast<int>(GPButtons::L2)},"L2"}, {{MOUSE, Mouse::RIGHT},{"R.MOUSE"}},
{{CONTROLLER, static_cast<int>(GPButtons::L3)},"L3"}, {{MOUSE, Mouse::MIDDLE},{"M.MOUSE"}},
{{CONTROLLER, static_cast<int>(GPButtons::R1)},"R1"}, {{CONTROLLER, static_cast<int>(GPButtons::FACE_D)},{"A/X","themes/button_d.png"}},
{{CONTROLLER, static_cast<int>(GPButtons::R2)},"R2"}, {{CONTROLLER, static_cast<int>(GPButtons::FACE_L)},{"X/Square","themes/button_l.png"}},
{{CONTROLLER, static_cast<int>(GPButtons::R3)},"R3"}, {{CONTROLLER, static_cast<int>(GPButtons::FACE_R)},{"B/Circle","themes/button_r.png"}},
{{CONTROLLER, static_cast<int>(GPButtons::SELECT)},"SELECT"}, {{CONTROLLER, static_cast<int>(GPButtons::FACE_U)},{"Y/Triangle","themes/button_u.png"}},
{{CONTROLLER, static_cast<int>(GPButtons::START)},"START"}, {{CONTROLLER, static_cast<int>(GPButtons::L1)},{"L1","themes/button_l1.png"}},
{{CONTROLLER, static_cast<int>(GPButtons::DPAD_L)},"LEFT"}, {{CONTROLLER, static_cast<int>(GPButtons::L2)},{"L2","themes/button_l2.png"}},
{{CONTROLLER, static_cast<int>(GPButtons::DPAD_R)},"RIGHT"}, {{CONTROLLER, static_cast<int>(GPButtons::L3)},{"L3"}},
{{CONTROLLER, static_cast<int>(GPButtons::DPAD_U)},"UP"}, {{CONTROLLER, static_cast<int>(GPButtons::R1)},{"R1","themes/button_r1.png"}},
{{CONTROLLER, static_cast<int>(GPButtons::DPAD_D)},"DOWN"}, {{CONTROLLER, static_cast<int>(GPButtons::R2)},{"R2","themes/button_r2.png"}},
{{CONTROLLER, static_cast<int>(GPButtons::R3)},{"R3"}},
{{CONTROLLER, static_cast<int>(GPButtons::SELECT)},{"SELECT"}},
{{CONTROLLER, static_cast<int>(GPButtons::START)},{"START"}},
{{CONTROLLER, static_cast<int>(GPButtons::DPAD_L)},{"LEFT"}},
{{CONTROLLER, static_cast<int>(GPButtons::DPAD_R)},{"RIGHT"}},
{{CONTROLLER, static_cast<int>(GPButtons::DPAD_U)},{"UP"}},
{{CONTROLLER, static_cast<int>(GPButtons::DPAD_D)},{"DOWN"}},
{{ANALOG, static_cast<int>(GPAxes::LY)},{"Up/Down","themes/button_analogstick_vert.png"}},
{{ANALOG, static_cast<int>(GPAxes::RY)},{"Up/Down","themes/button_analogstick_vert.png"}},
{{ANALOG, static_cast<int>(GPAxes::LX)},{"Right/Left","themes/button_analogstick_horz.png"}},
{{ANALOG, static_cast<int>(GPAxes::RX)},{"Right/Left","themes/button_analogstick_horz.png"}},
{{ANALOG, static_cast<int>(GPAxes::TL)},{"Left Trigger","themes/button_l2.png"}},
{{ANALOG, static_cast<int>(GPAxes::TR)},{"Right Trigger","themes/button_r2.png"}},
{{ANALOG, static_cast<int>(GPAxes::DX)},{"Right/Left","themes/button_analogstick_horz.png"}},
{{ANALOG, static_cast<int>(GPAxes::DY)},{"Up/Down","themes/button_analogstick_vert.png"}},
}; };
void Input::SetUsingGamepad(const bool usingGamepad){ void Input::SetUsingGamepad(const bool usingGamepad){
@ -386,3 +427,40 @@ void Input::StopVibration(){
const bool operator<(const InputGroup&group1,const InputGroup&group2){ const bool operator<(const InputGroup&group1,const InputGroup&group2){
return &group1<&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::optional<Input>InputGroup::GetPrimaryKey(InputType type)const{
if(type==ANALOG)ERR("WARNING! Type cannot be ANALOG! Not supported!");
std::optional<Input>result;
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;
}

@ -39,6 +39,7 @@ All rights reserved.
#include <set> #include <set>
#include <string> #include <string>
#include <map> #include <map>
#include "olcPixelGameEngine.h"
//Future-proof game controller support. //Future-proof game controller support.
enum InputType{ enum InputType{
@ -61,7 +62,7 @@ public:
bool Held(); bool Held();
bool Released(); bool Released();
float Analog(); float Analog();
std::string GetDisplayName(); std::string GetDisplayName()const;
bool operator<(const Input&rhs)const{ bool operator<(const Input&rhs)const{
return type<rhs.type||(type==rhs.type&&key<rhs.key); return type<rhs.type||(type==rhs.type&&key<rhs.key);
} }
@ -69,10 +70,16 @@ public:
static const bool AxesActive(); static const bool AxesActive();
static void StartVibration(); static void StartVibration();
static void StopVibration(); static void StopVibration();
const InputType GetType()const;
const std::string str()const;
const bool HasIcon()const;
const Renderable&GetIcon()const;
friend const bool operator==(const Input&input1,const Input&input2);
}; };
class InputGroup{ class InputGroup{
std::set<Input>keys; std::set<Input>keys;
std::vector<Input>keyOrder; //A list of keys inserted in order, for primary key mapping.
public: public:
InputGroup(); InputGroup();
void AddKeybind(Input bind); void AddKeybind(Input bind);
@ -82,11 +89,36 @@ public:
const bool Released()const; const bool Released()const;
const float Analog()const; const float Analog()const;
std::string GetDisplayName(); std::string GetDisplayName();
const std::optional<Input>GetPrimaryKey(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{ class GenericKey{
struct KeyInfo{
std::string displayName;
std::string iconName;
};
public: public:
static std::map<std::pair<InputType,int>,std::string>keyLiteral; //The displayed text for a given key for a given input type. static std::map<std::pair<InputType,int>,KeyInfo>keyLiteral; //The displayed text for a given key for a given input type.
}; };
const bool operator<(const InputGroup&group1,const InputGroup&group2); const bool operator<(const InputGroup&group1,const InputGroup&group2);
const bool operator<(const InputEngageGroup&group1,const InputEngageGroup&group2);
using enum InputEngageGroup::EngageType;

@ -47,6 +47,20 @@ void Menu::InitializeLoadGameWindow(){
loadGameWindow->ADD("Game Files List",ScrollableWindowComponent)(geom2d::rect<float>{{-8,4},{112,116}})END; loadGameWindow->ADD("Game Files List",ScrollableWindowComponent)(geom2d::rect<float>{{-8,4},{112,116}})END;
loadGameWindow->ADD("Go Back Button",MenuComponent)(geom2d::rect<float>{{24,124},{48,12}},"Back",[](MenuFuncData menu){Menu::CloseMenu();return true;})END; loadGameWindow->ADD("Go Back Button",MenuComponent)(geom2d::rect<float>{{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<ScrollableWindowComponent>(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( loadGameWindow->SetupKeyboardNavigation(
[](MenuType type,Data&returnData){ //On Open [](MenuType type,Data&returnData){ //On Open
if(SaveFile::GetSaveFileCount()>0){ if(SaveFile::GetSaveFileCount()>0){
@ -56,18 +70,12 @@ void Menu::InitializeLoadGameWindow(){
} }
}, },
{ //Button Key { //Button Key
{game->KEY_BACK,{"Back to Title Screen",[](MenuType type){ {{game->KEY_BACK},{"Back to Title Screen",[](MenuType type){
Component<MenuComponent>(type,"Go Back Button")->Click(); Component<MenuComponent>(type,"Go Back Button")->Click();
}}}, }}},
{game->KEY_SCROLL,{"Scroll Up/Down",[](MenuType type){ {{game->KEY_SCROLL,Analog},{"Scroll",ScrollWindow(game->KEY_SCROLL.Analog())}},
auto scrollWindow=Component<ScrollableWindowComponent>(type,"Game Files List"); {{game->KEY_SCROLLUP,Held},{"Scroll Up",ScrollWindow(-1.0f)}},
float scrollAmt=game->KEY_SCROLL.Analog()*game->GetElapsedTime()*"Interface.AnalogScrollSpeed"_F; {{game->KEY_SCROLLDOWN,Held},{"Scroll Down",ScrollWindow(1.0f)}},
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);
}}},
} }
,{ //Button Navigation Rules ,{ //Button Navigation Rules
{"Game Files List",{ {"Game Files List",{

@ -281,6 +281,10 @@ void Menu::Draw(AiL*game){
draggingComponent->DrawDecal(window,this==Menu::stack.back()); draggingComponent->DrawDecal(window,this==Menu::stack.back());
} }
} }
if(Menu::stack.back()==this){
helpDisplay.Draw();
}
}; };
void Menu::OpenMenu(MenuType menu,bool cover){ void Menu::OpenMenu(MenuType menu,bool cover){
@ -297,7 +301,24 @@ void Menu::KeyboardButtonNavigation(AiL*game,vf2d menuPos){
std::weak_ptr<MenuComponent>prevSelection=selection; std::weak_ptr<MenuComponent>prevSelection=selection;
for(auto&[input,data]:inputGroups){ 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); SetMouseNavigation(false);
auto&action=data.second; auto&action=data.second;
if(std::holds_alternative<ButtonName>(action))Component<MenuComponent>(type,std::get<ButtonName>(action))->Click(); if(std::holds_alternative<ButtonName>(action))Component<MenuComponent>(type,std::get<ButtonName>(action))->Click();
@ -624,6 +645,7 @@ MenuFuncData::MenuFuncData(Menu&menu,AiL*const game,std::weak_ptr<MenuComponent>
void Menu::SetupKeyboardNavigation(MenuDataFunc onOpen,MenuInputGroups inputGroups,ButtonNavigationGroups navigationGroups){ void Menu::SetupKeyboardNavigation(MenuDataFunc onOpen,MenuInputGroups inputGroups,ButtonNavigationGroups navigationGroups){
this->onOpenFunc=onOpen; this->onOpenFunc=onOpen;
this->inputGroups=inputGroups; this->inputGroups=inputGroups;
helpDisplay.Initialize(inputGroups);
this->navigationGroups=navigationGroups; this->navigationGroups=navigationGroups;
} }

@ -43,7 +43,7 @@ All rights reserved.
#include "Attributable.h" #include "Attributable.h"
#include "olcUTIL_Geometry2D.h" #include "olcUTIL_Geometry2D.h"
#include "olcPGEX_ViewPort.h" #include "olcPGEX_ViewPort.h"
#include "Key.h" #include "InputHelper.h"
class AiL; class AiL;
class MenuComponent; class MenuComponent;
@ -70,41 +70,8 @@ using MenuFunc=std::function<bool(MenuFuncData)>;
#define DEFAULT_DEPTH -999999 #define DEFAULT_DEPTH -999999
#define STARTING_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<ButtonName,std::weak_ptr<MenuComponent>>; using Data=std::variant<ButtonName,std::weak_ptr<MenuComponent>>;
using MenuDataFunc=std::function<void(MenuType,Data&)>; using MenuDataFunc=std::function<void(MenuType,Data&)>;
using MenuInputGroups=std::map<InputGroup,std::pair<InputGroupActionDisplayName,std::variant<ButtonName,std::function<void(MenuType)>>>>;
using ButtonNavigationGroups=std::map<ButtonName,Navigation>; using ButtonNavigationGroups=std::map<ButtonName,Navigation>;
@ -253,6 +220,8 @@ private:
std::weak_ptr<MenuComponent>selection; std::weak_ptr<MenuComponent>selection;
std::weak_ptr<MenuComponent>keyboardSelection; std::weak_ptr<MenuComponent>keyboardSelection;
InputHelper helpDisplay;
static bool MOUSE_NAVIGATION; static bool MOUSE_NAVIGATION;
bool cover; //A black cover for when a menu pops up to fade out the stuff behind it. bool cover; //A black cover for when a menu pops up to fade out the stuff behind it.
}; };

@ -0,0 +1,67 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
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////////////////////////////////
///////////////////////////////////////////////////////////
};

@ -160,9 +160,9 @@ protected:
if(game->GetMouseWheel()!=0){ if(game->GetMouseWheel()!=0){
if(game->GetMouseWheel()>0){ if(game->GetMouseWheel()>0){
SetScrollAmount(GetScrollAmount()+vf2d{0,"ThemeGlobal.MenuScrollWheelSpeed"_F}); SetScrollAmount(GetTargetScrollAmount()+vf2d{0,"ThemeGlobal.MenuScrollWheelSpeed"_F});
}else{ }else{
SetScrollAmount(GetScrollAmount()-vf2d{0,"ThemeGlobal.MenuScrollWheelSpeed"_F}); SetScrollAmount(GetTargetScrollAmount()-vf2d{0,"ThemeGlobal.MenuScrollWheelSpeed"_F});
} }
} }

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 0 #define VERSION_MAJOR 0
#define VERSION_MINOR 3 #define VERSION_MINOR 3
#define VERSION_PATCH 0 #define VERSION_PATCH 0
#define VERSION_BUILD 6119 #define VERSION_BUILD 6155
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

@ -5,4 +5,13 @@ Interface
# The size of each side of the 9-patch menu sprite. # The size of each side of the 9-patch menu sprite.
9PatchSize = 24,24 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
} }

@ -50,6 +50,16 @@ Images
GFX_FrogTongueEnd = tongue_end.png GFX_FrogTongueEnd = tongue_end.png
GFX_AimingTarget = aiming_target.png GFX_AimingTarget = aiming_target.png
GFX_AimingLine = aiming_line.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 # Ability Icons
GFX_Warrior_BattleCry_Icon = Ability Icons/battlecry.png GFX_Warrior_BattleCry_Icon = Ability Icons/battlecry.png

@ -13,12 +13,6 @@ ThemeGlobal
# How far the mouse has to move before mouse mode becomes active from keyboard/controller input mode. # How far the mouse has to move before mouse mode becomes active from keyboard/controller input mode.
MouseActivationDistance = 8 MouseActivationDistance = 8
# The back color of input keys.
InputButtonBackCol = 15,8,47,255
# The text color of input keys.
InputButtonTextCol = 175,199,191
} }
Themes Themes

Loading…
Cancel
Save