Add unequip hotkey to character equipment menu. Implemented mouse inputs displaying on input helper when mouse navigation is used. Release Build 7465.

pull/35/head
sigonasr2 9 months ago
parent e2cc0aa90c
commit 63f7062841
  1. 3
      Adventures in Lestoria/AdventuresInLestoria.cpp
  2. 2
      Adventures in Lestoria/AdventuresInLestoria.h
  3. 36
      Adventures in Lestoria/CharacterMenuWindow.cpp
  4. 73
      Adventures in Lestoria/InputHelper.cpp
  5. 1
      Adventures in Lestoria/InputHelper.h
  6. 59
      Adventures in Lestoria/Menu.cpp
  7. 4
      Adventures in Lestoria/Monster.cpp
  8. 6
      Adventures in Lestoria/TODO.txt
  9. 2
      Adventures in Lestoria/Version.h
  10. 5
      Adventures in Lestoria/assets/config/audio/events.txt
  11. BIN
      x64/Release/Adventures in Lestoria.exe

@ -136,6 +136,7 @@ InputGroup AiL::KEY_SCROLLVERT_L;
InputGroup AiL::KEY_SCROLL; InputGroup AiL::KEY_SCROLL;
InputGroup AiL::KEY_SHOULDER; InputGroup AiL::KEY_SHOULDER;
InputGroup AiL::KEY_CHANGE_LOADOUT; InputGroup AiL::KEY_CHANGE_LOADOUT;
InputGroup AiL::KEY_MOUSE_RIGHT;
#ifndef __EMSCRIPTEN__ #ifndef __EMSCRIPTEN__
::discord::Core*Discord{}; ::discord::Core*Discord{};
@ -2607,6 +2608,8 @@ void AiL::InitializeDefaultKeybinds(){
KEY_UNEQUIP.AddKeybind({KEY,R}); KEY_UNEQUIP.AddKeybind({KEY,R});
KEY_UNEQUIP.AddKeybind({CONTROLLER,static_cast<int>(GPButtons::FACE_U)}); KEY_UNEQUIP.AddKeybind({CONTROLLER,static_cast<int>(GPButtons::FACE_U)});
KEY_MOUSE_RIGHT.AddKeybind({MOUSE,Mouse::RIGHT});
#define TieMenuNameToMenuInputGroup(KEY_NAME) \ #define TieMenuNameToMenuInputGroup(KEY_NAME) \
InputGroup::menuNamesToInputGroups[DATA["Inputs"][#KEY_NAME].GetString()]=&KEY_NAME; \ InputGroup::menuNamesToInputGroups[DATA["Inputs"][#KEY_NAME].GetString()]=&KEY_NAME; \
InputGroup::menuInputGroups.push_back(DATA["Inputs"][#KEY_NAME].GetString()); InputGroup::menuInputGroups.push_back(DATA["Inputs"][#KEY_NAME].GetString());

@ -108,6 +108,8 @@ public:
static InputGroup KEY_CHANGE_LOADOUT; static InputGroup KEY_CHANGE_LOADOUT;
static InputGroup KEY_ENTER; static InputGroup KEY_ENTER;
static InputGroup KEY_MOUSE_RIGHT;
static float SIZE_CHANGE_SPEED; static float SIZE_CHANGE_SPEED;
double levelTime; double levelTime;
Camera2D camera; Camera2D camera;

@ -305,7 +305,7 @@ void Menu::InitializeCharacterMenuWindow(){
yOffset+=20; yOffset+=20;
} }
characterMenuWindow->ADD("Back button",MenuComponent)(geom2d::rect<float>{{windowSize.x/2-64,windowSize.y},{128,12}},"Back",[](MenuFuncData data){Menu::stack.pop_back();return true;})END; characterMenuWindow->ADD("Back button",MenuComponent)(geom2d::rect<float>{{windowSize.x/2-64,windowSize.y-4.f},{128,12}},"Back",[](MenuFuncData data){Menu::stack.pop_back();return true;})END;
auto itemNameDisplay=characterMenuWindow->ADD("Item Name",MenuLabel)(geom2d::rect<float>{{0,28},{120,12}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END; auto itemNameDisplay=characterMenuWindow->ADD("Item Name",MenuLabel)(geom2d::rect<float>{{0,28},{120,12}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END;
auto itemDescriptionDisplay=characterMenuWindow->ADD("Item Description",MenuLabel)(geom2d::rect<float>{{0,40},{120,windowSize.y-49}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW)END; auto itemDescriptionDisplay=characterMenuWindow->ADD("Item Description",MenuLabel)(geom2d::rect<float>{{0,40},{120,windowSize.y-49}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW)END;
@ -324,6 +324,40 @@ void Menu::InitializeCharacterMenuWindow(){
{ //Button Key { //Button Key
{{game->KEY_SHOULDER,Pressed},{"Scroll",[](MenuType type){}}}, {{game->KEY_SHOULDER,Pressed},{"Scroll",[](MenuType type){}}},
{{game->KEY_SCROLL,Analog},{"Scroll",[](MenuType type){}}}, {{game->KEY_SCROLL,Analog},{"Scroll",[](MenuType type){}}},
{{game->KEY_MOUSE_RIGHT,Pressed},{[](MenuFuncData data){
if(!data.menu.GetSelection().expired()&&
data.menu.GetSelection().lock()->GetName().starts_with("Equip Slot ")){
if(!ISBLANK(Inventory::GetEquip(EquipSlot(data.menu.GetSelection().lock()->I(Attribute::EQUIP_TYPE))))){
return "Unequip";
}
}
return "";
},[](MenuType type){
if(!Menu::menus[type]->GetSelection().expired()&&
Menu::menus[type]->GetSelection().lock()->GetName().starts_with("Equip Slot ")){
if(!ISBLANK(Inventory::GetEquip(EquipSlot(Menu::menus[type]->GetSelection().lock()->I(Attribute::EQUIP_TYPE))))){
Inventory::UnequipItem(EquipSlot(Menu::menus[type]->GetSelection().lock()->I(Attribute::EQUIP_TYPE)));
SoundEffect::PlaySFX("Unequip Armor",SoundEffect::CENTERED);
}
}
}}},
{{game->KEY_FACELEFT,Pressed},{[](MenuFuncData data){
if(!data.menu.GetSelection().expired()&&
data.menu.GetSelection().lock()->GetName().starts_with("Equip Slot ")){
if(!ISBLANK(Inventory::GetEquip(EquipSlot(data.menu.GetSelection().lock()->I(Attribute::EQUIP_TYPE))))){
return "Unequip";
}
}
return "";
},[](MenuType type){
if(!Menu::menus[type]->GetSelection().expired()&&
Menu::menus[type]->GetSelection().lock()->GetName().starts_with("Equip Slot ")){
if(!ISBLANK(Inventory::GetEquip(EquipSlot(Menu::menus[type]->GetSelection().lock()->I(Attribute::EQUIP_TYPE))))){
Inventory::UnequipItem(EquipSlot(Menu::menus[type]->GetSelection().lock()->I(Attribute::EQUIP_TYPE)));
SoundEffect::PlaySFX("Unequip Armor",SoundEffect::CENTERED);
}
}
}}},
{game->KEY_BACK,{"Back",[](MenuType type){ {game->KEY_BACK,{"Back",[](MenuType type){
if(!Menu::menus[type]->GetSelection().expired()&& if(!Menu::menus[type]->GetSelection().expired()&&
!Menu::menus[type]->GetSelection().lock()->parentComponent.expired()&& !Menu::menus[type]->GetSelection().lock()->parentComponent.expired()&&

@ -49,17 +49,7 @@ InputHelper::InputHelper(){}
void InputHelper::Initialize(MenuInputGroups&inputGroups){ void InputHelper::Initialize(MenuInputGroups&inputGroups){
this->inputGroups.clear(); this->inputGroups.clear();
for(auto&data:inputGroups){ groupData=inputGroups;
if(data.first.GetLabelVisible()){ //If the label is not visible, we don't care to include it in our list.
if(std::holds_alternative<ButtonName>(data.second.first)){
const ButtonName&name=std::get<ButtonName>(data.second.first);
groupedInputs[name].push_back(data.first.GetGroup());
if(groupedInputs[name].size()>1)continue; //Skip adding to the list of input groups because this input already has been added.
}
this->inputGroups[data.first.GetGroup()]=data.second.first;
}
}
} }
const InputType InputHelper::InputMode()const{ const InputType InputHelper::InputMode()const{
@ -79,32 +69,73 @@ void InputHelper::Draw(){
std::vector<std::vector<std::variant<Decal*,std::string>>>buttonImgs; //Store decals for buttons that actually have images, and strings for buttons that have labels. One button can have multiple icons. Store them all together. std::vector<std::vector<std::variant<Decal*,std::string>>>buttonImgs; //Store decals for buttons that actually have images, and strings for buttons that have labels. One button can have multiple icons. Store them all together.
std::vector<std::string>buttonDescriptions; std::vector<std::string>buttonDescriptions;
#pragma region Populate all buttons to display #pragma region Populate all buttons to display
inputGroups.clear();
groupedInputs.clear();
for(auto&data:groupData){
if(data.first.GetLabelVisible()){ //If the label is not visible, we don't care to include it in our list.
if(std::holds_alternative<ButtonName>(data.second.first)){
const ButtonName&name=std::get<ButtonName>(data.second.first);
groupedInputs[name].push_back(data.first.GetGroup());
if(groupedInputs[name].size()>1)continue; //Skip adding to the list of input groups because this input already has been added.
}else
if(std::holds_alternative<std::function<std::string(MenuFuncData)>>(data.second.first)){
std::weak_ptr<ScrollableWindowComponent>parentComponent;
if(!Menu::stack.back()->GetSelection().expired()){
parentComponent=Menu::stack.back()->GetSelection().lock()->parentComponent;
}
std::string name=std::get<std::function<std::string(MenuFuncData)>>(data.second.first)(MenuFuncData{*Menu::stack.back(),game,Menu::stack.back()->GetSelection(),parentComponent});
groupedInputs[name].push_back(data.first.GetGroup());
if(groupedInputs[name].size()>1)continue; //Skip adding to the list of input groups because this input already has been added.
}
this->inputGroups[data.first.GetGroup()]=data.second.first;
}
}
for(auto&[group,display]:inputGroups){ for(auto&[group,display]:inputGroups){
size_t groupedInputCount=1; std::weak_ptr<ScrollableWindowComponent>parentComponent;
std::vector<InputGroup>inputGroupsToCheck; if(!Menu::stack.back()->GetSelection().expired()){
if(std::holds_alternative<std::string>(display)&&groupedInputs.count(std::get<std::string>(display))){ parentComponent=Menu::stack.back()->GetSelection().lock()->parentComponent;
inputGroupsToCheck=groupedInputs.at(std::get<std::string>(display));
}else{
inputGroupsToCheck.push_back(group);
} }
std::vector<InputGroup>inputGroupsToCheck;
std::string displayName; std::string displayName;
if(std::holds_alternative<std::string>(display))displayName=std::get<std::string>(display); if(std::holds_alternative<std::string>(display))displayName=std::get<std::string>(display);
else else
if(std::holds_alternative<std::function<std::string(MenuFuncData)>>(display)){ if(std::holds_alternative<std::function<std::string(MenuFuncData)>>(display)){
std::weak_ptr<ScrollableWindowComponent>parentComponent;
if(!Menu::stack.back()->GetSelection().expired()){
parentComponent=Menu::stack.back()->GetSelection().lock()->parentComponent;
}
displayName=std::get<std::function<std::string(MenuFuncData)>>(display)(MenuFuncData{*Menu::stack.back(),game,Menu::stack.back()->GetSelection(),parentComponent}); displayName=std::get<std::function<std::string(MenuFuncData)>>(display)(MenuFuncData{*Menu::stack.back(),game,Menu::stack.back()->GetSelection(),parentComponent});
} }
else ERR("WARNING! display contains a variant alternative that does not exist. THIS SHOULD NOT BE HAPPENING!"); else ERR("WARNING! display contains a variant alternative that does not exist. THIS SHOULD NOT BE HAPPENING!");
if(groupedInputs.count(displayName)){
inputGroupsToCheck=groupedInputs.at(displayName);
}else{
inputGroupsToCheck.push_back(group);
}
buttonImgs.push_back({}); buttonImgs.push_back({});
std::vector<std::variant<Decal*,std::string>>&iconList=buttonImgs.back(); std::vector<std::variant<Decal*,std::string>>&iconList=buttonImgs.back();
for(InputGroup&group:inputGroupsToCheck){ for(InputGroup&group:inputGroupsToCheck){
if(Menu::UsingMouseNavigation()){
auto&primaryKey=group.GetPrimaryKey(MOUSE);
if(displayName.length()>0&&primaryKey.has_value()){
if(primaryKey.value().HasExtendedIcons()){//This means it follows the specialized icon controller schemes, now pick based on these icons.
buttonImgWidth+=primaryKey.value().GetIcon(GameSettings::GetIconType()).Sprite()->width+"Interface.InputHelperSpacing"_I;
iconList.push_back(primaryKey.value().GetIcon(GameSettings::GetIconType()).Decal());
}else
if(primaryKey.value().HasIcon()){
buttonImgWidth+=primaryKey.value().GetIcon().Sprite()->width+"Interface.InputHelperSpacing"_I;
iconList.push_back(primaryKey.value().GetIcon().Decal());
}else{
buttonImgWidth+=game->GetTextSizeProp(primaryKey.value().GetDisplayName()).x+"Interface.InputHelperSpacing"_I;
iconList.push_back(primaryKey.value().GetDisplayName());
}
}
}
auto&primaryKey=group.GetPrimaryKey(mode); auto&primaryKey=group.GetPrimaryKey(mode);
if(displayName.length()>0&&primaryKey.has_value()){ if(displayName.length()>0&&primaryKey.has_value()){

@ -46,6 +46,7 @@ All rights reserved.
class InputHelper{ class InputHelper{
std::map<InputGroup,InputGroupActionDisplayName>inputGroups; std::map<InputGroup,InputGroupActionDisplayName>inputGroups;
std::map<ButtonName,std::vector<InputGroup>>groupedInputs; std::map<ButtonName,std::vector<InputGroup>>groupedInputs;
MenuInputGroups groupData;
const InputType InputMode()const; const InputType InputMode()const;

@ -328,40 +328,39 @@ void Menu::KeyboardButtonNavigation(AiL*game,vf2d menuPos){
} }
} }
if(!Menu::UsingMouseNavigation()){ for(auto&[input,data]:inputGroups){
for(auto&[input,data]:inputGroups){ if(Menu::alreadyClicked)break;
if(Menu::alreadyClicked)break; bool activated=false;
bool activated=false; switch(input.GetEngageType()){
switch(input.GetEngageType()){ case Released:{
case Released:{ activated=input.GetGroup().Released();
activated=input.GetGroup().Released(); }break;
}break; case Pressed:{
case Pressed:{ activated=input.GetGroup().Pressed();
activated=input.GetGroup().Pressed(); }break;
}break; case PressedDAS:{
case PressedDAS:{ activated=input.GetGroup().PressedDAS();
activated=input.GetGroup().PressedDAS(); }break;
}break; case Held:{
case Held:{ activated=input.GetGroup().Held();
activated=input.GetGroup().Held(); }break;
}break; case Analog:{
case Analog:{ activated=input.GetGroup().Analog()!=0.f;
activated=input.GetGroup().Analog()!=0.f; }break;
}break; [[unlikely]]default:ERR(std::format("WARNING! Unhandled input engage type {}! THIS SHOULD NOT BE HAPPENING!",int(input.GetEngageType())));
[[unlikely]]default:ERR(std::format("WARNING! Unhandled input engage type {}! THIS SHOULD NOT BE HAPPENING!",int(input.GetEngageType()))); }
}
if(activated){ if(activated){
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();
else else
if(std::holds_alternative<std::function<void(MenuType)>>(action))std::get<std::function<void(MenuType)>>(action)(type); if(std::holds_alternative<std::function<void(MenuType)>>(action))std::get<std::function<void(MenuType)>>(action)(type);
else{ else{
ERR("WARNING! Navigation data has an unrecognized type or is empty! This should not be happening!") ERR("WARNING! Navigation data has an unrecognized type or is empty! This should not be happening!")
}
} }
} }
} }
if(!selection.expired()){ if(!selection.expired()){
const int MAX_ITERATIONS=10; //Skip a maximum amount of items in case everything gets disabled somehow, prevents an infinite loop. const int MAX_ITERATIONS=10; //Skip a maximum amount of items in case everything gets disabled somehow, prevents an infinite loop.
int iterationCount=0; int iterationCount=0;

@ -145,7 +145,7 @@ bool Monster::_SetX(float x,const bool monsterInvoked){
vf2d newPos={x,pos.y}; vf2d newPos={x,pos.y};
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth; vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel); geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
if(isBoss||collisionRect==game->NO_COLLISION){ if(collisionRect==game->NO_COLLISION){
pos.x=std::clamp(x,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().width*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult())); pos.x=std::clamp(x,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().width*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved(); Moved();
return true; return true;
@ -175,7 +175,7 @@ bool Monster::_SetY(float y,const bool monsterInvoked){
vf2d newPos={pos.x,y}; vf2d newPos={pos.x,y};
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth; vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel); geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
if(isBoss||collisionRect==game->NO_COLLISION){ if(collisionRect==game->NO_COLLISION){
pos.y=std::clamp(y,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().height*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult())); pos.y=std::clamp(y,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().height*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved(); Moved();
return true; return true;

@ -1,7 +1,5 @@
January 1st January 1st
=========== ===========
- Clamp bosses in boss arenas.
- Track items used during a stage, on death, restore the loadout item quantities used. - Track items used during a stage, on death, restore the loadout item quantities used.
Add Bonus XP when completing a stage Add Bonus XP when completing a stage
@ -23,4 +21,6 @@ January 31st
- Auto aim causes retreat-type moves to aim away from the auto target, and prefer the direction the player's moving in. - Auto aim causes retreat-type moves to aim away from the auto target, and prefer the direction the player's moving in.
- Condense stage track (loading times) - Condense stage track (loading times)
- Credits/Licensing

@ -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 7447 #define VERSION_BUILD 7465
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

@ -191,6 +191,11 @@ Events
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = toggle_off.ogg, 100% File[0] = toggle_off.ogg, 100%
} }
Unequip Armor
{
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = equip2.ogg, 60%
}
Ursule Dead Ursule Dead
{ {
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)

Loading…
Cancel
Save