Added wrapping menu macro functionality. Implemented controller compatibility for level complete window. Added right alignment for menu labels. Added ProgressBar component. Added XP-related functions for callbacks to other components in-game. Fix giant normalized vector issue with Wolf AI script. Release Build 7041.

pull/35/head
sigonasr2 10 months ago
parent 52a01d04ad
commit 22b1557c05
  1. 4
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  2. 3
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  3. 11
      Adventures in Lestoria/AdventuresInLestoria.cpp
  4. 4
      Adventures in Lestoria/CharacterMenuWindow.cpp
  5. 8
      Adventures in Lestoria/InventoryConsumableWindow.cpp
  6. 17
      Adventures in Lestoria/InventoryScrollableWindowComponent.h
  7. 6
      Adventures in Lestoria/Key.cpp
  8. 432
      Adventures in Lestoria/LevelCompleteWindow.cpp
  9. 11
      Adventures in Lestoria/MenuComponent.h
  10. 7
      Adventures in Lestoria/MenuLabel.h
  11. 2
      Adventures in Lestoria/MenuType.h
  12. 23
      Adventures in Lestoria/Player.cpp
  13. 4
      Adventures in Lestoria/Player.h
  14. 90
      Adventures in Lestoria/ProgressBar.h
  15. 6
      Adventures in Lestoria/SaveFile.cpp
  16. 2
      Adventures in Lestoria/State_LevelComplete.cpp
  17. 2
      Adventures in Lestoria/TODO.txt
  18. 2
      Adventures in Lestoria/Version.h
  19. 4
      Adventures in Lestoria/Wolf.cpp
  20. 4
      Adventures in Lestoria/assets/Campaigns/1_B1.tmx
  21. 1
      Adventures in Lestoria/assets/config/gfx/gfx.txt
  22. BIN
      Adventures in Lestoria/assets/xpbar.png
  23. BIN
      x64/Release/Adventures in Lestoria.exe

@ -504,6 +504,10 @@
<ClInclude Include="Unlock.h" /> <ClInclude Include="Unlock.h" />
<ClInclude Include="util.h" /> <ClInclude Include="util.h" />
<ClInclude Include="Version.h" /> <ClInclude Include="Version.h" />
<ClInclude Include="ProgressBar.h">
<SubType>
</SubType>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Ability.cpp" /> <ClCompile Include="Ability.cpp" />

@ -453,6 +453,9 @@
<ClInclude Include="InputDisplayComponent.h"> <ClInclude Include="InputDisplayComponent.h">
<Filter>Header Files\Interface</Filter> <Filter>Header Files\Interface</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="ProgressBar.h">
<Filter>Header Files\Interface</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Player.cpp"> <ClCompile Include="Player.cpp">

@ -320,16 +320,16 @@ bool AiL::OnUserUpdate(float fElapsedTime){
} }
bool AiL::LeftHeld(){ bool AiL::LeftHeld(){
return KEY_LEFT.Held(); return KEY_LEFT.Held()||KEY_SCROLLHORZ_L.Analog()<=-0.2f;
} }
bool AiL::RightHeld(){ bool AiL::RightHeld(){
return KEY_RIGHT.Held(); return KEY_RIGHT.Held()||KEY_SCROLLHORZ_L.Analog()>=0.2f;
} }
bool AiL::UpHeld(){ bool AiL::UpHeld(){
return KEY_UP.Held(); return KEY_UP.Held()||KEY_SCROLLVERT_L.Analog()<=-0.2f;
} }
bool AiL::DownHeld(){ bool AiL::DownHeld(){
return KEY_DOWN.Held(); return KEY_DOWN.Held()||KEY_SCROLLVERT_L.Analog()>=0.2f;
} }
bool AiL::LeftReleased(){ bool AiL::LeftReleased(){
return KEY_LEFT.Released(); return KEY_LEFT.Released();
@ -2193,7 +2193,7 @@ void AiL::ChangePlayerClass(Class cl){
player.reset(NEW Witch(player.get())); player.reset(NEW Witch(player.get()));
}break; }break;
} }
player->level=1; player->SetLevel(level);
player->levelCap=levelCap; player->levelCap=levelCap;
player->stats=previousStats; player->stats=previousStats;
player->SetBaseStat("Health",DATA.GetProperty(player->GetClassName()+".BaseHealth").GetInt()); player->SetBaseStat("Health",DATA.GetProperty(player->GetClassName()+".BaseHealth").GetInt());
@ -2215,6 +2215,7 @@ void AiL::ChangePlayerClass(Class cl){
GetPlayer()->SetItem2UseFunc(itemAbility2); GetPlayer()->SetItem2UseFunc(itemAbility2);
GetPlayer()->SetItem3UseFunc(itemAbility3); GetPlayer()->SetItem3UseFunc(itemAbility3);
camera.SetTarget(player->GetPos()); camera.SetTarget(player->GetPos());
Component<MenuLabel>(CHARACTER_MENU,"Level Class Display")->SetLabel(std::format("Lv{} {}",game->GetPlayer()->Level(),game->GetPlayer()->GetClassName()));
Player::moneyListeners=moneyListeners; Player::moneyListeners=moneyListeners;
} }

