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. 438
      Adventures in Lestoria/LevelCompleteWindow.cpp
  9. 11
      Adventures in Lestoria/MenuComponent.h
  10. 9
      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. 4
      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="util.h" />
<ClInclude Include="Version.h" />
<ClInclude Include="ProgressBar.h">
<SubType>
</SubType>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Ability.cpp" />

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

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

@ -48,6 +48,7 @@ All rights reserved.
#include "ScrollableWindowComponent.h"
#include "RowItemDisplay.h"
#include "SoundEffect.h"
#include "ProgressBar.h"
INCLUDE_game
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("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{
"Health",
"Attack",

@ -169,7 +169,6 @@ void Menu::InitializeConsumableInventoryWindow(){
int newRowIndex=selectedButton.lock()->inventoryIndex-invWidth; /*Moving up moves the cursor up an entire row.*/ \
if(newRowIndex<0){
#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)); \
/*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); \
int rowBaseIndex=(selectedButton.lock()->inventoryIndex/invWidth)*invWidth; \
int newRowIndex=selectedButton.lock()->inventoryIndex+1; \
if(newRowIndex>rowBaseIndex+invWidth-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); \
@ -303,7 +303,9 @@ void Menu::InitializeConsumableInventoryWindow(){
returnData=*component; \
} \
}else
#define DEFAULT_WRAPPING_BEHAVIOR newRowIndex-=invWidth;
#define DEFAULT_WRAPPING_BEHAVIOR \
int rowBaseIndex=(selectedButton.lock()->inventoryIndex/invWidth)*invWidth; \
newRowIndex=rowBaseIndex;
#pragma endregion
#define SUBCLASS MenuItemButton
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 addButtonOnSlotUpdate;
CompactText compact=COMPACT;
InventoryWindowOptions options;
std::string itemNameLabelName;
std::string itemDescriptionLabelName;
bool inventoryButtonsActive=true;
private:
InventoryWindowOptions options;
int invWidth;
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)
: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)
: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{
ScrollableWindowComponent::Update(game);
bool noneHovered=true;
@ -98,6 +102,15 @@ public:
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:
virtual inline void Cleanup()override final{

@ -551,16 +551,10 @@ const bool Input::AxesActive(){
if(gamepad->stillConnected){
if(fabs(gamepad->getAxis(GPAxes::RX))>0.f){
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){
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.

@ -38,11 +38,11 @@ All rights reserved.
#include "Menu.h"
#include "AdventuresInLestoria.h"
#include "MenuLabel.h"
#include "MenuComponent.h"
#include "InventoryScrollableWindowComponent.h"
#include "PopupMenuLabel.h"
#include "Unlock.h"
#include "State_OverworldMap.h"
#include "ProgressBar.h"
INCLUDE_game
@ -51,7 +51,10 @@ void Menu::InitializeLevelCompleteWindow(){
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 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
{"Monster Loot Window",{
.up=[](MenuType type,Data&returnData){
auto&selection=Menu::menus[type]->GetSelection();
auto&itemsList=Component<InventoryScrollableWindowComponent>(type,"Monster Loot Window")->GetComponents();
auto itemsWindow=Component<InventoryScrollableWindowComponent>(type,"Monster Loot Window");
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();
#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){
#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{
returnData=selectedButton;
}
}
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){
#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,"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;
}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,"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{
int invWidth=int((itemsWindow->rect.size.x-12)/(float(itemsWindow->options.size.x)+itemsWindow->options.padding));
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.
if(newRowIndex<0){
//This means we have to wrap around.
returnData="OK Button";
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));
//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;
returnData=selectedButton;
}
}
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,"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="OK Button";
returnData="Next Button";
}
},
.down="Load Game Button",}},
{"Load Game Button",{
.up="New Game Button",
.down="Quit Game Button",}},
{"Quit Game Button",{
.up="Load Game Button",
.down="New Game 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{
NONE= 0b0'0000'0000,
LEFT_ALIGN= 0b0'0000'0001, //Labels are centered by default.
SHADOW= 0b0'0000'0010, //Adds shadows to the label text.
OUTLINE= 0b0'0000'0100, //Adds an outline around the component.
BACKGROUND= 0b0'0000'1000, //Renders the background of the menu theme for this 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.
FIXED_WIDTH_FONT= 0b0'0010'0000, //Uses a fixed-width font (instead of a proportional one) for text rendering.
RIGHT_ALIGN= 0b0'0000'0010, //Labels are centered by default.
SHADOW= 0b0'0000'0100, //Adds shadows to the label text.
OUTLINE= 0b0'0000'1000, //Adds an outline around the component.
BACKGROUND= 0b0'0001'0000, //Renders the background of the menu theme for this component.
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{

@ -49,6 +49,7 @@ protected:
float scale=1;
bool shadow=false;
bool centered=true;
bool rightAlign=false;
bool proportional=true;
bool runOnLabelChangeFunc=false;
std::function<void(std::string_view newLabel)>onLabelChangeFunc;
@ -59,7 +60,7 @@ public:
this->onLabelChangeFunc=onLabelChangeFunc;
}
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;
this->background=attributes&ComponentAttr::BACKGROUND;
showDefaultLabel=false;
@ -95,7 +96,11 @@ protected:
vf2d drawPos=vf2d{-1,0}+rect.middle()-vf2d{labelTextSize}/2; //Assume centered.
if(!centered){
drawPos=vf2d{rect.pos.x+2,rect.middle().y-labelTextSize.y/2}; //We should at least vertically align here.
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.
}
}
if(shadow){

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

@ -52,6 +52,8 @@ All rights reserved.
#include <string_view>
#include "SoundEffect.h"
#include "olcPGEX_Gamepad.h"
#include "ProgressBar.h"
#include "MenuLabel.h"
INCLUDE_MONSTER_DATA
INCLUDE_MONSTER_LIST
@ -1213,6 +1215,16 @@ void Player::AddXP(const uint32_t xpGain){
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(){
@ -1226,6 +1238,11 @@ const uint8_t Player::LevelCap()const{
const uint8_t Player::Level()const{
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{
return currentLevelXP;
}
@ -1283,16 +1300,10 @@ const vf2d Player::GetAimingLocation(){
if(gamepad->stillConnected){
if(fabs(gamepad->getAxis(GPAxes::RX))>0.f){
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){
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.

@ -91,7 +91,6 @@ class Player{
friend class State_GameRun;
friend class Inventory;
friend void ItemOverlay::Draw();
friend class SaveFile;
public:
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
@ -241,6 +240,9 @@ public:
void Knockup(float duration);
const vf2d GetAimingLocation();
const vf2d GetWorldAimingLocation();
void SetXP(const uint32_t xp);
void SetTotalXPEarned(const uint32_t totalXP);
void SetLevel(uint8_t newLevel);
private:
int hp="Warrior.BaseHealth"_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->GetPlayer()->level=loadFile["Player"]["Level"].GetInt();
game->GetPlayer()->SetLevel(loadFile["Player"]["Level"].GetInt());
game->GetPlayer()->SetMoney(loadFile["Player"]["Money"].GetInt());
game->GetPlayer()->currentLevelXP=loadFile["Player"]["Current EXP"].GetInt();
game->GetPlayer()->totalXPEarned=loadFile["Player"]["Total EXP"].GetInt();
game->GetPlayer()->SetXP(loadFile["Player"]["Current EXP"].GetInt());
game->GetPlayer()->SetTotalXPEarned(loadFile["Player"]["Total EXP"].GetInt());
for(auto&[key,data]:loadFile["Player"]["Base Stats"].GetOrderedKeys()){
game->GetPlayer()->SetBaseStat(key,data.GetReal());
}

@ -41,6 +41,7 @@ All rights reserved.
#include "Menu.h"
#include "MenuLabel.h"
#include "SaveFile.h"
#include "ProgressBar.h"
INCLUDE_MONSTER_LIST
INCLUDE_game
@ -50,6 +51,7 @@ void State_LevelComplete::OnStateChange(GameState*prevState){
Menu::CloseAllMenus();
}
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());
for(const ItemMapData&data:game->GetCurrentMap().GetStageLoot()){
uint8_t amountDiff=data.maxAmt-data.minAmt;

@ -33,4 +33,6 @@ January 31st
- 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_MINOR 3
#define VERSION_PATCH 0
#define VERSION_BUILD 7010
#define VERSION_BUILD 7041
#define stringify(a) stringify_(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;
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.SetPos(m.pos+moveTowardsLine.vector().norm()*100*fElapsedTime*m.GetMoveSpdMult());
}
m.SetPos(m.pos+moveTowardsLine.vector().norm()*100*fElapsedTime*m.GetMoveSpdMult());
}break;
}
}

@ -1,5 +1,5 @@
<?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>
<property name="Backdrop" propertytype="Backdrop" value="forest"/>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/>
@ -415,7 +415,7 @@
</layer>
<objectgroup id="5" name="Spawn Zones">
<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">
<ellipse/>
</object>

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Loading…
Cancel
Save