The open source repository for the action RPG game in development by Sig Productions titled 'Adventures in Lestoria'!
https://forums.lestoria.net
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
842 lines
28 KiB
842 lines
28 KiB
#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 © 2024 The FreeType
|
|
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
|
All rights reserved.
|
|
*/
|
|
#pragma endregion
|
|
#include "Key.h"
|
|
#include "DEFINES.h"
|
|
#include "AdventuresInLestoria.h"
|
|
#include "olcPGEX_Gamepad.h"
|
|
#include "Menu.h"
|
|
#include "GameSettings.h"
|
|
|
|
INCLUDE_game
|
|
INCLUDE_GFX
|
|
|
|
bool Input::usingGamepad;
|
|
safemap<std::string,InputGroup*>InputGroup::menuNamesToInputGroups;
|
|
std::vector<std::string>InputGroup::menuInputGroups;
|
|
std::vector<std::string>InputGroup::gameplayInputGroups;
|
|
|
|
Input::Input(InputType type,int key)
|
|
:type(type),key(key){}
|
|
|
|
bool Input::Pressed(){
|
|
if(!game->IsFocused())return false;
|
|
bool inputPressed=false;
|
|
switch(type){
|
|
case KEY:{
|
|
inputPressed=game->GetKey(Key(key)).bPressed;
|
|
}break;
|
|
case MOUSE:{
|
|
inputPressed=game->GetMouse(key).bPressed;
|
|
}break;
|
|
case CONTROLLER:{
|
|
for(GamePad*gamepad:GamePad::getGamepads()){
|
|
if(gamepad->stillConnected&&gamepad->getButton(static_cast<GPButtons>(key)).bPressed)inputPressed=true;
|
|
}
|
|
}break;
|
|
case ANALOG:{
|
|
//An analog input can never be "pressed". No-op.
|
|
}break;
|
|
default:{
|
|
ERR("Invalid Control Scheme detected! We shouldn't be here!! Type is "<<type);
|
|
}
|
|
}
|
|
if(inputPressed){
|
|
usingGamepad=type==CONTROLLER;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Input::Held(){
|
|
if(!game->IsFocused())return false;
|
|
bool inputHeld=false;
|
|
switch(type){
|
|
case KEY:{
|
|
inputHeld=game->GetKey(Key(key)).bHeld;
|
|
}break;
|
|
case MOUSE:{
|
|
inputHeld=game->GetMouse(key).bHeld;
|
|
}break;
|
|
case CONTROLLER:{
|
|
for(GamePad*gamepad:GamePad::getGamepads()){
|
|
if(gamepad->stillConnected&&gamepad->getButton(static_cast<GPButtons>(key)).bHeld)inputHeld=true;
|
|
}
|
|
}break;
|
|
case ANALOG:{
|
|
//An analog input can never be "held". No-op.
|
|
}break;
|
|
default:{
|
|
ERR("Invalid Control Scheme detected! We shouldn't be here!! Type is "<<type);
|
|
}
|
|
}
|
|
if(inputHeld){
|
|
usingGamepad=type==CONTROLLER;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Input::Released(){
|
|
if(!game->IsFocused())return false;
|
|
bool inputReleased=false;
|
|
switch(type){
|
|
case KEY:{
|
|
inputReleased=game->GetKey(Key(key)).bReleased;
|
|
}break;
|
|
case MOUSE:{
|
|
inputReleased=game->GetMouse(key).bReleased;
|
|
}break;
|
|
case CONTROLLER:{
|
|
for(GamePad*gamepad:GamePad::getGamepads()){
|
|
if(gamepad->stillConnected&&gamepad->getButton(static_cast<GPButtons>(key)).bReleased)inputReleased=true;
|
|
}
|
|
}break;
|
|
case ANALOG:{
|
|
//An analog input can never be "released". No-op.
|
|
}break;
|
|
default:{
|
|
ERR("Invalid Control Scheme detected! We shouldn't be here!! Type is "<<type);
|
|
}
|
|
}
|
|
if(inputReleased){
|
|
usingGamepad=type==CONTROLLER;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
float Input::Analog(){
|
|
if(!game->IsFocused())return false;
|
|
switch(type){
|
|
case ANALOG:{
|
|
for(GamePad*gamepad:GamePad::getGamepads()){
|
|
if(gamepad->stillConnected){
|
|
float axisVal=gamepad->getAxis(static_cast<GPAxes>(key));
|
|
if(axisVal!=0.f){
|
|
usingGamepad=true;
|
|
return axisVal;
|
|
}
|
|
}
|
|
}
|
|
}break;
|
|
case KEY:
|
|
case MOUSE:
|
|
case CONTROLLER:{
|
|
//Doesn't return analog inputs. No-op.
|
|
}break;
|
|
default:{
|
|
ERR("Invalid Control Scheme detected for analog controls! We shouldn't be here!! Type is "<<type);
|
|
}
|
|
}
|
|
return 0.f;
|
|
}
|
|
|
|
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::ClearAllKeybinds(){
|
|
keys.clear();
|
|
keyOrder.clear();
|
|
}
|
|
|
|
void InputGroup::RemoveKeybind(Input 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{
|
|
for(Input input:keys){
|
|
if(input.Pressed())return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const bool InputGroup::PressedDAS(){
|
|
for(Input input:keys){
|
|
if(input.Pressed()){
|
|
if(initialHoldDownTime==0.f){
|
|
initialHoldDownTime="Interface.InitialScrollDelay"_F;
|
|
}
|
|
return true;
|
|
}else
|
|
if(input.Held()&&initialHoldDownTime>0.f){
|
|
initialHoldDownTime-=game->GetElapsedTime();
|
|
if(initialHoldDownTime<=0.f){
|
|
holdDownTime="Interface.ScrollDelay"_F;
|
|
return true;
|
|
}
|
|
}else
|
|
if(input.Held()&&holdDownTime>0.f){
|
|
holdDownTime-=game->GetElapsedTime();
|
|
if(holdDownTime<=0.f){
|
|
holdDownTime="Interface.ScrollDelay"_F;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const bool InputGroup::Held()const{
|
|
for(Input input:keys){
|
|
if(input.Held())return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const bool InputGroup::Released(){
|
|
for(Input input:keys){
|
|
if(input.Released()){
|
|
initialHoldDownTime=holdDownTime=0.f; //Reset hold times if we release the key.
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const float InputGroup::Analog()const{
|
|
for(Input input:keys){
|
|
float analogVal=input.Analog();
|
|
if(analogVal!=0.f)return analogVal;
|
|
}
|
|
return 0.f;
|
|
}
|
|
|
|
const float InputGroup::AnalogDAS(const float threshold){
|
|
for(Input input:keys){
|
|
float analogVal=input.Analog();
|
|
if(abs(analogVal)>=threshold&&initialHoldDownTime==0.f){
|
|
initialHoldDownTime="Interface.InitialScrollDelay"_F;
|
|
return analogVal;
|
|
}else
|
|
if(abs(analogVal)>=threshold&&initialHoldDownTime>0.f){
|
|
initialHoldDownTime-=game->GetElapsedTime();
|
|
if(initialHoldDownTime<=0.f){
|
|
holdDownTime="Interface.ScrollDelay"_F;
|
|
return analogVal;
|
|
}
|
|
return 0.f;
|
|
}else
|
|
if(abs(analogVal)>=threshold&&holdDownTime>0.f){
|
|
holdDownTime-=game->GetElapsedTime();
|
|
if(holdDownTime<=0.f){
|
|
holdDownTime="Interface.ScrollDelay"_F;
|
|
return analogVal;
|
|
}
|
|
return 0.f;
|
|
}
|
|
}
|
|
initialHoldDownTime=holdDownTime=0.f;
|
|
return 0.f;
|
|
}
|
|
|
|
std::string InputGroup::GetDisplayName(){
|
|
std::string combinationDisplay="";
|
|
for(Input input:keys){
|
|
if(combinationDisplay.length()>0){
|
|
combinationDisplay+=",";
|
|
}
|
|
combinationDisplay+=input.GetDisplayName();
|
|
}
|
|
return combinationDisplay;
|
|
}
|
|
|
|
void InputGroup::DrawInput(const std::variant<AiL*const,TileTransformedView*const,ViewPort*const>renderer,const vf2d pos,const std::string_view displayText,const uint8_t alpha,InputType type,vf2d textScale)const{
|
|
std::vector<Input>displayKeys;
|
|
switch(type){
|
|
case CONTROLLER:std::copy_if(keys.begin(),keys.end(),std::back_inserter(displayKeys),[](const Input&input){return input.GetType()==CONTROLLER;});break;
|
|
case MOUSE:std::copy_if(keys.begin(),keys.end(),std::back_inserter(displayKeys),[](const Input&input){return input.GetType()==KEY||input.GetType()==MOUSE;});break;
|
|
default:std::copy_if(keys.begin(),keys.end(),std::back_inserter(displayKeys),[](const Input&input){return input.GetType()==KEY||input.GetType()==MOUSE;});break;
|
|
}
|
|
|
|
vf2d buttonImgSize{};
|
|
std::vector<std::variant<Decal*,std::string>>buttonImgs;
|
|
|
|
for(const Input&input:displayKeys){
|
|
if(input.HasExtendedIcons()){
|
|
buttonImgSize.x+=input.GetIcon(GameSettings::GetIconType()).Sprite()->width*textScale.x+"Interface.InputHelperSpacing"_F;
|
|
buttonImgSize.y=std::max(buttonImgSize.y,float(input.GetIcon(GameSettings::GetIconType()).Sprite()->height));
|
|
buttonImgs.push_back(input.GetIcon(GameSettings::GetIconType()).Decal());
|
|
}else
|
|
if(input.HasIcon()){
|
|
buttonImgSize.x+=input.GetIcon().Sprite()->width*textScale.x+"Interface.InputHelperSpacing"_F;
|
|
buttonImgSize.y=std::max(buttonImgSize.y,float(input.GetIcon().Sprite()->height));
|
|
buttonImgs.push_back(input.GetIcon().Decal());
|
|
}else{
|
|
buttonImgSize.x+=game->GetTextSizeProp(input.GetDisplayName()).x*textScale.x+"Interface.InputHelperSpacing"_F;
|
|
buttonImgSize.y=std::max(buttonImgSize.y,float(game->GetTextSizeProp(input.GetDisplayName()).y)+"Interface.InputHelperSpacing"_F);
|
|
buttonImgs.push_back(input.GetDisplayName());
|
|
}
|
|
}
|
|
|
|
vf2d descriptionTextSize=game->GetTextSizeProp(displayText);
|
|
vf2d offset=-((buttonImgSize+descriptionTextSize)/2.f);
|
|
for(auto&button:buttonImgs){
|
|
if(std::holds_alternative<Decal*>(button)){
|
|
Decal*img=std::get<Decal*>(button);
|
|
|
|
#pragma region Render Macro
|
|
#define Render(rendererType) \
|
|
std::get<rendererType*const>(renderer)->DrawDecal(pos+offset-vf2d{0.f,2.f},img,textScale,{255,255,255,alpha});
|
|
#pragma endregion
|
|
if(std::holds_alternative<AiL*const>(renderer)){
|
|
Render(AiL);
|
|
}else
|
|
if(std::holds_alternative<TileTransformedView*const>(renderer)){
|
|
Render(TileTransformedView);
|
|
}else
|
|
if(std::holds_alternative<ViewPort*const>(renderer)){
|
|
Render(ViewPort);
|
|
}else ERR("Could not find proper renderer for rendering Inputs!");
|
|
offset.x+=img->sprite->width*textScale.x+"Interface.InputHelperSpacing"_I;
|
|
}else
|
|
if(std::holds_alternative<std::string>(button)){
|
|
std::string label=std::get<std::string>(button);
|
|
vf2d textSize=game->GetTextSizeProp(label)*textScale;
|
|
Pixel buttonBackCol="Interface.InputButtonBackCol"_Pixel;
|
|
Pixel buttonTextCol="Interface.InputButtonTextCol"_Pixel;
|
|
buttonBackCol.a=alpha;
|
|
buttonTextCol.a=alpha;
|
|
|
|
#pragma region Render Macro
|
|
#define Render(rendererType) \
|
|
std::get<rendererType*const>(renderer)->FillRectDecal(pos+offset+vf2d{-2.f,0.f},vf2d{textSize.x+4,textSize.y},buttonBackCol); \
|
|
std::get<rendererType*const>(renderer)->FillRectDecal(pos+offset+vf2d{-1.f,-1.f},vf2d{textSize.x+2,textSize.y},buttonBackCol); \
|
|
std::get<rendererType*const>(renderer)->FillRectDecal(pos+offset+vf2d{-1.f,0.f},vf2d{textSize.x+2,textSize.y+1.f},buttonBackCol); \
|
|
std::get<rendererType*const>(renderer)->DrawStringPropDecal(pos+offset+vf2d{0.f,0.f},label,buttonTextCol,textScale);
|
|
#pragma endregion
|
|
|
|
if(std::holds_alternative<AiL*const>(renderer)){
|
|
Render(AiL);
|
|
}else
|
|
if(std::holds_alternative<TileTransformedView*const>(renderer)){
|
|
Render(TileTransformedView);
|
|
}else
|
|
if(std::holds_alternative<ViewPort*const>(renderer)){
|
|
Render(ViewPort);
|
|
}else ERR("Could not find proper renderer for rendering Inputs!");
|
|
offset.x+=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!");
|
|
}
|
|
|
|
#pragma region Render Display Text
|
|
#pragma region Render Macro
|
|
#define Render(rendererType) \
|
|
std::get<rendererType*const>(renderer)->DrawShadowStringPropDecal(pos+offset,displayText,{255,255,255,alpha},{0,0,0,alpha},textScale);
|
|
#pragma endregion
|
|
|
|
if(std::holds_alternative<AiL*const>(renderer)){
|
|
Render(AiL);
|
|
}else
|
|
if(std::holds_alternative<TileTransformedView*const>(renderer)){
|
|
Render(TileTransformedView);
|
|
}else
|
|
if(std::holds_alternative<ViewPort*const>(renderer)){
|
|
Render(ViewPort);
|
|
}else ERR("Could not find proper renderer for rendering Inputs!");
|
|
#pragma endregion
|
|
}
|
|
|
|
void InputGroup::DrawPrimaryInput(const std::variant<AiL*const,TileTransformedView*const,ViewPort*const>renderer,const vf2d pos,const std::string_view displayText,const uint8_t alpha,InputType type,vf2d textScale)const{
|
|
std::optional<Input>primaryKey;
|
|
switch(type){
|
|
case CONTROLLER:primaryKey=GetPrimaryKey(CONTROLLER);break;
|
|
case MOUSE:primaryKey=GetPrimaryKey(MOUSE);break;
|
|
default:primaryKey=GetPrimaryKey(KEY);break;
|
|
}
|
|
|
|
vf2d buttonImgSize{};
|
|
std::vector<std::variant<Decal*,std::string>>buttonImgs;
|
|
|
|
if(type!=CONTROLLER&&type!=MOUSE){
|
|
for(const Input&input:keyOrder){
|
|
if(input.GetType()==MOUSE){
|
|
if(input.HasExtendedIcons()){
|
|
buttonImgSize.x+=input.GetIcon(GameSettings::GetIconType()).Sprite()->width*textScale.x+"Interface.InputHelperSpacing"_F;
|
|
buttonImgSize.y=std::max(buttonImgSize.y,float(input.GetIcon(GameSettings::GetIconType()).Sprite()->height));
|
|
buttonImgs.push_back(input.GetIcon(GameSettings::GetIconType()).Decal());
|
|
}else
|
|
if(input.HasIcon()){
|
|
buttonImgSize.x+=input.GetIcon().Sprite()->width*textScale.x+"Interface.InputHelperSpacing"_F;
|
|
buttonImgSize.y=std::max(buttonImgSize.y,float(input.GetIcon().Sprite()->height));
|
|
buttonImgs.push_back(input.GetIcon().Decal());
|
|
}else{
|
|
buttonImgSize.x+=game->GetTextSizeProp(input.GetDisplayName()).x*textScale.x+"Interface.InputHelperSpacing"_F;
|
|
buttonImgSize.y=std::max(buttonImgSize.y,float(game->GetTextSizeProp(input.GetDisplayName()).y)+"Interface.InputHelperSpacing"_F);
|
|
buttonImgs.push_back(input.GetDisplayName());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(primaryKey.has_value()){
|
|
if(primaryKey.value().HasExtendedIcons()){
|
|
buttonImgSize.x+=primaryKey.value().GetIcon(GameSettings::GetIconType()).Sprite()->width*textScale.x+"Interface.InputHelperSpacing"_F;
|
|
buttonImgSize.y=std::max(buttonImgSize.y,float(primaryKey.value().GetIcon(GameSettings::GetIconType()).Sprite()->height));
|
|
buttonImgs.push_back(primaryKey.value().GetIcon(GameSettings::GetIconType()).Decal());
|
|
}else
|
|
if(primaryKey.value().HasIcon()){
|
|
buttonImgSize.x+=primaryKey.value().GetIcon().Sprite()->width*textScale.x+"Interface.InputHelperSpacing"_F;
|
|
buttonImgSize.y=std::max(buttonImgSize.y,float(primaryKey.value().GetIcon().Sprite()->height));
|
|
buttonImgs.push_back(primaryKey.value().GetIcon().Decal());
|
|
}else{
|
|
buttonImgSize.x+=game->GetTextSizeProp(primaryKey.value().GetDisplayName()).x*textScale.x+"Interface.InputHelperSpacing"_F;
|
|
buttonImgSize.y=std::max(buttonImgSize.y,float(game->GetTextSizeProp(primaryKey.value().GetDisplayName()).y)+"Interface.InputHelperSpacing"_F);
|
|
buttonImgs.push_back(primaryKey.value().GetDisplayName());
|
|
}
|
|
}
|
|
|
|
vf2d descriptionTextSize=game->GetTextSizeProp(displayText);
|
|
vf2d offset=-((buttonImgSize+descriptionTextSize)/2.f);
|
|
for(auto&button:buttonImgs){
|
|
if(std::holds_alternative<Decal*>(button)){
|
|
Decal*img=std::get<Decal*>(button);
|
|
|
|
#pragma region Render Macro
|
|
#define Render(rendererType) \
|
|
std::get<rendererType*const>(renderer)->DrawDecal(pos+offset-vf2d{0.f,2.f},img,textScale,{255,255,255,alpha});
|
|
#pragma endregion
|
|
if(std::holds_alternative<AiL*const>(renderer)){
|
|
Render(AiL);
|
|
}else
|
|
if(std::holds_alternative<TileTransformedView*const>(renderer)){
|
|
Render(TileTransformedView);
|
|
}else
|
|
if(std::holds_alternative<ViewPort*const>(renderer)){
|
|
Render(ViewPort);
|
|
}else ERR("Could not find proper renderer for rendering Inputs!");
|
|
offset.x+=img->sprite->width*textScale.x+"Interface.InputHelperSpacing"_I;
|
|
}else
|
|
if(std::holds_alternative<std::string>(button)){
|
|
std::string label=std::get<std::string>(button);
|
|
vf2d textSize=game->GetTextSizeProp(label)*textScale;
|
|
Pixel buttonBackCol="Interface.InputButtonBackCol"_Pixel;
|
|
Pixel buttonTextCol="Interface.InputButtonTextCol"_Pixel;
|
|
buttonBackCol.a=alpha;
|
|
buttonTextCol.a=alpha;
|
|
|
|
#pragma region Render Macro
|
|
#define Render(rendererType) \
|
|
std::get<rendererType*const>(renderer)->FillRectDecal(pos+offset+vf2d{-2.f,0.f},vf2d{textSize.x+4,textSize.y},buttonBackCol); \
|
|
std::get<rendererType*const>(renderer)->FillRectDecal(pos+offset+vf2d{-1.f,-1.f},vf2d{textSize.x+2,textSize.y},buttonBackCol); \
|
|
std::get<rendererType*const>(renderer)->FillRectDecal(pos+offset+vf2d{-1.f,0.f},vf2d{textSize.x+2,textSize.y+1.f},buttonBackCol); \
|
|
std::get<rendererType*const>(renderer)->DrawStringPropDecal(pos+offset+vf2d{0.f,0.f},label,buttonTextCol,textScale);
|
|
#pragma endregion
|
|
|
|
if(std::holds_alternative<AiL*const>(renderer)){
|
|
Render(AiL);
|
|
}else
|
|
if(std::holds_alternative<TileTransformedView*const>(renderer)){
|
|
Render(TileTransformedView);
|
|
}else
|
|
if(std::holds_alternative<ViewPort*const>(renderer)){
|
|
Render(ViewPort);
|
|
}else ERR("Could not find proper renderer for rendering Inputs!");
|
|
offset.x+=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!");
|
|
}
|
|
|
|
#pragma region Render Display Text
|
|
#pragma region Render Macro
|
|
#define Render(rendererType) \
|
|
std::get<rendererType*const>(renderer)->DrawShadowStringPropDecal(pos+offset,displayText,{255,255,255,alpha},{0,0,0,alpha},textScale);
|
|
#pragma endregion
|
|
|
|
if(std::holds_alternative<AiL*const>(renderer)){
|
|
Render(AiL);
|
|
}else
|
|
if(std::holds_alternative<TileTransformedView*const>(renderer)){
|
|
Render(TileTransformedView);
|
|
}else
|
|
if(std::holds_alternative<ViewPort*const>(renderer)){
|
|
Render(ViewPort);
|
|
}else ERR("Could not find proper renderer for rendering Inputs!");
|
|
#pragma endregion
|
|
}
|
|
|
|
void InputGroup::DrawPrimaryInput(const std::variant<AiL*const,TileTransformedView*const,ViewPort*const>renderer,const vf2d pos,const std::string_view displayText,const uint8_t alpha)const{
|
|
InputType primaryType;
|
|
if(Input::UsingGamepad())primaryType=CONTROLLER;
|
|
else if(Menu::UsingMouseNavigation())primaryType=MOUSE;
|
|
else primaryType=KEY;
|
|
DrawPrimaryInput(renderer,pos,displayText,alpha,primaryType);
|
|
}
|
|
|
|
const bool Input::HasIcon()const{
|
|
return GenericKey::keyLiteral.at({type,key}).iconName.length()>0;
|
|
}
|
|
const bool Input::HasExtendedIcons()const{
|
|
return GenericKey::keyLiteral.at({type,key}).iconName.length()>0&&GenericKey::keyLiteral.at({type,key}).iconName2.length()>0&&GenericKey::keyLiteral.at({type,key}).iconName3.length()>0;
|
|
}
|
|
const Renderable&Input::GetIcon()const{
|
|
return GFX.at(GenericKey::keyLiteral.at({type,key}).iconName);
|
|
}
|
|
const Renderable&Input::GetIcon(IconType type)const{
|
|
switch(type){
|
|
case IconType::XB:{
|
|
return GFX.at(GenericKey::keyLiteral.at({this->type,key}).iconName);
|
|
}break;
|
|
case IconType::PS:{
|
|
return GFX.at(GenericKey::keyLiteral.at({this->type,key}).iconName2);
|
|
}break;
|
|
case IconType::XB_REVERSED:{
|
|
return GFX.at(GenericKey::keyLiteral.at({this->type,key}).iconName3);
|
|
}break;
|
|
}
|
|
ERR("WARNING! Did not handle a valid controller icon type. THIS SHOULD NOT BE HAPPENING!");
|
|
return GFX.at("themes/button_d_xb.png");
|
|
}
|
|
|
|
#undef END
|
|
|
|
std::map<std::pair<InputType,int>,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},{"K"}},
|
|
{{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, 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},{""}},
|
|
{{KEY, SHOULDER},{"Q-E","themes/button_qe.png"}},
|
|
{{KEY, ARROWS},{"Arrow Keys","themes/button_arrows.png"}},
|
|
{{MOUSE, Mouse::LEFT},{"L.MOUSE","themes/lmb.png"}},
|
|
{{MOUSE, Mouse::RIGHT},{"R.MOUSE","themes/rmb.png"}},
|
|
{{MOUSE, Mouse::MIDDLE},{"M.MOUSE","themes/mmb.png"}},
|
|
{{CONTROLLER, static_cast<int>(GPButtons::FACE_D)},{"A/X","themes/button_d_xb.png","themes/button_d_ps.png","themes/button_r_xb.png"}},
|
|
{{CONTROLLER, static_cast<int>(GPButtons::FACE_L)},{"X/Square","themes/button_l_xb.png","themes/button_l_ps.png","themes/button_u_xb.png"}},
|
|
{{CONTROLLER, static_cast<int>(GPButtons::FACE_R)},{"B/Circle","themes/button_r_xb.png","themes/button_r_ps.png","themes/button_d_xb.png"}},
|
|
{{CONTROLLER, static_cast<int>(GPButtons::FACE_U)},{"Y/Triangle","themes/button_u_xb.png","themes/button_u_ps.png","themes/button_l_xb.png"}},
|
|
{{CONTROLLER, static_cast<int>(GPButtons::L1)},{"L1","themes/button_l1.png"}},
|
|
{{CONTROLLER, static_cast<int>(GPButtons::L2)},{"L2","themes/button_l2.png"}},
|
|
{{CONTROLLER, static_cast<int>(GPButtons::L3)},{"L3"}},
|
|
{{CONTROLLER, static_cast<int>(GPButtons::R1)},{"R1","themes/button_r1.png"}},
|
|
{{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"}},
|
|
{{CONTROLLER, static_cast<int>(GPButtons::SHOULDER)},{"L1-R1","themes/button_r1l1.png"}},
|
|
{{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"}},
|
|
{{ANALOG, static_cast<int>(GPAxes::ALL)},{"Analog Stick","themes/button_analogstick.png"}},
|
|
};
|
|
|
|
void Input::SetUsingGamepad(const bool usingGamepad){
|
|
Input::usingGamepad=usingGamepad;
|
|
}
|
|
|
|
const bool Input::UsingGamepad(){
|
|
return usingGamepad;
|
|
}
|
|
|
|
const bool Input::AxesActive(){
|
|
float xAxis=0.f,yAxis=0.f;
|
|
for(GamePad*gamepad:GamePad::getGamepads()){
|
|
if(gamepad->stillConnected){
|
|
if(fabs(gamepad->getAxis(GPAxes::RX))>0.f){
|
|
xAxis=gamepad->getAxis(GPAxes::RX);
|
|
}
|
|
|
|
if(fabs(gamepad->getAxis(GPAxes::RY))>0.f){
|
|
yAxis=gamepad->getAxis(GPAxes::RY);
|
|
}
|
|
|
|
if(xAxis!=0.f||yAxis!=0.f)break; //Found a controller, so we're good to break.
|
|
}
|
|
}
|
|
return xAxis!=0.f||yAxis!=0.f;
|
|
}
|
|
|
|
void Input::StartVibration(const bool override){
|
|
if(!GameSettings::RumbleEnabled(override))return;
|
|
if(UsingGamepad()){
|
|
for(GamePad*gamepad:GamePad::getGamepads()){
|
|
if(gamepad->stillConnected){
|
|
gamepad->startVibration();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void Input::StopVibration(){
|
|
for(GamePad*gamepad:GamePad::getGamepads()){
|
|
if(gamepad->stillConnected){
|
|
gamepad->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::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,const bool labelVisible)
|
|
:group(group),type(type),labelVisible(labelVisible){}
|
|
|
|
|
|
const InputEngageGroup::EngageType InputEngageGroup::GetEngageType()const{
|
|
return type;
|
|
}
|
|
|
|
InputGroup&InputEngageGroup::GetGroup()const{
|
|
return group;
|
|
}
|
|
|
|
const InputEngageGroup InputEngageGroup::operator=(const InputEngageGroup&rhs){
|
|
return InputEngageGroup{rhs.group,rhs.type};
|
|
}
|
|
|
|
void InputGroup::RemovePrimaryKeybind(InputType type){
|
|
auto primaryInputIt=std::find_if(keyOrder.begin(),keyOrder.end(),[&](Input&input){return input.GetType()==type;});
|
|
|
|
if(primaryInputIt!=keyOrder.end()){
|
|
Input&primaryInput=*primaryInputIt;
|
|
keys.erase(primaryInput);
|
|
keyOrder.erase(primaryInputIt);
|
|
}else ERR(std::format("WARNING! Could not find a valid primary input of type {}! THIS SHOULD NOT BE HAPPENING!",int(type)));
|
|
}
|
|
void InputGroup::AddPrimaryKeybind(Input key){
|
|
keys.insert(key);
|
|
keyOrder.insert(keyOrder.begin(),key);
|
|
}
|
|
void InputGroup::SetNewPrimaryKeybind(Input key){
|
|
RemovePrimaryKeybind(key.GetType());
|
|
AddPrimaryKeybind(key);
|
|
}
|
|
|
|
void InputListener::Update(){
|
|
#pragma region New controller input binding listener
|
|
using A=Attribute;
|
|
if(Menu::IsMenuOpen()&&Menu::stack.back()==Menu::menus[NEW_INPUT]){
|
|
if(!Menu::menus[NEW_INPUT]->B(A::IS_KEYBOARD)){//We are requesting a brand new controller input.
|
|
if(InputGroup::menuNamesToInputGroups.count(Menu::menus[NEW_INPUT]->S(A::KEYBIND))){
|
|
for(GamePad*gamepad:GamePad::getGamepads()){
|
|
for(size_t button=0;bool hasButton:gamepad->availableButtons){
|
|
GPButtons releasedButton=olc::GPButtons(button);
|
|
if(gamepad->getButton(releasedButton).bReleased){
|
|
InputGroup::menuNamesToInputGroups[Menu::menus[NEW_INPUT]->S(A::KEYBIND)]->SetNewPrimaryKeybind(Input{CONTROLLER,int(button)});
|
|
for(auto&[menuType,menu]:Menu::menus){
|
|
menu->helpDisplay.Initialize(menu->inputGroups);
|
|
}
|
|
Menu::alreadyClicked=true;
|
|
Menu::CloseMenu();
|
|
return;
|
|
}
|
|
button++;
|
|
}
|
|
}
|
|
}
|
|
}else{
|
|
for(GamePad*gamepad:GamePad::getGamepads()){
|
|
for(size_t button=0;bool hasButton:gamepad->availableButtons){
|
|
GPButtons releasedButton=olc::GPButtons(button);
|
|
if(gamepad->getButton(releasedButton).bReleased){
|
|
Menu::alreadyClicked=true;
|
|
Menu::CloseMenu();
|
|
return;
|
|
}
|
|
button++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#pragma endregion
|
|
}
|
|
|
|
const int Input::GetKeyCode()const{
|
|
return key;
|
|
}
|
|
|
|
const bool InputEngageGroup::GetLabelVisible()const{
|
|
return labelVisible;
|
|
} |