@ -48,6 +48,7 @@ All rights reserved.
#include "ScrollableWindowComponent.h" #include "ScrollableWindowComponent.h"
#include "RowItemDisplay.h" #include "RowItemDisplay.h"
#include "SoundEffect.h" #include "SoundEffect.h"
#include "ProgressBar.h"
INCLUDE_game INCLUDE_game
INCLUDE_GFX INCLUDE_GFX
@ -62,6 +63,9 @@ void Menu::InitializeCharacterMenuWindow(){
characterMenuWindow->ADD("Equip Slot Outline",MenuComponent)(geom2d::rect<float>{{0,28},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; characterMenuWindow->ADD("Equip Slot Outline",MenuComponent)(geom2d::rect<float>{{0,28},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
characterMenuWindow->ADD("Character Rotating Display",CharacterRotatingDisplay)(geom2d::rect<float>{{118,18},{130,windowSize.y-28}},GFX[classutils::GetClassInfo(game->GetPlayer()->GetClassName()).classFullImgName].Decal())END; characterMenuWindow->ADD("Character Rotating Display",CharacterRotatingDisplay)(geom2d::rect<float>{{118,18},{130,windowSize.y-28}},GFX[classutils::GetClassInfo(game->GetPlayer()->GetClassName()).classFullImgName].Decal())END;
characterMenuWindow->ADD("Level Class Display",MenuLabel)(geom2d::rect<float>{vf2d{126.f,windowSize.y-28},{118.f,8.f}},std::format("Lv{} {}",game->GetPlayer()->Level(),game->GetPlayer()->GetClassName()),1.f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END;
characterMenuWindow->ADD("XP Bar",ProgressBar)(geom2d::rect<float>{vf2d{126.f,10.f+windowSize.y-28},{118.f,8.f}},Pixel{247,183,82},BLACK,game->GetPlayer()->CurrentXP(),game->GetPlayer()->NextLevelXPRequired(),"xp")END;
const static std::array<std::string,7>displayAttrs{ const static std::array<std::string,7>displayAttrs{
"Health", "Health",
"Attack", "Attack",

@ -169,7 +169,6 @@ void Menu::InitializeConsumableInventoryWindow(){
int newRowIndex=selectedButton.lock()->inventoryIndex-invWidth; /*Moving up moves the cursor up an entire row.*/ \ int newRowIndex=selectedButton.lock()->inventoryIndex-invWidth; /*Moving up moves the cursor up an entire row.*/ \
if(newRowIndex<0){ if(newRowIndex<0){
#define DefaultBehavior \ #define DefaultBehavior \
return; \
} \ } \
if(newRowIndex<0||newRowIndex>=itemsList.size())ERR(std::format("New Row Index ended up out-of-bounds! newRowIndex={}. THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \ if(newRowIndex<0||newRowIndex>=itemsList.size())ERR(std::format("New Row Index ended up out-of-bounds! newRowIndex={}. THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
/*Select the component that matches this new number.*/ \ /*Select the component that matches this new number.*/ \
@ -292,7 +291,8 @@ void Menu::InitializeConsumableInventoryWindow(){
std::weak_ptr<SUBCLASS>selectedButton=DYNAMIC_POINTER_CAST<SUBCLASS>(*component); \ std::weak_ptr<SUBCLASS>selectedButton=DYNAMIC_POINTER_CAST<SUBCLASS>(*component); \
int rowBaseIndex=(selectedButton.lock()->inventoryIndex/invWidth)*invWidth; \ int rowBaseIndex=(selectedButton.lock()->inventoryIndex/invWidth)*invWidth; \
int newRowIndex=selectedButton.lock()->inventoryIndex+1; \ int newRowIndex=selectedButton.lock()->inventoryIndex+1; \
if(newRowIndex>rowBaseIndex+invWidth-1) int newRowBaseIndex = (newRowIndex / invWidth) * invWidth; \
if(rowBaseIndex!=newRowBaseIndex||newRowIndex>=itemsList.size())
#define DefaultBehavior \ #define DefaultBehavior \
newRowIndex=std::clamp(newRowIndex,0,int(itemsList.size()-1)); \ newRowIndex=std::clamp(newRowIndex,0,int(itemsList.size()-1)); \
newRowIndex=std::min(int(itemsList.size())-1,newRowIndex); \ newRowIndex=std::min(int(itemsList.size())-1,newRowIndex); \
@ -303,7 +303,9 @@ void Menu::InitializeConsumableInventoryWindow(){
returnData=*component; \ returnData=*component; \
} \ } \
}else }else
#define DEFAULT_WRAPPING_BEHAVIOR newRowIndex-=invWidth; #define DEFAULT_WRAPPING_BEHAVIOR \
int rowBaseIndex=(selectedButton.lock()->inventoryIndex/invWidth)*invWidth; \
newRowIndex=rowBaseIndex;
#pragma endregion #pragma endregion
#define SUBCLASS MenuItemButton #define SUBCLASS MenuItemButton
DetectInventory(InventoryScrollableWindowComponent,type,"inventory") DetectInventory(InventoryScrollableWindowComponent,type,"inventory")

@ -66,17 +66,21 @@ protected:
const std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>*const onInventorySlotsUpdate; const std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>*const onInventorySlotsUpdate;
const std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>*const addButtonOnSlotUpdate; const std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>*const addButtonOnSlotUpdate;
CompactText compact=COMPACT; CompactText compact=COMPACT;
InventoryWindowOptions options;
std::string itemNameLabelName; std::string itemNameLabelName;
std::string itemDescriptionLabelName; std::string itemDescriptionLabelName;
bool inventoryButtonsActive=true; bool inventoryButtonsActive=true;
private:
InventoryWindowOptions options;
int invWidth;
public: public:
inline InventoryScrollableWindowComponent(geom2d::rect<float>rect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::function<bool(MenuFuncData)>inventoryButtonClickAction,const InventoryCreator&creator,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) inline InventoryScrollableWindowComponent(geom2d::rect<float>rect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::function<bool(MenuFuncData)>inventoryButtonClickAction,const InventoryCreator&creator,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)
:InventoryScrollableWindowComponent(rect,itemNameLabelName,itemDescriptionLabelName,inventoryButtonClickAction,DO_NOTHING,DO_NOTHING,creator,options,inventoryButtonsActive,attributes){ :InventoryScrollableWindowComponent(rect,itemNameLabelName,itemDescriptionLabelName,inventoryButtonClickAction,DO_NOTHING,DO_NOTHING,creator,options,inventoryButtonsActive,attributes){
} }
inline InventoryScrollableWindowComponent(geom2d::rect<float>rect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::function<bool(MenuFuncData)>inventoryButtonClickAction,std::function<bool(MenuFuncData)>inventoryButtonHoverAction,std::function<bool(MenuFuncData)>inventoryButtonMouseOutAction,const InventoryCreator&creator,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) inline InventoryScrollableWindowComponent(geom2d::rect<float>rect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::function<bool(MenuFuncData)>inventoryButtonClickAction,std::function<bool(MenuFuncData)>inventoryButtonHoverAction,std::function<bool(MenuFuncData)>inventoryButtonMouseOutAction,const InventoryCreator&creator,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)
:ScrollableWindowComponent(rect,attributes),inventoryButtonHoverAction(inventoryButtonHoverAction),inventoryButtonMouseOutAction(inventoryButtonMouseOutAction),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName), :ScrollableWindowComponent(rect,attributes),inventoryButtonHoverAction(inventoryButtonHoverAction),inventoryButtonMouseOutAction(inventoryButtonMouseOutAction),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName),
options(options),inventoryButtonClickAction(inventoryButtonClickAction),inventoryButtonsActive(inventoryButtonsActive),addButtonOnSlotUpdate(creator.AddButtonOnSlotUpdate),onInventorySlotsUpdate(creator.InventorySlotsUpdate){} options(options),inventoryButtonClickAction(inventoryButtonClickAction),inventoryButtonsActive(inventoryButtonsActive),addButtonOnSlotUpdate(creator.AddButtonOnSlotUpdate),onInventorySlotsUpdate(creator.InventorySlotsUpdate){
SetInventoryOptions(options);
}
virtual inline void Update(AiL*game)override{ virtual inline void Update(AiL*game)override{
ScrollableWindowComponent::Update(game); ScrollableWindowComponent::Update(game);
bool noneHovered=true; bool noneHovered=true;
@ -98,6 +102,15 @@ public:
itemButton.lock()->SetCompactDescriptions(compact); itemButton.lock()->SetCompactDescriptions(compact);
} }
} }
inline const int GetInventoryWidth()const{
return invWidth;
}
inline void SetInventoryOptions(InventoryWindowOptions newOptions){
options=newOptions;
invWidth=int((rect.size.x-12)/(float(options.size.x)+options.padding));
}
protected: protected:
virtual inline void Cleanup()override final{ virtual inline void Cleanup()override final{

@ -551,16 +551,10 @@ const bool Input::AxesActive(){
if(gamepad->stillConnected){ if(gamepad->stillConnected){
if(fabs(gamepad->getAxis(GPAxes::RX))>0.f){ if(fabs(gamepad->getAxis(GPAxes::RX))>0.f){
xAxis=gamepad->getAxis(GPAxes::RX); xAxis=gamepad->getAxis(GPAxes::RX);
}else
if(fabs(gamepad->getAxis(GPAxes::LX))>0.f){
xAxis=gamepad->getAxis(GPAxes::LX);
} }
if(fabs(gamepad->getAxis(GPAxes::RY))>0.f){ if(fabs(gamepad->getAxis(GPAxes::RY))>0.f){
yAxis=gamepad->getAxis(GPAxes::RY); yAxis=gamepad->getAxis(GPAxes::RY);
}else
if(fabs(gamepad->getAxis(GPAxes::LY))>0.f){
yAxis=gamepad->getAxis(GPAxes::LY);
} }
if(xAxis!=0.f||yAxis!=0.f)break; //Found a controller, so we're good to break. if(xAxis!=0.f||yAxis!=0.f)break; //Found a controller, so we're good to break.

@ -38,11 +38,11 @@ All rights reserved.
#include "Menu.h" #include "Menu.h"
#include "AdventuresInLestoria.h" #include "AdventuresInLestoria.h"
#include "MenuLabel.h" #include "MenuLabel.h"
#include "MenuComponent.h"
#include "InventoryScrollableWindowComponent.h" #include "InventoryScrollableWindowComponent.h"
#include "PopupMenuLabel.h" #include "PopupMenuLabel.h"
#include "Unlock.h" #include "Unlock.h"
#include "State_OverworldMap.h" #include "State_OverworldMap.h"
#include "ProgressBar.h"
INCLUDE_game INCLUDE_game
@ -51,7 +51,10 @@ void Menu::InitializeLevelCompleteWindow(){
Menu*levelCompleteWindow=Menu::CreateMenu(LEVEL_COMPLETE,windowSize.pos,windowSize.size); Menu*levelCompleteWindow=Menu::CreateMenu(LEVEL_COMPLETE,windowSize.pos,windowSize.size);
levelCompleteWindow->ADD("Stage Complete Label",MenuLabel)(geom2d::rect<float>{{0,4},{windowSize.size.x-1.f,20}},"Stage Completed",2,ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND|ComponentAttr::SHADOW)END; levelCompleteWindow->ADD("Stage Complete Label",MenuLabel)(geom2d::rect<float>{{0,-8},{windowSize.size.x-1.f,20}},"Stage Completed",2,ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND|ComponentAttr::SHADOW)END;
levelCompleteWindow->ADD("XP Bar",ProgressBar)(geom2d::rect<float>{vf2d{windowSize.size.x/2.f-96.f,18.f},{192.f,8.f}},Pixel{247,183,82},BLACK,game->GetPlayer()->CurrentXP(),game->GetPlayer()->NextLevelXPRequired(),"xp")END;
levelCompleteWindow->ADD("Level Display",MenuLabel)(geom2d::rect<float>{vf2d{windowSize.size.x/2.f-96.f-42.f,18.f},{42.f,8.f}},std::format("Lv{}",game->GetPlayer()->Level()),1.f,ComponentAttr::SHADOW|ComponentAttr::RIGHT_ALIGN)END;
levelCompleteWindow->ADD("Monster Loot Outline",MenuComponent)(geom2d::rect<float>{{0,32},{windowSize.size.x-80.f,72}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; levelCompleteWindow->ADD("Monster Loot Outline",MenuComponent)(geom2d::rect<float>{{0,32},{windowSize.size.x-80.f,72}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
levelCompleteWindow->ADD("Monster Loot Label",MenuLabel)(geom2d::rect<float>{{0,32},{windowSize.size.x-80.f,12}},"Monster Loot",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END; levelCompleteWindow->ADD("Monster Loot Label",MenuLabel)(geom2d::rect<float>{{0,32},{windowSize.size.x-80.f,12}},"Monster Loot",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END;
@ -117,39 +120,412 @@ void Menu::InitializeLevelCompleteWindow(){
,{ //Button Navigation Rules ,{ //Button Navigation Rules
{"Monster Loot Window",{ {"Monster Loot Window",{
.up=[](MenuType type,Data&returnData){ .up=[](MenuType type,Data&returnData){
auto&selection=Menu::menus[type]->GetSelection(); #pragma region Inventory Wrapping Handling Up Macros
auto&itemsList=Component<InventoryScrollableWindowComponent>(type,"Monster Loot Window")->GetComponents(); //Make sure before using this you have #define SUBCLASS with the children class for this inventory scrollable window component!
auto itemsWindow=Component<InventoryScrollableWindowComponent>(type,"Monster Loot Window"); #define DetectInventory(menuClass,menuType,inventoryComponentName) \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return comp.lock()==selection.lock();}); auto&selection=Menu::menus[menuType]->GetSelection(); \
if(itemsList.size()>0){ auto&itemsList=Component<menuClass>(menuType,inventoryComponentName)->GetComponents(); \
if(component==itemsList.end()){ auto itemsWindow=Component<menuClass>(menuType,inventoryComponentName); \
//Set the selected button to the last element in the list. auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return comp.lock()==selection.lock();}); \
returnData=itemsList.back(); if(itemsList.size()>0){ \
if(component==itemsList.end()){ \
/*Set the selected button to the last element in the list.*/ \
returnData=itemsList.back(); \
}else{ \
int invWidth=int((itemsWindow->rect.size.x-12)/(float(itemsWindow->options.size.x)+itemsWindow->options.padding)); \
std::weak_ptr<SUBCLASS>selectedButton=DYNAMIC_POINTER_CAST<SUBCLASS>(*component); \
int newRowIndex=selectedButton.lock()->inventoryIndex-invWidth; /*Moving up moves the cursor up an entire row.*/ \
if(newRowIndex<0){
#define DefaultBehavior \
} \
if(newRowIndex<0||newRowIndex>=itemsList.size())ERR(std::format("New Row Index ended up out-of-bounds! newRowIndex={}. THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
/*Select the component that matches this new number.*/ \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return DYNAMIC_POINTER_CAST<SUBCLASS>(comp)->inventoryIndex==newRowIndex;}); \
if(component==itemsList.end())ERR(std::format("WARNING! Could not find row index {} in items list while navigating! THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
returnData=*component; \
} \
} \
else
#define DEFAULT_WRAPPING_BEHAVIOR newRowIndex=itemsList.size()-1;
#pragma endregion
#define SUBCLASS MenuItemButton
DetectInventory(InventoryScrollableWindowComponent,type,"Monster Loot Window")
{ //This means we have to wrap around. We have access to itemsList if we needed to point to a specific item in the inventory.
//By returning here, you are processing returnData yourself. Otherwise manipulate newRowIndex to point to an item in the itemsList when wrapping.
int colIndex=(newRowIndex+invWidth)%invWidth; //Negative index, so we have to loop around.
//Get the base row index of the new inventory.
auto stageLootWindow=Component<InventoryScrollableWindowComponent>(type,"Stage Loot Window");
if(stageLootWindow->GetComponents().size()>0){
int rowBaseIndex=(stageLootWindow->GetComponents().back().lock()->inventoryIndex/invWidth)*invWidth;
returnData=stageLootWindow->GetComponents()[std::clamp(rowBaseIndex+colIndex,0,int(stageLootWindow->GetComponents().size()-1))];
return;
}else{
returnData=selectedButton;
}
}
DefaultBehavior{ //When nothing is found...
returnData="Next Button";
}
},
.down=[](MenuType type,Data&returnData){
#pragma region Inventory Wrapping Handling Down Macros
//Make sure before using this you have #define SUBCLASS with the children class for this inventory scrollable window component!
#define DetectInventory(menuClass,menuType,inventoryComponentName) \
auto&selection=Menu::menus[menuType]->GetSelection(); \
auto&itemsList=Component<menuClass>(menuType,inventoryComponentName)->GetComponents(); \
auto itemsWindow=Component<menuClass>(menuType,inventoryComponentName); \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return comp.lock()==selection.lock();}); \
if(itemsList.size()>0){ \
if(component==itemsList.end()){ \
/*Set the selected button to the last element in the list.*/ \
returnData=itemsList.front();\
}else{ \
int invWidth=int((itemsWindow->rect.size.x-12)/(float(itemsWindow->options.size.x)+itemsWindow->options.padding)); \
std::weak_ptr<SUBCLASS>selectedButton=DYNAMIC_POINTER_CAST<SUBCLASS>(*component); \
int newRowIndex=selectedButton.lock()->inventoryIndex+invWidth; /*Moving down moves the cursor down an entire row.*/ \
if(newRowIndex>=itemsList.size()){ \
int currentRow=newRowIndex/invWidth; \
/*The logic here is, if we clamp this row index to the last possible index and we're still on the same row, it means there is at least an item on this row. So we go to that instead.*/ \
newRowIndex=std::clamp(newRowIndex,0,int(itemsList.size()-1)); \
int newRow=newRowIndex/invWidth; \
if(currentRow!=newRow) //This means we are on a different row, so the row we went down on didn't have any items. Simply drop down to the OK Button.
#define DefaultBehavior \
} \
if(newRowIndex<0||newRowIndex>=itemsList.size())ERR(std::format("New Row Index ended up out-of-bounds! newRowIndex={}. THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
/*Select the component that matches this new number.*/ \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return DYNAMIC_POINTER_CAST<SUBCLASS>(comp)->inventoryIndex==newRowIndex;}); \
if(component==itemsList.end())ERR(std::format("WARNING! Could not find row index {} in items list while navigating! THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
returnData=*component; \
} \
} \
else
#define DEFAULT_WRAPPING_BEHAVIOR newRowIndex=0;
#pragma endregion
#define SUBCLASS MenuItemButton
DetectInventory(InventoryScrollableWindowComponent,type,"Monster Loot Window")
{ //This means we have to wrap around. We have access to itemsList if we needed to point to a specific item in the inventory.
//By returning here, you are processing returnData yourself. Otherwise manipulate newRowIndex to point to an item in the itemsList when wrapping.
int colIndex=newRowIndex%invWidth;
//Get the base row index of the new inventory.
auto stageLootWindow=Component<InventoryScrollableWindowComponent>(type,"Stage Loot Window");
if(stageLootWindow->GetComponents().size()>0){
returnData=stageLootWindow->GetComponents()[std::clamp(colIndex,0,int(stageLootWindow->GetComponents().size()-1))];
return;
}else{ }else{
int invWidth=int((itemsWindow->rect.size.x-12)/(float(itemsWindow->options.size.x)+itemsWindow->options.padding)); returnData=selectedButton;
std::weak_ptr<MenuItemButton>selectedButton=DYNAMIC_POINTER_CAST<MenuItemButton>(*component); }
int newRowIndex=selectedButton.lock()->inventoryIndex-invWidth; //Moving up moves the cursor up an entire row. }
DefaultBehavior{ //When nothing is found...
returnData="Next Button";
}
},
.left=[](MenuType type,Data&returnData){
#pragma region Inventory Wrapping Handling Left Macros
#define DetectInventory(menuClass,menuType,inventoryComponentName) \
auto&selection=Menu::menus[menuType]->GetSelection(); \
auto&itemsList=Component<menuClass>(menuType,inventoryComponentName)->GetComponents(); \
auto itemsWindow=Component<menuClass>(menuType,inventoryComponentName); \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return comp.lock()==selection.lock();}); \
if(itemsList.size()>0){ \
if(component==itemsList.end()){ \
/*Set the selected button to the last element in the list.*/ \
returnData=itemsList.back(); \
}else{ \
int invWidth=int((itemsWindow->rect.size.x-12)/(float(itemsWindow->options.size.x)+itemsWindow->options.padding)); \
std::weak_ptr<SUBCLASS>selectedButton=DYNAMIC_POINTER_CAST<SUBCLASS>(*component); \
int rowBaseIndex=(selectedButton.lock()->inventoryIndex/invWidth)*invWidth; \
int newRowIndex=selectedButton.lock()->inventoryIndex-1; \
if(newRowIndex<rowBaseIndex)
#define DefaultBehavior \
newRowIndex=std::min(int(itemsList.size())-1,newRowIndex); \
if(newRowIndex<0)newRowIndex+=itemsList.size(); \
if(newRowIndex<0||newRowIndex>=itemsList.size())ERR(std::format("New Row Index ended up out-of-bounds! newRowIndex={}. THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
/*Select the component that matches this new number.*/ \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return DYNAMIC_POINTER_CAST<SUBCLASS>(comp)->inventoryIndex==newRowIndex;}); \
if(component==itemsList.end())ERR(std::format("WARNING! Could not find row index {} in items list while navigating! THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
returnData=*component; \
} \
}else
#define DEFAULT_WRAPPING_BEHAVIOR newRowIndex+=invWidth;
#pragma endregion
#define SUBCLASS MenuItemButton
DetectInventory(InventoryScrollableWindowComponent,type,"Monster Loot Window")
{ //This means we have to wrap around. We have access to itemsList if we needed to point to a specific item in the inventory.
//By returning here, you are processing returnData yourself. Otherwise manipulate newRowIndex to point to an item in the itemsList when wrapping.
returnData="Next Button";
return;
}
DefaultBehavior{ //When nothing is found...
returnData="Next Button";
}
},
.right=[](MenuType type,Data&returnData){
#pragma region Inventory Wrapping Handling Right Macros
#define DetectInventory(menuClass,menuType,inventoryComponentName) \
auto&selection=Menu::menus[menuType]->GetSelection(); \
auto&itemsList=Component<menuClass>(menuType,inventoryComponentName)->GetComponents(); \
auto itemsWindow=Component<menuClass>(menuType,inventoryComponentName); \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return comp.lock()==selection.lock();}); \
if(itemsList.size()>0){ \
if(component==itemsList.end()){ \
/*Set the selected button to the last element in the list.*/ \
returnData=itemsList.front(); \
}else{ \
int invWidth=int((itemsWindow->rect.size.x-12)/(float(itemsWindow->options.size.x)+itemsWindow->options.padding)); \
std::weak_ptr<SUBCLASS>selectedButton=DYNAMIC_POINTER_CAST<SUBCLASS>(*component); \
int rowBaseIndex=(selectedButton.lock()->inventoryIndex/invWidth)*invWidth; \
int newRowIndex=selectedButton.lock()->inventoryIndex+1; \
int newRowBaseIndex = (newRowIndex / invWidth) * invWidth; \
if(rowBaseIndex!=newRowBaseIndex)
#define DefaultBehavior \
newRowIndex=std::clamp(newRowIndex,0,int(itemsList.size()-1)); \
newRowIndex=std::min(int(itemsList.size())-1,newRowIndex); \
if(newRowIndex<0||newRowIndex>=itemsList.size())ERR(std::format("New Row Index ended up out-of-bounds! newRowIndex={}. THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
/*Select the component that matches this new number.*/ \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return DYNAMIC_POINTER_CAST<SUBCLASS>(comp)->inventoryIndex==newRowIndex;}); \
if(component==itemsList.end())ERR(std::format("WARNING! Could not find row index {} in items list while navigating! THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
returnData=*component; \
} \
}else
#define DEFAULT_WRAPPING_BEHAVIOR \
int rowBaseIndex=(selectedButton.lock()->inventoryIndex/invWidth)*invWidth; \
newRowIndex=rowBaseIndex;
#pragma endregion
#define SUBCLASS MenuItemButton
DetectInventory(InventoryScrollableWindowComponent,type,"Monster Loot Window")
{ //This means we have to wrap around. We have access to itemsList if we needed to point to a specific item in the inventory.
//By returning here, you are processing returnData yourself. Otherwise manipulate newRowIndex to point to an item in the itemsList when wrapping.
returnData="Next Button";
return;
}
DefaultBehavior{ //When nothing is found...
returnData="Next Button";
}
},}},
{"Stage Loot Window",{
.up=[](MenuType type,Data&returnData){
#pragma region Inventory Wrapping Handling Up Macros
//Make sure before using this you have #define SUBCLASS with the children class for this inventory scrollable window component!
#define DetectInventory(menuClass,menuType,inventoryComponentName) \
auto&selection=Menu::menus[menuType]->GetSelection(); \
auto&itemsList=Component<menuClass>(menuType,inventoryComponentName)->GetComponents(); \
auto itemsWindow=Component<menuClass>(menuType,inventoryComponentName); \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return comp.lock()==selection.lock();}); \
if(itemsList.size()>0){ \
if(component==itemsList.end()){ \
/*Set the selected button to the last element in the list.*/ \
returnData=itemsList.back(); \
}else{ \
int invWidth=int((itemsWindow->rect.size.x-12)/(float(itemsWindow->options.size.x)+itemsWindow->options.padding)); \
std::weak_ptr<SUBCLASS>selectedButton=DYNAMIC_POINTER_CAST<SUBCLASS>(*component); \
int newRowIndex=selectedButton.lock()->inventoryIndex-invWidth; /*Moving up moves the cursor up an entire row.*/ \
if(newRowIndex<0){ if(newRowIndex<0){
//This means we have to wrap around. #define DefaultBehavior \
returnData="OK Button"; } \
if(newRowIndex<0||newRowIndex>=itemsList.size())ERR(std::format("New Row Index ended up out-of-bounds! newRowIndex={}. THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
/*Select the component that matches this new number.*/ \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return DYNAMIC_POINTER_CAST<SUBCLASS>(comp)->inventoryIndex==newRowIndex;}); \
if(component==itemsList.end())ERR(std::format("WARNING! Could not find row index {} in items list while navigating! THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
returnData=*component; \
} \
} \
else
#define DEFAULT_WRAPPING_BEHAVIOR newRowIndex=itemsList.size()-1;
#pragma endregion
#define SUBCLASS MenuItemButton
DetectInventory(InventoryScrollableWindowComponent,type,"Stage Loot Window")
{ //This means we have to wrap around. We have access to itemsList if we needed to point to a specific item in the inventory.
//By returning here, you are processing returnData yourself. Otherwise manipulate newRowIndex to point to an item in the itemsList when wrapping.
int colIndex=(newRowIndex+invWidth)%invWidth;
//Get the base row index of the new inventory.
auto stageLootWindow=Component<InventoryScrollableWindowComponent>(type,"Monster Loot Window");
if(stageLootWindow->GetComponents().size()>0){
int rowBaseIndex=(stageLootWindow->GetComponents().back().lock()->inventoryIndex/invWidth)*invWidth;
returnData=stageLootWindow->GetComponents()[std::clamp(rowBaseIndex+colIndex,0,int(stageLootWindow->GetComponents().size()-1))];
return; return;
}else{
returnData=selectedButton;
} }
if(newRowIndex<0||newRowIndex>=itemsList.size())ERR(std::format("New Row Index ended up out-of-bounds! newRowIndex={}. THIS SHOULD NOT BE HAPPENING!",newRowIndex));
//Select the component that matches this new number.
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return DYNAMIC_POINTER_CAST<MenuItemButton>(comp)->inventoryIndex==newRowIndex;});
if(component==itemsList.end())ERR(std::format("WARNING! Could not find row index {} in items list while navigating! THIS SHOULD NOT BE HAPPENING!",newRowIndex));
returnData=*component;
} }
DefaultBehavior{ //When nothing is found...
returnData="Next Button";
}
},
.down=[](MenuType type,Data&returnData){
#pragma region Inventory Wrapping Handling Down Macros
//Make sure before using this you have #define SUBCLASS with the children class for this inventory scrollable window component!
#define DetectInventory(menuClass,menuType,inventoryComponentName) \
auto&selection=Menu::menus[menuType]->GetSelection(); \
auto&itemsList=Component<menuClass>(menuType,inventoryComponentName)->GetComponents(); \
auto itemsWindow=Component<menuClass>(menuType,inventoryComponentName); \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return comp.lock()==selection.lock();}); \
if(itemsList.size()>0){ \
if(component==itemsList.end()){ \
/*Set the selected button to the last element in the list.*/ \
returnData=itemsList.front();\
}else{ \
int invWidth=int((itemsWindow->rect.size.x-12)/(float(itemsWindow->options.size.x)+itemsWindow->options.padding)); \
std::weak_ptr<SUBCLASS>selectedButton=DYNAMIC_POINTER_CAST<SUBCLASS>(*component); \
int newRowIndex=selectedButton.lock()->inventoryIndex+invWidth; /*Moving down moves the cursor down an entire row.*/ \
if(newRowIndex>=itemsList.size()){ \
int currentRow=newRowIndex/invWidth; \
/*The logic here is, if we clamp this row index to the last possible index and we're still on the same row, it means there is at least an item on this row. So we go to that instead.*/ \
newRowIndex=std::clamp(newRowIndex,0,int(itemsList.size()-1)); \
int newRow=newRowIndex/invWidth; \
if(currentRow!=newRow) //This means we are on a different row, so the row we went down on didn't have any items. Simply drop down to the OK Button.
#define DefaultBehavior \
} \
if(newRowIndex<0||newRowIndex>=itemsList.size())ERR(std::format("New Row Index ended up out-of-bounds! newRowIndex={}. THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
/*Select the component that matches this new number.*/ \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return DYNAMIC_POINTER_CAST<SUBCLASS>(comp)->inventoryIndex==newRowIndex;}); \
if(component==itemsList.end())ERR(std::format("WARNING! Could not find row index {} in items list while navigating! THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
returnData=*component; \
} \
} \
else
#define DEFAULT_WRAPPING_BEHAVIOR newRowIndex=0;
#pragma endregion
#define SUBCLASS MenuItemButton
DetectInventory(InventoryScrollableWindowComponent,type,"Stage Loot Window")
{ //This means we have to wrap around. We have access to itemsList if we needed to point to a specific item in the inventory.
//By returning here, you are processing returnData yourself. Otherwise manipulate newRowIndex to point to an item in the itemsList when wrapping.
int colIndex=newRowIndex%invWidth;
//Get the base row index of the new inventory.
auto stageLootWindow=Component<InventoryScrollableWindowComponent>(type,"Monster Loot Window");
if(stageLootWindow->GetComponents().size()>0){
returnData=stageLootWindow->GetComponents()[std::clamp(colIndex,0,int(stageLootWindow->GetComponents().size()-1))];
return;
}else{ }else{
returnData="OK Button"; returnData=selectedButton;
}
}
DefaultBehavior{ //When nothing is found...
returnData="Next Button";
} }
}, },
.down="Load Game Button",}}, .left=[](MenuType type,Data&returnData){
{"Load Game Button",{ #pragma region Inventory Wrapping Handling Left Macros
.up="New Game Button", #define DetectInventory(menuClass,menuType,inventoryComponentName) \
.down="Quit Game Button",}}, auto&selection=Menu::menus[menuType]->GetSelection(); \
{"Quit Game Button",{ auto&itemsList=Component<menuClass>(menuType,inventoryComponentName)->GetComponents(); \
.up="Load Game Button", auto itemsWindow=Component<menuClass>(menuType,inventoryComponentName); \
.down="New Game Button",}}, auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return comp.lock()==selection.lock();}); \
if(itemsList.size()>0){ \
if(component==itemsList.end()){ \
/*Set the selected button to the last element in the list.*/ \
returnData=itemsList.back(); \
}else{ \
int invWidth=int((itemsWindow->rect.size.x-12)/(float(itemsWindow->options.size.x)+itemsWindow->options.padding)); \
std::weak_ptr<SUBCLASS>selectedButton=DYNAMIC_POINTER_CAST<SUBCLASS>(*component); \
int rowBaseIndex=(selectedButton.lock()->inventoryIndex/invWidth)*invWidth; \
int newRowIndex=selectedButton.lock()->inventoryIndex-1; \
if(newRowIndex<rowBaseIndex)
#define DefaultBehavior \
newRowIndex=std::min(int(itemsList.size())-1,newRowIndex); \
if(newRowIndex<0)newRowIndex+=itemsList.size(); \
if(newRowIndex<0||newRowIndex>=itemsList.size())ERR(std::format("New Row Index ended up out-of-bounds! newRowIndex={}. THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
/*Select the component that matches this new number.*/ \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return DYNAMIC_POINTER_CAST<SUBCLASS>(comp)->inventoryIndex==newRowIndex;}); \
if(component==itemsList.end())ERR(std::format("WARNING! Could not find row index {} in items list while navigating! THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
returnData=*component; \
} \
}else
#define DEFAULT_WRAPPING_BEHAVIOR newRowIndex+=invWidth;
#pragma endregion
#define SUBCLASS MenuItemButton
DetectInventory(InventoryScrollableWindowComponent,type,"Stage Loot Window")
{ //This means we have to wrap around. We have access to itemsList if we needed to point to a specific item in the inventory.
//By returning here, you are processing returnData yourself. Otherwise manipulate newRowIndex to point to an item in the itemsList when wrapping.
returnData="Next Button";
return;
}
DefaultBehavior{ //When nothing is found...
returnData="Next Button";
}
},
.right=[](MenuType type,Data&returnData){
#pragma region Inventory Wrapping Handling Right Macros
#define DetectInventory(menuClass,menuType,inventoryComponentName) \
auto&selection=Menu::menus[menuType]->GetSelection(); \
auto&itemsList=Component<menuClass>(menuType,inventoryComponentName)->GetComponents(); \
auto itemsWindow=Component<menuClass>(menuType,inventoryComponentName); \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return comp.lock()==selection.lock();}); \
if(itemsList.size()>0){ \
if(component==itemsList.end()){ \
/*Set the selected button to the last element in the list.*/ \
returnData=itemsList.front(); \
}else{ \
int invWidth=int((itemsWindow->rect.size.x-12)/(float(itemsWindow->options.size.x)+itemsWindow->options.padding)); \
std::weak_ptr<SUBCLASS>selectedButton=DYNAMIC_POINTER_CAST<SUBCLASS>(*component); \
int rowBaseIndex=(selectedButton.lock()->inventoryIndex/invWidth)*invWidth; \
int newRowIndex=selectedButton.lock()->inventoryIndex+1; \
int newRowBaseIndex = (newRowIndex / invWidth) * invWidth; \
if(rowBaseIndex!=newRowBaseIndex||newRowIndex>=itemsList.size())
#define DefaultBehavior \
newRowIndex=std::clamp(newRowIndex,0,int(itemsList.size()-1)); \
newRowIndex=std::min(int(itemsList.size())-1,newRowIndex); \
if(newRowIndex<0||newRowIndex>=itemsList.size())ERR(std::format("New Row Index ended up out-of-bounds! newRowIndex={}. THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
/*Select the component that matches this new number.*/ \
auto component=std::find_if(itemsList.begin(),itemsList.end(),[&](auto&comp){return DYNAMIC_POINTER_CAST<SUBCLASS>(comp)->inventoryIndex==newRowIndex;}); \
if(component==itemsList.end())ERR(std::format("WARNING! Could not find row index {} in items list while navigating! THIS SHOULD NOT BE HAPPENING!",newRowIndex)); \
returnData=*component; \
} \
}else
#define DEFAULT_WRAPPING_BEHAVIOR \
int rowBaseIndex=(selectedButton.lock()->inventoryIndex/invWidth)*invWidth; \
newRowIndex=rowBaseIndex;
#pragma endregion
#define SUBCLASS MenuItemButton
DetectInventory(InventoryScrollableWindowComponent,type,"Stage Loot Window")
{ //This means we have to wrap around. We have access to itemsList if we needed to point to a specific item in the inventory.
//By returning here, you are processing returnData yourself. Otherwise manipulate newRowIndex to point to an item in the itemsList when wrapping.
returnData="Next Button";
return;
}
DefaultBehavior{ //When nothing is found...
returnData="Next Button";
}
},}},
{"Next Button",{
.up=[](MenuType type,Data&returnData){
auto monsterLoot=Component<InventoryScrollableWindowComponent>(type,"Monster Loot Window");
auto stageLoot=Component<InventoryScrollableWindowComponent>(type,"Stage Loot Window");
if(monsterLoot->GetComponents().size()>0){
int invWidth=monsterLoot->GetInventoryWidth();
returnData=monsterLoot->GetComponents()[0];
}else
if(stageLoot->GetComponents().size()>0){
int invWidth=stageLoot->GetInventoryWidth();
returnData=stageLoot->GetComponents()[0];
}else{
returnData="Next Button";
}
},
.left=[](MenuType type,Data&returnData){
auto monsterLoot=Component<InventoryScrollableWindowComponent>(type,"Monster Loot Window");
auto stageLoot=Component<InventoryScrollableWindowComponent>(type,"Stage Loot Window");
if(monsterLoot->GetComponents().size()>0){
int invWidth=monsterLoot->GetInventoryWidth();
returnData=monsterLoot->GetComponents()[std::clamp(invWidth-1,0,int(monsterLoot->GetComponents().size()-1))];
}else
if(stageLoot->GetComponents().size()>0){
int invWidth=stageLoot->GetInventoryWidth();
returnData=stageLoot->GetComponents()[std::clamp(invWidth-1,0,int(stageLoot->GetComponents().size()-1))];
}else{
returnData="Next Button";
}
},
.right=[](MenuType type,Data&returnData){
auto monsterLoot=Component<InventoryScrollableWindowComponent>(type,"Monster Loot Window");
auto stageLoot=Component<InventoryScrollableWindowComponent>(type,"Stage Loot Window");
if(monsterLoot->GetComponents().size()>0){
returnData=monsterLoot->GetComponents()[0];
}else
if(stageLoot->GetComponents().size()>0){
returnData=stageLoot->GetComponents()[0];
}else{
returnData="Next Button";
}
},}},
}); });
} }

@ -49,11 +49,12 @@ enum class ButtonAttr{
enum class ComponentAttr{ enum class ComponentAttr{
NONE= 0b0'0000'0000, NONE= 0b0'0000'0000,
LEFT_ALIGN= 0b0'0000'0001, //Labels are centered by default. LEFT_ALIGN= 0b0'0000'0001, //Labels are centered by default.
SHADOW= 0b0'0000'0010, //Adds shadows to the label text. RIGHT_ALIGN= 0b0'0000'0010, //Labels are centered by default.
OUTLINE= 0b0'0000'0100, //Adds an outline around the component. SHADOW= 0b0'0000'0100, //Adds shadows to the label text.
BACKGROUND= 0b0'0000'1000, //Renders the background of the menu theme for this component. OUTLINE= 0b0'0000'1000, //Adds an outline around the component.
FIT_TO_LABEL= 0b0'0001'0000, //Scales the text horizontally to fit the label if the text takes up more space rather than wrapping. BACKGROUND= 0b0'0001'0000, //Renders the background of the menu theme for this component.
FIXED_WIDTH_FONT= 0b0'0010'0000, //Uses a fixed-width font (instead of a proportional one) for text rendering. FIT_TO_LABEL= 0b0'0010'0000, //Scales the text horizontally to fit the label if the text takes up more space rather than wrapping.
FIXED_WIDTH_FONT= 0b0'0100'0000, //Uses a fixed-width font (instead of a proportional one) for text rendering.
}; };
enum class SelectionType{ enum class SelectionType{

@ -49,6 +49,7 @@ protected:
float scale=1; float scale=1;
bool shadow=false; bool shadow=false;
bool centered=true; bool centered=true;
bool rightAlign=false;
bool proportional=true; bool proportional=true;
bool runOnLabelChangeFunc=false; bool runOnLabelChangeFunc=false;
std::function<void(std::string_view newLabel)>onLabelChangeFunc; std::function<void(std::string_view newLabel)>onLabelChangeFunc;
@ -59,7 +60,7 @@ public:
this->onLabelChangeFunc=onLabelChangeFunc; this->onLabelChangeFunc=onLabelChangeFunc;
} }
inline MenuLabel(geom2d::rect<float>rect,std::string label,float scale=1,ComponentAttr attributes=ComponentAttr::NONE) inline MenuLabel(geom2d::rect<float>rect,std::string label,float scale=1,ComponentAttr attributes=ComponentAttr::NONE)
:MenuComponent(rect,label,MenuFunc{},ButtonAttr::UNSELECTABLE|ButtonAttr::UNSELECTABLE_VIA_KEYBOARD),scale(scale),centered(!(attributes&ComponentAttr::LEFT_ALIGN)),shadow(attributes&ComponentAttr::SHADOW),proportional(!(attributes&ComponentAttr::FIXED_WIDTH_FONT)){ :MenuComponent(rect,label,MenuFunc{},ButtonAttr::UNSELECTABLE|ButtonAttr::UNSELECTABLE_VIA_KEYBOARD),scale(scale),rightAlign(attributes&ComponentAttr::RIGHT_ALIGN),centered(!(attributes&ComponentAttr::LEFT_ALIGN)&&!(attributes&ComponentAttr::RIGHT_ALIGN)),shadow(attributes&ComponentAttr::SHADOW),proportional(!(attributes&ComponentAttr::FIXED_WIDTH_FONT)){
border=attributes&ComponentAttr::OUTLINE; border=attributes&ComponentAttr::OUTLINE;
this->background=attributes&ComponentAttr::BACKGROUND; this->background=attributes&ComponentAttr::BACKGROUND;
showDefaultLabel=false; showDefaultLabel=false;
@ -95,8 +96,12 @@ protected:
vf2d drawPos=vf2d{-1,0}+rect.middle()-vf2d{labelTextSize}/2; //Assume centered. vf2d drawPos=vf2d{-1,0}+rect.middle()-vf2d{labelTextSize}/2; //Assume centered.
if(!centered){ if(!centered){
if(rightAlign){
drawPos=vf2d{rect.pos.x+rect.size.x-labelTextSize.x-2,rect.middle().y-labelTextSize.y/2}; //We should at least vertically align here.
}else{ //Left Align
drawPos=vf2d{rect.pos.x+2,rect.middle().y-labelTextSize.y/2}; //We should at least vertically align here. drawPos=vf2d{rect.pos.x+2,rect.middle().y-labelTextSize.y/2}; //We should at least vertically align here.
} }
}
if(shadow){ if(shadow){
if(proportional){ if(proportional){

@ -48,7 +48,7 @@ enum MenuType{
MAIN_MENU, //100% Controller Compatibility MAIN_MENU, //100% Controller Compatibility
OVERWORLD_LEVEL_SELECT, //100% Controller Compatibility - Scrolling for keyboard? OVERWORLD_LEVEL_SELECT, //100% Controller Compatibility - Scrolling for keyboard?
ITEM_LOADOUT, //100% Controller Compatibility ITEM_LOADOUT, //100% Controller Compatibility
LEVEL_COMPLETE, //0% Controller Compatibility LEVEL_COMPLETE, //100% Controller Compatibility
OVERWORLD_MENU, //0% Controller Compatibility OVERWORLD_MENU, //0% Controller Compatibility
CHARACTER_MENU, //0% Controller Compatibility CHARACTER_MENU, //0% Controller Compatibility
INVENTORY, //0% Controller Compatibility INVENTORY, //0% Controller Compatibility

@ -52,6 +52,8 @@ All rights reserved.
#include <string_view> #include <string_view>
#include "SoundEffect.h" #include "SoundEffect.h"
#include "olcPGEX_Gamepad.h" #include "olcPGEX_Gamepad.h"
#include "ProgressBar.h"
#include "MenuLabel.h"
INCLUDE_MONSTER_DATA INCLUDE_MONSTER_DATA
INCLUDE_MONSTER_LIST INCLUDE_MONSTER_LIST
@ -1213,6 +1215,16 @@ void Player::AddXP(const uint32_t xpGain){
OnLevelUp(); OnLevelUp();
} }
} }
Component<ProgressBar>(MenuType::CHARACTER_MENU,"XP Bar")->ResetProgressBar(game->GetPlayer()->CurrentXP(),game->GetPlayer()->NextLevelXPRequired());
}
void Player::SetXP(const uint32_t xp){
currentLevelXP=xp;
Component<ProgressBar>(MenuType::CHARACTER_MENU,"XP Bar")->ResetProgressBar(game->GetPlayer()->CurrentXP(),game->GetPlayer()->NextLevelXPRequired());
}
void Player::SetTotalXPEarned(const uint32_t totalXP){
totalXPEarned=totalXP;
} }
void Player::OnLevelUp(){ void Player::OnLevelUp(){
@ -1226,6 +1238,11 @@ const uint8_t Player::LevelCap()const{
const uint8_t Player::Level()const{ const uint8_t Player::Level()const{
return level; return level;
} }
void Player::SetLevel(uint8_t newLevel){
level=newLevel;
Component<MenuLabel>(CHARACTER_MENU,"Level Class Display")->SetLabel(std::format("Lv{} {}",game->GetPlayer()->Level(),game->GetPlayer()->GetClassName()));
Component<MenuLabel>(LEVEL_COMPLETE,"Level Display")->SetLabel(std::format("Lv{}",game->GetPlayer()->Level()));
}
const uint32_t Player::CurrentXP()const{ const uint32_t Player::CurrentXP()const{
return currentLevelXP; return currentLevelXP;
} }
@ -1283,16 +1300,10 @@ const vf2d Player::GetAimingLocation(){
if(gamepad->stillConnected){ if(gamepad->stillConnected){
if(fabs(gamepad->getAxis(GPAxes::RX))>0.f){ if(fabs(gamepad->getAxis(GPAxes::RX))>0.f){
xAxis=gamepad->getAxis(GPAxes::RX); xAxis=gamepad->getAxis(GPAxes::RX);
}else
if(fabs(gamepad->getAxis(GPAxes::LX))>0.f){
xAxis=gamepad->getAxis(GPAxes::LX);
} }
if(fabs(gamepad->getAxis(GPAxes::RY))>0.f){ if(fabs(gamepad->getAxis(GPAxes::RY))>0.f){
yAxis=gamepad->getAxis(GPAxes::RY); yAxis=gamepad->getAxis(GPAxes::RY);
}else
if(fabs(gamepad->getAxis(GPAxes::LY))>0.f){
yAxis=gamepad->getAxis(GPAxes::LY);
} }
if(xAxis!=0.f||yAxis!=0.f)break; //Found a controller, so we're good to break. if(xAxis!=0.f||yAxis!=0.f)break; //Found a controller, so we're good to break.

@ -91,7 +91,6 @@ class Player{
friend class State_GameRun; friend class State_GameRun;
friend class Inventory; friend class Inventory;
friend void ItemOverlay::Draw(); friend void ItemOverlay::Draw();
friend class SaveFile;
public: public:
Player(); Player();
//So this is rather fascinating and only exists because we have the ability to change classes which means we need to initialize a class //So this is rather fascinating and only exists because we have the ability to change classes which means we need to initialize a class
@ -241,6 +240,9 @@ public:
void Knockup(float duration); void Knockup(float duration);
const vf2d GetAimingLocation(); const vf2d GetAimingLocation();
const vf2d GetWorldAimingLocation(); const vf2d GetWorldAimingLocation();
void SetXP(const uint32_t xp);
void SetTotalXPEarned(const uint32_t totalXP);
void SetLevel(uint8_t newLevel);
private: private:
int hp="Warrior.BaseHealth"_I; int hp="Warrior.BaseHealth"_I;
int mana="Player.BaseMana"_I; int mana="Player.BaseMana"_I;

@ -0,0 +1,90 @@
#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
#pragma once
#include "MenuComponent.h"
#include "DEFINES.h"
INCLUDE_GFX
INCLUDE_game
class ProgressBar:public MenuComponent{
Pixel backCol;
Pixel textCol;
int currentProgress;
int finalProgress;
std::string subText;
public:
inline ProgressBar(geom2d::rect<float>rect,Pixel backCol,Pixel textCol,int currentProgress,int finalProgress,std::string subText)
:MenuComponent::MenuComponent(rect,"",DO_NOTHING,ButtonAttr::UNSELECTABLE),backCol(backCol),textCol(textCol),currentProgress(currentProgress),finalProgress(finalProgress),subText(subText){}
inline void UpdateProgressBar(int newProgress){
currentProgress=newProgress;
}
inline void ResetProgressBar(int currentProgress,int finalProgress){
this->currentProgress=currentProgress;
this->finalProgress=finalProgress;
}
inline const int GetCurrentProgress()const{
return currentProgress;
}
inline const int GetFinalProgress()const{
return finalProgress;
}
inline void SetSubText(std::string subText){
this->subText=subText;
}
inline virtual void DrawDecal(ViewPort&window,bool focused)override final{
float barPct=float(currentProgress)/float(finalProgress);
vf2d barSize=rect.size-vf2d{2.f,2.f};
Pixel lighterCol=backCol;
lighterCol.r=std::min(255U,lighterCol.r+30U);
lighterCol.g=std::min(255U,lighterCol.g+30U);
lighterCol.b=std::min(255U,lighterCol.b+30U);
window.GradientFillRectDecal(rect.pos+vf2d{1.f,1.f},vf2d{barPct*barSize.x,barSize.y},lighterCol,backCol,backCol,lighterCol);
window.GradientFillRectDecal(rect.pos+vf2d{(barPct*(barSize)).x+1.f,2.f},vf2d{1.f,barSize.y-2.f},lighterCol,backCol,backCol,lighterCol);
window.DrawPartialDecal(rect.pos,vf2d{2.f,rect.size.y},GFX["xpbar.png"].Decal(),vf2d{0.f,0.f},vf2d{2.f,8.f},WHITE);
window.DrawPartialDecal(rect.pos+vf2d{2.f,0.f},vf2d{rect.size.x-4.f,rect.size.y},GFX["xpbar.png"].Decal(),vf2d{2.f,0.f},vf2d{1.f,8.f},WHITE);
window.DrawPartialDecal(rect.pos+vf2d{rect.size.x-2.f,0.f},vf2d{2.f,rect.size.y},GFX["xpbar.png"].Decal(),vf2d{3.f,0.f},vf2d{2.f,8.f},WHITE);
std::string barText=std::format("{}/{}{}",currentProgress,finalProgress,subText);
vf2d textSize=game->GetTextSizeProp(barText);
window.DrawShadowStringPropDecal(rect.pos+rect.size/2.f-textSize/2.f,barText);
}
};

@ -253,10 +253,10 @@ void SaveFile::LoadFile(){
} }
} }
game->ChangePlayerClass(classutils::StringToClass(loadFile["Player"]["Class"].GetString())); game->ChangePlayerClass(classutils::StringToClass(loadFile["Player"]["Class"].GetString()));
game->GetPlayer()->level=loadFile["Player"]["Level"].GetInt(); game->GetPlayer()->SetLevel(loadFile["Player"]["Level"].GetInt());
game->GetPlayer()->SetMoney(loadFile["Player"]["Money"].GetInt()); game->GetPlayer()->SetMoney(loadFile["Player"]["Money"].GetInt());
game->GetPlayer()->currentLevelXP=loadFile["Player"]["Current EXP"].GetInt(); game->GetPlayer()->SetXP(loadFile["Player"]["Current EXP"].GetInt());
game->GetPlayer()->totalXPEarned=loadFile["Player"]["Total EXP"].GetInt(); game->GetPlayer()->SetTotalXPEarned(loadFile["Player"]["Total EXP"].GetInt());
for(auto&[key,data]:loadFile["Player"]["Base Stats"].GetOrderedKeys()){ for(auto&[key,data]:loadFile["Player"]["Base Stats"].GetOrderedKeys()){
game->GetPlayer()->SetBaseStat(key,data.GetReal()); game->GetPlayer()->SetBaseStat(key,data.GetReal());
} }

@ -41,6 +41,7 @@ All rights reserved.
#include "Menu.h" #include "Menu.h"
#include "MenuLabel.h" #include "MenuLabel.h"
#include "SaveFile.h" #include "SaveFile.h"
#include "ProgressBar.h"
INCLUDE_MONSTER_LIST INCLUDE_MONSTER_LIST
INCLUDE_game INCLUDE_game
@ -50,6 +51,7 @@ void State_LevelComplete::OnStateChange(GameState*prevState){
Menu::CloseAllMenus(); Menu::CloseAllMenus();
} }
Component<MenuLabel>(MenuType::LEVEL_COMPLETE,"Level EXP Gain Outline")->SetLabel(std::format("+{} Exp",game->GetPlayer()->GetAccumulatedXP())); Component<MenuLabel>(MenuType::LEVEL_COMPLETE,"Level EXP Gain Outline")->SetLabel(std::format("+{} Exp",game->GetPlayer()->GetAccumulatedXP()));
Component<ProgressBar>(MenuType::LEVEL_COMPLETE,"XP Bar")->ResetProgressBar(game->GetPlayer()->CurrentXP(),game->GetPlayer()->NextLevelXPRequired());
game->GetPlayer()->AddXP(game->GetPlayer()->GetAccumulatedXP()); game->GetPlayer()->AddXP(game->GetPlayer()->GetAccumulatedXP());
for(const ItemMapData&data:game->GetCurrentMap().GetStageLoot()){ for(const ItemMapData&data:game->GetCurrentMap().GetStageLoot()){
uint8_t amountDiff=data.maxAmt-data.minAmt; uint8_t amountDiff=data.maxAmt-data.minAmt;

@ -34,3 +34,5 @@ January 31st
- Save window position and size - Save window position and size
- Fullscreen toggle - Fullscreen toggle
- Condense stage track (loading times)

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

@ -115,10 +115,10 @@ void Monster::STRATEGY::WOLF(Monster&m,float fElapsedTime,std::string strategy){
m.target=m.path.GetSplinePoint(m.pathIndex).pos; m.target=m.path.GetSplinePoint(m.pathIndex).pos;
geom2d::line<float>moveTowardsLine=geom2d::line(m.pos,m.path.GetSplinePoint(m.pathIndex).pos); geom2d::line<float>moveTowardsLine=geom2d::line(m.pos,m.path.GetSplinePoint(m.pathIndex).pos);
if(moveTowardsLine.length()>4.f){ if(moveTowardsLine.length()>1.f){
m.UpdateFacingDirection(moveTowardsLine.end); m.UpdateFacingDirection(moveTowardsLine.end);
}
m.SetPos(m.pos+moveTowardsLine.vector().norm()*100*fElapsedTime*m.GetMoveSpdMult()); m.SetPos(m.pos+moveTowardsLine.vector().norm()*100*fElapsedTime*m.GetMoveSpdMult());
}
}break; }break;
} }
} }

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.1" class="Map" orientation="orthogonal" renderorder="right-down" width="148" height="131" tilewidth="24" tileheight="24" infinite="0" nextlayerid="6" nextobjectid="68"> <map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="148" height="131" tilewidth="24" tileheight="24" infinite="0" nextlayerid="6" nextobjectid="68">
<properties> <properties>
<property name="Backdrop" propertytype="Backdrop" value="forest"/> <property name="Backdrop" propertytype="Backdrop" value="forest"/>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/> <property name="Background Music" propertytype="BGM" value="foresty1_1"/>
@ -415,7 +415,7 @@
</layer> </layer>
<objectgroup id="5" name="Spawn Zones"> <objectgroup id="5" name="Spawn Zones">
<object id="1" name="Player Spawn" type="PlayerSpawnLocation" x="96" y="360" width="24" height="24"/> <object id="1" name="Player Spawn" type="PlayerSpawnLocation" x="96" y="360" width="24" height="24"/>
<object id="2" name="End Zone" type="EndZone" x="3191" y="1507" width="120" height="120"/> <object id="2" name="End Zone" type="EndZone" x="77.8813" y="303.88" width="120" height="120"/>
<object id="3" name="Spawn Group 1" type="SpawnGroup" x="670" y="157" width="506" height="492"> <object id="3" name="Spawn Group 1" type="SpawnGroup" x="670" y="157" width="506" height="492">
<ellipse/> <ellipse/>
</object> </object>

@ -71,6 +71,7 @@ Images
GFX_Exclamation = exclamation.png GFX_Exclamation = exclamation.png
GFX_Ursule2 = monsters/Ursule Mother of Bears2.png GFX_Ursule2 = monsters/Ursule Mother of Bears2.png
GFX_Wisp = wisp.png GFX_Wisp = wisp.png
GFX_XPBar = xpbar.png
# Ability Icons # Ability Icons
GFX_Warrior_BattleCry_Icon = Ability Icons/battlecry.png GFX_Warrior_BattleCry_Icon = Ability Icons/battlecry.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Loading…
Cancel
Save