diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj index 41d7c4e0..96f73d9a 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj @@ -504,6 +504,10 @@ + + + + diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters index 07e10e25..bdb58ff7 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters @@ -453,6 +453,9 @@ Header Files\Interface + + Header Files\Interface + diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index 6cdfa217..ddfb0b0f 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.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(CHARACTER_MENU,"Level Class Display")->SetLabel(std::format("Lv{} {}",game->GetPlayer()->Level(),game->GetPlayer()->GetClassName())); Player::moneyListeners=moneyListeners; } diff --git a/Adventures in Lestoria/CharacterMenuWindow.cpp b/Adventures in Lestoria/CharacterMenuWindow.cpp index 0b12bd85..2ffe053f 100644 --- a/Adventures in Lestoria/CharacterMenuWindow.cpp +++ b/Adventures in Lestoria/CharacterMenuWindow.cpp @@ -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{{0,28},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; characterMenuWindow->ADD("Character Rotating Display",CharacterRotatingDisplay)(geom2d::rect{{118,18},{130,windowSize.y-28}},GFX[classutils::GetClassInfo(game->GetPlayer()->GetClassName()).classFullImgName].Decal())END; + characterMenuWindow->ADD("Level Class Display",MenuLabel)(geom2d::rect{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{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::arraydisplayAttrs{ "Health", "Attack", diff --git a/Adventures in Lestoria/InventoryConsumableWindow.cpp b/Adventures in Lestoria/InventoryConsumableWindow.cpp index e9e0e70f..264d270b 100644 --- a/Adventures in Lestoria/InventoryConsumableWindow.cpp +++ b/Adventures in Lestoria/InventoryConsumableWindow.cpp @@ -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_ptrselectedButton=DYNAMIC_POINTER_CAST(*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") diff --git a/Adventures in Lestoria/InventoryScrollableWindowComponent.h b/Adventures in Lestoria/InventoryScrollableWindowComponent.h index 805fca82..628e543b 100644 --- a/Adventures in Lestoria/InventoryScrollableWindowComponent.h +++ b/Adventures in Lestoria/InventoryScrollableWindowComponent.h @@ -66,17 +66,21 @@ protected: const std::function*const onInventorySlotsUpdate; const std::function*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::rectrect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,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::rectrect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,std::functioninventoryButtonHoverAction,std::functioninventoryButtonMouseOutAction,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{ diff --git a/Adventures in Lestoria/Key.cpp b/Adventures in Lestoria/Key.cpp index e0bb59f1..9602085d 100644 --- a/Adventures in Lestoria/Key.cpp +++ b/Adventures in Lestoria/Key.cpp @@ -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. diff --git a/Adventures in Lestoria/LevelCompleteWindow.cpp b/Adventures in Lestoria/LevelCompleteWindow.cpp index dbc3d69f..16a62f94 100644 --- a/Adventures in Lestoria/LevelCompleteWindow.cpp +++ b/Adventures in Lestoria/LevelCompleteWindow.cpp @@ -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{{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{{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{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{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{{0,32},{windowSize.size.x-80.f,72}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; levelCompleteWindow->ADD("Monster Loot Label",MenuLabel)(geom2d::rect{{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(type,"Monster Loot Window")->GetComponents(); - auto itemsWindow=Component(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(menuType,inventoryComponentName)->GetComponents(); \ + auto itemsWindow=Component(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_ptrselectedButton=DYNAMIC_POINTER_CAST(*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(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(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(menuType,inventoryComponentName)->GetComponents(); \ + auto itemsWindow=Component(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_ptrselectedButton=DYNAMIC_POINTER_CAST(*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(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(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(menuType,inventoryComponentName)->GetComponents(); \ + auto itemsWindow=Component(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_ptrselectedButton=DYNAMIC_POINTER_CAST(*component); \ + int rowBaseIndex=(selectedButton.lock()->inventoryIndex/invWidth)*invWidth; \ + int newRowIndex=selectedButton.lock()->inventoryIndex-1; \ + if(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(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(menuType,inventoryComponentName)->GetComponents(); \ + auto itemsWindow=Component(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_ptrselectedButton=DYNAMIC_POINTER_CAST(*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(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(menuType,inventoryComponentName)->GetComponents(); \ + auto itemsWindow=Component(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_ptrselectedButton=DYNAMIC_POINTER_CAST(*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(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(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(menuType,inventoryComponentName)->GetComponents(); \ + auto itemsWindow=Component(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_ptrselectedButton=DYNAMIC_POINTER_CAST(*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(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(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_ptrselectedButton=DYNAMIC_POINTER_CAST(*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(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(menuType,inventoryComponentName)->GetComponents(); \ + auto itemsWindow=Component(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_ptrselectedButton=DYNAMIC_POINTER_CAST(*component); \ + int rowBaseIndex=(selectedButton.lock()->inventoryIndex/invWidth)*invWidth; \ + int newRowIndex=selectedButton.lock()->inventoryIndex-1; \ + if(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(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(menuType,inventoryComponentName)->GetComponents(); \ + auto itemsWindow=Component(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_ptrselectedButton=DYNAMIC_POINTER_CAST(*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(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(type,"Monster Loot Window"); + auto stageLoot=Component(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(type,"Monster Loot Window"); + auto stageLoot=Component(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(type,"Monster Loot Window"); + auto stageLoot=Component(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"; + } + },}}, }); } \ No newline at end of file diff --git a/Adventures in Lestoria/MenuComponent.h b/Adventures in Lestoria/MenuComponent.h index 99a3f4b1..d1e05b73 100644 --- a/Adventures in Lestoria/MenuComponent.h +++ b/Adventures in Lestoria/MenuComponent.h @@ -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{ diff --git a/Adventures in Lestoria/MenuLabel.h b/Adventures in Lestoria/MenuLabel.h index 712780b9..7bb44ffe 100644 --- a/Adventures in Lestoria/MenuLabel.h +++ b/Adventures in Lestoria/MenuLabel.h @@ -49,6 +49,7 @@ protected: float scale=1; bool shadow=false; bool centered=true; + bool rightAlign=false; bool proportional=true; bool runOnLabelChangeFunc=false; std::functiononLabelChangeFunc; @@ -59,7 +60,7 @@ public: this->onLabelChangeFunc=onLabelChangeFunc; } inline MenuLabel(geom2d::rectrect,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){ diff --git a/Adventures in Lestoria/MenuType.h b/Adventures in Lestoria/MenuType.h index 181c24da..106bc9a8 100644 --- a/Adventures in Lestoria/MenuType.h +++ b/Adventures in Lestoria/MenuType.h @@ -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 diff --git a/Adventures in Lestoria/Player.cpp b/Adventures in Lestoria/Player.cpp index 9a6d6fc0..42649619 100644 --- a/Adventures in Lestoria/Player.cpp +++ b/Adventures in Lestoria/Player.cpp @@ -52,6 +52,8 @@ All rights reserved. #include #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(MenuType::CHARACTER_MENU,"XP Bar")->ResetProgressBar(game->GetPlayer()->CurrentXP(),game->GetPlayer()->NextLevelXPRequired()); +} + +void Player::SetXP(const uint32_t xp){ + currentLevelXP=xp; + Component(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(CHARACTER_MENU,"Level Class Display")->SetLabel(std::format("Lv{} {}",game->GetPlayer()->Level(),game->GetPlayer()->GetClassName())); + Component(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. diff --git a/Adventures in Lestoria/Player.h b/Adventures in Lestoria/Player.h index ff6a9a82..9574ecda 100644 --- a/Adventures in Lestoria/Player.h +++ b/Adventures in Lestoria/Player.h @@ -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; diff --git a/Adventures in Lestoria/ProgressBar.h b/Adventures in Lestoria/ProgressBar.h new file mode 100644 index 00000000..d2314e57 --- /dev/null +++ b/Adventures in Lestoria/ProgressBar.h @@ -0,0 +1,90 @@ +#pragma region License +/* +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2024 Joshua Sigona + +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::rectrect,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); + } +}; \ No newline at end of file diff --git a/Adventures in Lestoria/SaveFile.cpp b/Adventures in Lestoria/SaveFile.cpp index 35168722..8562d11f 100644 --- a/Adventures in Lestoria/SaveFile.cpp +++ b/Adventures in Lestoria/SaveFile.cpp @@ -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()); } diff --git a/Adventures in Lestoria/State_LevelComplete.cpp b/Adventures in Lestoria/State_LevelComplete.cpp index 4ddac0f3..c717d215 100644 --- a/Adventures in Lestoria/State_LevelComplete.cpp +++ b/Adventures in Lestoria/State_LevelComplete.cpp @@ -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(MenuType::LEVEL_COMPLETE,"Level EXP Gain Outline")->SetLabel(std::format("+{} Exp",game->GetPlayer()->GetAccumulatedXP())); + Component(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; diff --git a/Adventures in Lestoria/TODO.txt b/Adventures in Lestoria/TODO.txt index 079b559d..c7c542a7 100644 --- a/Adventures in Lestoria/TODO.txt +++ b/Adventures in Lestoria/TODO.txt @@ -33,4 +33,6 @@ January 31st - Save window position and size -- Fullscreen toggle \ No newline at end of file +- Fullscreen toggle + +- Condense stage track (loading times) \ No newline at end of file diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index 34002381..572852db 100644 --- a/Adventures in Lestoria/Version.h +++ b/Adventures in Lestoria/Version.h @@ -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 diff --git a/Adventures in Lestoria/Wolf.cpp b/Adventures in Lestoria/Wolf.cpp index b2ff7017..95f9aa02 100644 --- a/Adventures in Lestoria/Wolf.cpp +++ b/Adventures in Lestoria/Wolf.cpp @@ -115,10 +115,10 @@ void Monster::STRATEGY::WOLF(Monster&m,float fElapsedTime,std::string strategy){ m.target=m.path.GetSplinePoint(m.pathIndex).pos; geom2d::linemoveTowardsLine=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; } } \ No newline at end of file diff --git a/Adventures in Lestoria/assets/Campaigns/1_B1.tmx b/Adventures in Lestoria/assets/Campaigns/1_B1.tmx index 375d2e18..1e7b33e0 100644 --- a/Adventures in Lestoria/assets/Campaigns/1_B1.tmx +++ b/Adventures in Lestoria/assets/Campaigns/1_B1.tmx @@ -1,5 +1,5 @@ - + @@ -415,7 +415,7 @@ - + diff --git a/Adventures in Lestoria/assets/config/gfx/gfx.txt b/Adventures in Lestoria/assets/config/gfx/gfx.txt index 1abd82bc..ebccaba3 100644 --- a/Adventures in Lestoria/assets/config/gfx/gfx.txt +++ b/Adventures in Lestoria/assets/config/gfx/gfx.txt @@ -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 diff --git a/Adventures in Lestoria/assets/xpbar.png b/Adventures in Lestoria/assets/xpbar.png new file mode 100644 index 00000000..c580e6ff Binary files /dev/null and b/Adventures in Lestoria/assets/xpbar.png differ diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index af66dc56..59238b59 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