Merge pull request 'MenuComponentRefactor' (#30) from MenuComponentRefactor into master

Reviewed-on: #30
pull/35/head
sigonasr2 11 months ago
commit 848d0e4816
  1. 12
      .gitignore
  2. 4
      .vscode/settings.json
  3. BIN
      Adventures in Lestoria/Adventures in Lestoria.data
  4. 75
      Adventures in Lestoria/Adventures in Lestoria.html
  5. 1
      Adventures in Lestoria/Adventures in Lestoria.js
  6. BIN
      Adventures in Lestoria/Adventures in Lestoria.wasm
  7. 13
      Adventures in Lestoria/AdventuresInLestoria.cpp
  8. 50
      Adventures in Lestoria/BlacksmithCraftingWindow.cpp
  9. 22
      Adventures in Lestoria/BuyItemWindow.cpp
  10. 12
      Adventures in Lestoria/C++/scripts/web.sh
  11. 20
      Adventures in Lestoria/CharacterInfoWindow.cpp
  12. 128
      Adventures in Lestoria/CharacterMenuWindow.cpp
  13. 28
      Adventures in Lestoria/ClassSelectionWindow.cpp
  14. 22
      Adventures in Lestoria/ConsumableCraftItemWindow.cpp
  15. 34
      Adventures in Lestoria/ConsumableCraftingWindow.cpp
  16. 18
      Adventures in Lestoria/CraftItemWindow.cpp
  17. 5
      Adventures in Lestoria/EncountersSpawnListScrollableWindowComponent.h
  18. 15
      Adventures in Lestoria/Error.h
  19. 32
      Adventures in Lestoria/InventoryConsumableWindow.cpp
  20. 18
      Adventures in Lestoria/InventoryCreator.cpp
  21. 10
      Adventures in Lestoria/InventoryScrollableWindowComponent.h
  22. 38
      Adventures in Lestoria/InventoryWindow.cpp
  23. 14
      Adventures in Lestoria/ItemLoadoutWindow.cpp
  24. 28
      Adventures in Lestoria/LevelCompleteWindow.cpp
  25. 6
      Adventures in Lestoria/LoadGameWindow.cpp
  26. 6
      Adventures in Lestoria/MainMenuWindow.cpp
  27. 333
      Adventures in Lestoria/Menu.cpp
  28. 78
      Adventures in Lestoria/Menu.h
  29. 6
      Adventures in Lestoria/MenuAnimatedIconToggleButton.h
  30. 45
      Adventures in Lestoria/MenuComponent.cpp
  31. 8
      Adventures in Lestoria/MenuComponent.h
  32. 8
      Adventures in Lestoria/MenuItemButton.h
  33. 84
      Adventures in Lestoria/MerchantWindow.cpp
  34. 16
      Adventures in Lestoria/OverworldMapLevelWindow.cpp
  35. 10
      Adventures in Lestoria/OverworldMenuWindow.cpp
  36. 16
      Adventures in Lestoria/Player.cpp
  37. 4
      Adventures in Lestoria/Player.h
  38. 14
      Adventures in Lestoria/RowInventoryScrollableWindowComponent.h
  39. 6
      Adventures in Lestoria/SaveFile.cpp
  40. 8
      Adventures in Lestoria/SaveFileWindow.cpp
  41. 118
      Adventures in Lestoria/ScrollableWindowComponent.h
  42. 22
      Adventures in Lestoria/SellItemWindow.cpp
  43. 1
      Adventures in Lestoria/TODO.txt
  44. 22
      Adventures in Lestoria/Toggleable.h
  45. 8
      Adventures in Lestoria/UserIDMenu.cpp
  46. 2
      Adventures in Lestoria/Version.h

12
.gitignore vendored

@ -383,3 +383,15 @@ Crawler/out
/Adventures in Lestoria/x64 /Adventures in Lestoria/x64
/Adventures in Lestoria/saves /Adventures in Lestoria/saves
/Adventures in Lestoria/assets/tmpsaves /Adventures in Lestoria/assets/tmpsaves
a.out
build/CMakeCache.txt
build/.cmake/api/v1/query/client-vscode/query.json
build/CMakeFiles/cmake.check_cache
build/CMakeFiles/3.16.3/CMakeCCompiler.cmake
build/CMakeFiles/3.16.3/CMakeCXXCompiler.cmake
build/CMakeFiles/3.16.3/CMakeDetermineCompilerABI_C.bin
build/CMakeFiles/3.16.3/CMakeDetermineCompilerABI_CXX.bin
build/CMakeFiles/3.16.3/CMakeSystem.cmake
build/CMakeFiles/3.16.3/CompilerIdC/CMakeCCompilerId.c
build/CMakeFiles/3.16.3/CompilerIdCXX/CMakeCXXCompilerId.cpp
test.cpp

@ -89,6 +89,8 @@
"regex": "cpp", "regex": "cpp",
"valarray": "cpp", "valarray": "cpp",
"*.inc": "cpp", "*.inc": "cpp",
"future": "cpp" "future": "cpp",
"any": "cpp",
"source_location": "cpp"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 MiB

@ -0,0 +1,75 @@
<!doctype html>
<html lang="en-us">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Emscripten-Generated Code</title>
<style>
html,body { width: 100%; height: 100%; }
body { font-family: arial; margin: 0; padding: 0; background: #000; }
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
div.emscripten_border { border: none; }
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
canvas.emscripten { border: 0px none; background-color: black; }
</style>
</head>
<body>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
<script type='text/javascript'>
var Module = {
preRun: [],
postRun: [],
canvas: (function() {
var canvas = document.getElementById('canvas');
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
// application robust, you may want to override this behavior before shipping!
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
return canvas;
})(),
};
</script>
<script async type="text/javascript" src="Adventures in Lestoria.js"></script>
<script type="text/javascript">
Module.canvas.addEventListener("resize", (e) => {
var viewWidth = e.detail.width;
var viewHeight = e.detail.width / Module._olc_WindowAspectRatio;
if(viewHeight > e.detail.height)
{
viewHeight = e.detail.height;
viewWidth = e.detail.height * Module._olc_WindowAspectRatio;
}
// update dom attributes
Module.canvas.setAttribute("width", viewWidth);
Module.canvas.setAttribute("height", viewHeight);
var top = (e.detail.height - viewHeight) / 2;
var left = (e.detail.width - viewWidth) / 2;
// update styles
Module.canvas.style.position = "fixed";
Module.canvas.style.top = top.toString() + "px";
Module.canvas.style.left = left.toString() + "px";
Module.canvas.style.width = "";
Module.canvas.style.height = "";
// trigger PGE update
Module._olc_PGE_UpdateWindowSize(viewWidth, viewHeight);
// ensure canvas has focus
Module.canvas.focus();
e.preventDefault();
});
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

@ -1462,7 +1462,6 @@ void AiL::RenderHud(){
if(!ISBLANK(GetLoadoutItem(0))){ if(!ISBLANK(GetLoadoutItem(0))){
DrawShadowStringDecal({0,92},"Loadout Slot 1 Qty: "+std::to_string(GetLoadoutItem(0).lock()->Amt())); DrawShadowStringDecal({0,92},"Loadout Slot 1 Qty: "+std::to_string(GetLoadoutItem(0).lock()->Amt()));
} }
DrawShadowStringDecal({0,1},"Selection: "+Menu::menus[INVENTORY_CONSUMABLES]->selection.str());
DrawShadowStringDecal({0,12},"Button Hold Time: "+std::to_string(Menu::menus[INVENTORY_CONSUMABLES]->buttonHoldTime)); DrawShadowStringDecal({0,12},"Button Hold Time: "+std::to_string(Menu::menus[INVENTORY_CONSUMABLES]->buttonHoldTime));
} }
#endif #endif
@ -2077,7 +2076,7 @@ void AiL::ChangePlayerClass(Class cl){
uint8_t levelCap=player->levelCap; uint8_t levelCap=player->levelCap;
uint32_t totalXPEarned=player->totalXPEarned; uint32_t totalXPEarned=player->totalXPEarned;
uint32_t currentLevelXP=player->currentLevelXP; uint32_t currentLevelXP=player->currentLevelXP;
std::set<MenuComponent*>moneyListeners=Player::moneyListeners; std::vector<std::weak_ptr<MenuComponent>>moneyListeners=Player::moneyListeners;
EntityStats previousStats=player->stats; EntityStats previousStats=player->stats;
size_t cooldownSoundInstance=player->cooldownSoundInstance; size_t cooldownSoundInstance=player->cooldownSoundInstance;
switch(cl){ switch(cl){
@ -2549,12 +2548,6 @@ const MapName&AiL::GetCurrentMapName()const{
} }
void AiL::ValidateGameStatus(){ void AiL::ValidateGameStatus(){
if(IToggleable::uninitializedToggleGroupItems.size()>0){
for(IToggleable*item:IToggleable::uninitializedToggleGroupItems){
std::cout<<"\tUninitialized Toggle Item Ptr: 0x"<<std::hex<<item<<std::endl;
}
ERR("Error TOGGLE!!! Please turn on debug_toggleable_items in configuration.txt and re-run the program to see which toggleable item did not properly get a toggleable group set.");
}
if(Unlock::unlocks.size()==0){ if(Unlock::unlocks.size()==0){
ERR("WARNING! There are no unlocks set! This was probably not intentional! It means no areasa on the overworld are accessible!"); ERR("WARNING! There are no unlocks set! This was probably not intentional! It means no areasa on the overworld are accessible!");
} }
@ -2582,8 +2575,8 @@ int AiL::GetCurrentChapter(){
void AiL::SetChapter(int chapter){ void AiL::SetChapter(int chapter){
this->chapter=chapter; this->chapter=chapter;
for(MenuComponent*component:Menu::chapterListeners){ for(std::weak_ptr<MenuComponent>component:Menu::chapterListeners){
component->OnChapterUpdate(chapter); component.lock()->OnChapterUpdate(chapter);
} }
} }

@ -30,7 +30,7 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE. SUCH DAMAGE.
Portions of this software are copyright © 2023 The FreeType Portions of this software are copyright <EFBFBD> 2023 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information. Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved. All rights reserved.
*/ */
@ -72,29 +72,29 @@ void Menu::InitializeBlacksmithCraftingWindow(){
}); });
#pragma endregion #pragma endregion
auto weaponTab=blacksmithWindow->ADD("Weapon Tab",MenuComponent)({{2,0},{blacksmithWindow->size.x/2-4,24}},"Weapon",[](MenuFuncData data){ auto weaponTab=blacksmithWindow->ADD("Weapon Tab",MenuComponent)(geom2d::rect<float>{{2,0},{blacksmithWindow->size.x/2-4,24}},"Weapon",[](MenuFuncData data){
Component<MenuComponent>(BLACKSMITH,"Armor Tab")->selected=false; Component<MenuComponent>(BLACKSMITH,"Armor Tab")->selected=false;
Component<RowInventoryScrollableWindowComponent>(BLACKSMITH,"Weapon Inventory Display")->Enable(true); Component<RowInventoryScrollableWindowComponent>(BLACKSMITH,"Weapon Inventory Display")->Enable(true);
Component<RowInventoryScrollableWindowComponent>(BLACKSMITH,"Armor Inventory Display")->Enable(false); Component<RowInventoryScrollableWindowComponent>(BLACKSMITH,"Armor Inventory Display")->Enable(false);
data.component->selected=true; data.component.lock()->selected=true;
return true; return true;
})END; })END;
weaponTab->selected=true; weaponTab->selected=true;
weaponTab->selectionType=SelectionType::HIGHLIGHT; weaponTab->selectionType=SelectionType::HIGHLIGHT;
auto armorTab=blacksmithWindow->ADD("Armor Tab",MenuComponent)({{blacksmithWindow->size.x/2+2,0},{blacksmithWindow->size.x/2-4,24}},"Armor",[](MenuFuncData data){ auto armorTab=blacksmithWindow->ADD("Armor Tab",MenuComponent)(geom2d::rect<float>{{blacksmithWindow->size.x/2+2,0},{blacksmithWindow->size.x/2-4,24}},"Armor",[](MenuFuncData data){
Component<MenuComponent>(BLACKSMITH,"Weapon Tab")->selected=false; Component<MenuComponent>(BLACKSMITH,"Weapon Tab")->selected=false;
Component<RowInventoryScrollableWindowComponent>(BLACKSMITH,"Weapon Inventory Display")->Enable(false); Component<RowInventoryScrollableWindowComponent>(BLACKSMITH,"Weapon Inventory Display")->Enable(false);
Component<RowInventoryScrollableWindowComponent>(BLACKSMITH,"Armor Inventory Display")->Enable(true); Component<RowInventoryScrollableWindowComponent>(BLACKSMITH,"Armor Inventory Display")->Enable(true);
data.component->selected=true; data.component.lock()->selected=true;
return true; return true;
})END; })END;
armorTab->selectionType=SelectionType::HIGHLIGHT; armorTab->selectionType=SelectionType::HIGHLIGHT;
#pragma region Weapon Inventory Display #pragma region Weapon Inventory Display
auto weaponsDisplay=blacksmithWindow->ADD("Weapon Inventory Display",RowInventoryScrollableWindowComponent)({{2,28},{220,blacksmithWindow->size.y-44}},"Item Name Label","Item Description Label", auto weaponsDisplay=blacksmithWindow->ADD("Weapon Inventory Display",RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{2,28},{220,blacksmithWindow->size.y-44}},"Item Name Label","Item Description Label",
[](MenuFuncData data){ [](MenuFuncData data){
RowItemDisplay*comp=DYNAMIC_CAST<RowItemDisplay*>(data.component); std::weak_ptr<RowItemDisplay>comp=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
const std::weak_ptr<Item>item=comp->GetItem(); const std::weak_ptr<Item>item=comp.lock()->GetItem();
std::string label=""; std::string label="";
if(item.lock()->EnhancementIsPossible()&&item.lock()->GetEnhancementInfo().size()>item.lock()->EnhancementLevel()+1){ if(item.lock()->EnhancementIsPossible()&&item.lock()->GetEnhancementInfo().size()>item.lock()->EnhancementLevel()+1){
@ -109,8 +109,8 @@ void Menu::InitializeBlacksmithCraftingWindow(){
return true; return true;
}, },
[](MenuFuncData data){ [](MenuFuncData data){
RowItemDisplay*rowItem=DYNAMIC_CAST<RowItemDisplay*>(data.component); std::weak_ptr<RowItemDisplay>rowItem=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
Component<MenuItemItemButton>(BLACKSMITH,"Item Icon")->SetItem(rowItem->GetItem()); Component<MenuItemItemButton>(BLACKSMITH,"Item Icon")->SetItem(rowItem.lock()->GetItem());
return true; return true;
}, },
[](MenuFuncData data){ [](MenuFuncData data){
@ -118,18 +118,18 @@ void Menu::InitializeBlacksmithCraftingWindow(){
return true; return true;
}, },
InventoryCreator::RowPlayerWeapons_InventoryUpdate, InventoryCreator::RowPlayerWeapons_InventoryUpdate,
{.padding=1,.size={207,28}} InventoryWindowOptions{.padding=1,.size={207,28}}
)END; )END;
AddInventoryListener(weaponsDisplay,"Equipment"); AddInventoryListener(weaponsDisplay,"Equipment");
weaponsDisplay->SetCompactDescriptions(CRAFTING_INFO); weaponsDisplay->SetCompactDescriptions(CRAFTING_INFO);
#pragma endregion #pragma endregion
#pragma region Armor Inventory Display #pragma region Armor Inventory Display
auto armorDisplay=blacksmithWindow->ADD("Armor Inventory Display",RowInventoryScrollableWindowComponent)({{2,28},{220,blacksmithWindow->size.y-44}},"Item Name Label","Item Description Label", auto armorDisplay=blacksmithWindow->ADD("Armor Inventory Display",RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{2,28},{220,blacksmithWindow->size.y-44}},"Item Name Label","Item Description Label",
[](MenuFuncData data){ [](MenuFuncData data){
Menu::OpenMenu(CRAFT_ITEM); Menu::OpenMenu(CRAFT_ITEM);
RowItemDisplay*comp=DYNAMIC_CAST<RowItemDisplay*>(data.component); std::weak_ptr<RowItemDisplay>comp=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
const std::weak_ptr<Item>item=comp->GetItem(); const std::weak_ptr<Item>item=comp.lock()->GetItem();
std::string label=""; std::string label="";
if(item.lock()->EnhancementIsPossible()&&item.lock()->GetEnhancementInfo().size()>item.lock()->EnhancementLevel()+1){ if(item.lock()->EnhancementIsPossible()&&item.lock()->GetEnhancementInfo().size()>item.lock()->EnhancementLevel()+1){
@ -143,8 +143,8 @@ void Menu::InitializeBlacksmithCraftingWindow(){
return true; return true;
}, },
[](MenuFuncData data){ [](MenuFuncData data){
RowItemDisplay*rowItem=DYNAMIC_CAST<RowItemDisplay*>(data.component); std::weak_ptr<RowItemDisplay>rowItem=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
Component<MenuItemItemButton>(BLACKSMITH,"Item Icon")->SetItem(rowItem->GetItem()); Component<MenuItemItemButton>(BLACKSMITH,"Item Icon")->SetItem(rowItem.lock()->GetItem());
return true; return true;
}, },
[](MenuFuncData data){ [](MenuFuncData data){
@ -152,7 +152,7 @@ void Menu::InitializeBlacksmithCraftingWindow(){
return true; return true;
}, },
InventoryCreator::RowPlayerArmor_InventoryUpdate, InventoryCreator::RowPlayerArmor_InventoryUpdate,
{.padding=1,.size={207,28}} InventoryWindowOptions{.padding=1,.size={207,28}}
)END; )END;
AddInventoryListener(armorDisplay,"Equipment"); AddInventoryListener(armorDisplay,"Equipment");
armorDisplay->Enable(false); armorDisplay->Enable(false);
@ -161,25 +161,25 @@ void Menu::InitializeBlacksmithCraftingWindow(){
#pragma region Inventory Description #pragma region Inventory Description
float inventoryDescriptionWidth=blacksmithWindow->pos.x+blacksmithWindow->size.x-26-224; float inventoryDescriptionWidth=blacksmithWindow->pos.x+blacksmithWindow->size.x-26-224;
blacksmithWindow->ADD("Item Description Outline",MenuLabel)({{224,28},{inventoryDescriptionWidth,blacksmithWindow->size.y-44}},"",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; blacksmithWindow->ADD("Item Description Outline",MenuLabel)(geom2d::rect<float>{{224,28},{inventoryDescriptionWidth,blacksmithWindow->size.y-44}},"",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
blacksmithWindow->ADD("Item Icon",MenuItemItemButton)({{226+inventoryDescriptionWidth/2-24,30},{48,48}},Item::BLANK,MenuType::ENUM_END,DO_NOTHING,"","",IconButtonAttr::NOT_SELECTABLE)END; blacksmithWindow->ADD("Item Icon",MenuItemItemButton)(geom2d::rect<float>{{226+inventoryDescriptionWidth/2-24,30},{48,48}},Item::BLANK,MenuType::ENUM_END,DO_NOTHING,"","",IconButtonAttr::NOT_SELECTABLE)END;
blacksmithWindow->ADD("Item Name Label",MenuLabel)({{226,84},{inventoryDescriptionWidth-6,12}},"",0.75f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; blacksmithWindow->ADD("Item Name Label",MenuLabel)(geom2d::rect<float>{{226,84},{inventoryDescriptionWidth-6,12}},"",0.75f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
blacksmithWindow->ADD("Item Description Label",MenuLabel)({{226,94},{inventoryDescriptionWidth-6,blacksmithWindow->size.y-44-66}},"",0.5f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; blacksmithWindow->ADD("Item Description Label",MenuLabel)(geom2d::rect<float>{{226,94},{inventoryDescriptionWidth-6,blacksmithWindow->size.y-44-66}},"",0.5f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
#pragma endregion #pragma endregion
#pragma region Money Display #pragma region Money Display
vf2d moneyIconPos={224+inventoryDescriptionWidth-24,28+blacksmithWindow->size.y-44+6}; vf2d moneyIconPos={224+inventoryDescriptionWidth-24,28+blacksmithWindow->size.y-44+6};
auto moneyIcon=blacksmithWindow->ADD("Money Icon",MenuIconButton)({moneyIconPos,{24,24}},GFX["money.png"].Decal(),DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE|IconButtonAttr::NO_BACKGROUND)END; auto moneyIcon=blacksmithWindow->ADD("Money Icon",MenuIconButton)(geom2d::rect<float>{moneyIconPos,{24,24}},GFX["money.png"].Decal(),DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE|IconButtonAttr::NO_BACKGROUND)END;
std::string moneyText=std::to_string(game->GetPlayer()->GetMoney()); std::string moneyText=std::to_string(game->GetPlayer()->GetMoney());
vf2d moneyTextSize=game->GetTextSizeProp(moneyText)*2; vf2d moneyTextSize=game->GetTextSizeProp(moneyText)*2;
auto moneyDisplay=blacksmithWindow->ADD("Money Label",PlayerMoneyLabel)({moneyIconPos-vf2d{2+moneyTextSize.x,-2},moneyTextSize},2,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN|ComponentAttr::FIT_TO_LABEL)END; auto moneyDisplay=blacksmithWindow->ADD("Money Label",PlayerMoneyLabel)(geom2d::rect<float>{moneyIconPos-vf2d{2+moneyTextSize.x,-2},moneyTextSize},2,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN|ComponentAttr::FIT_TO_LABEL)END;
moneyDisplay->SetRightAlignment(true); moneyDisplay->SetRightAlignment(true);
Player::AddMoneyListener(moneyDisplay); Player::AddMoneyListener(moneyDisplay);
#pragma endregion #pragma endregion
blacksmithWindow->ADD("Leave Button",MenuComponent)({{blacksmithWindow->size.x/2-48,28+blacksmithWindow->size.y-44+6},{96,24}},"Leave",MenuType::ENUM_END, blacksmithWindow->ADD("Leave Button",MenuComponent)(geom2d::rect<float>{{blacksmithWindow->size.x/2-48,28+blacksmithWindow->size.y-44+6},{96,24}},"Leave",MenuType::ENUM_END,
[](MenuFuncData data){ [](MenuFuncData data){
Menu::CloseMenu(); Menu::CloseMenu();
return true; return true;
},{2,2})END; },vf2d{2,2})END;
} }

@ -62,27 +62,27 @@ void Menu::InitializeBuyItemWindow(){
Component<MenuComponent>(BUY_ITEM,"Purchase Button")->SetGrayedOut(!canPurchase); Component<MenuComponent>(BUY_ITEM,"Purchase Button")->SetGrayedOut(!canPurchase);
}; };
buyItemWindow->ADD("Item Purchase Header",MenuLabel)({{2,2},{188,12}},"Buying ",1,ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND|ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END; buyItemWindow->ADD("Item Purchase Header",MenuLabel)(geom2d::rect<float>{{2,2},{188,12}},"Buying ",1.f,ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND|ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END;
buyItemWindow->ADD("Price Per Item Label",MenuLabel)({{4,18},{188,12}},"Price Per Item",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; buyItemWindow->ADD("Price Per Item Label",MenuLabel)(geom2d::rect<float>{{4,18},{188,12}},"Price Per Item",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
buyItemWindow->ADD("Amount to Buy Label",MenuLabel)({{4,34},{188,12}},"Amount to Buy",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; buyItemWindow->ADD("Amount to Buy Label",MenuLabel)(geom2d::rect<float>{{4,34},{188,12}},"Amount to Buy",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
buyItemWindow->ADD("Price Label",MenuLabel)({{4,50},{188,12}},"Total Cost",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; buyItemWindow->ADD("Price Label",MenuLabel)(geom2d::rect<float>{{4,50},{188,12}},"Total Cost",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
buyItemWindow->ADD("Price per item Amount Label",MenuLabel)({{buyItemWindow->size.x/2+28,18},{72,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END; buyItemWindow->ADD("Price per item Amount Label",MenuLabel)(geom2d::rect<float>{{buyItemWindow->size.x/2+28,18},{72,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END;
buyItemWindow->ADD("Amount to buy Amount Label",MenuLabel)({{buyItemWindow->size.x/2+48,34},{32,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::FIT_TO_LABEL)END; buyItemWindow->ADD("Amount to buy Amount Label",MenuLabel)(geom2d::rect<float>{{buyItemWindow->size.x/2+48,34},{32,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::FIT_TO_LABEL)END;
buyItemWindow->ADD("Increase buy amount Button",MenuComponent)({{buyItemWindow->size.x/2+80+2,34},{12,12}},"+",[&](MenuFuncData data){ buyItemWindow->ADD("Increase buy amount Button",MenuComponent)(geom2d::rect<float>{{buyItemWindow->size.x/2+80+2,34},{12,12}},"+",[&](MenuFuncData data){
UpdateMenu(GetQuantity()+1); UpdateMenu(GetQuantity()+1);
return true; return true;
})END; })END;
buyItemWindow->ADD("Decrease buy amount Button",MenuComponent)({{buyItemWindow->size.x/2+48-14,34},{12,12}},"-",[](MenuFuncData data){ buyItemWindow->ADD("Decrease buy amount Button",MenuComponent)(geom2d::rect<float>{{buyItemWindow->size.x/2+48-14,34},{12,12}},"-",[](MenuFuncData data){
UpdateMenu(GetQuantity()-1); UpdateMenu(GetQuantity()-1);
return true; return true;
})END; })END;
buyItemWindow->ADD("Total Price Amount Label",MenuLabel)({{buyItemWindow->size.x/2+28,50},{72,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END; buyItemWindow->ADD("Total Price Amount Label",MenuLabel)(geom2d::rect<float>{{buyItemWindow->size.x/2+28,50},{72,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END;
buyItemWindow->ADD("Purchase Button",MenuComponent)({{buyItemWindow->size.x/2+18,70},{64,12}},"Purchase",[&](MenuFuncData data){ buyItemWindow->ADD("Purchase Button",MenuComponent)(geom2d::rect<float>{{buyItemWindow->size.x/2+18,70},{64,12}},"Purchase",[&](MenuFuncData data){
Merchant&merchant=Merchant::GetCurrentTravelingMerchant(); Merchant&merchant=Merchant::GetCurrentTravelingMerchant();
const std::string&item=Component<MenuLabel>(BUY_ITEM,"Item Purchase Header")->GetString(A::ITEM_NAME); const std::string&item=Component<MenuLabel>(BUY_ITEM,"Item Purchase Header")->GetString(A::ITEM_NAME);
merchant.PurchaseItem(item,GetQuantity()); merchant.PurchaseItem(item,GetQuantity());
@ -90,7 +90,7 @@ void Menu::InitializeBuyItemWindow(){
Menu::CloseMenu(); Menu::CloseMenu();
return true; return true;
})END; })END;
buyItemWindow->ADD("Cancel Button",MenuComponent)({{buyItemWindow->size.x/2-82,70},{64,12}},"Cancel",[](MenuFuncData data){ buyItemWindow->ADD("Cancel Button",MenuComponent)(geom2d::rect<float>{{buyItemWindow->size.x/2-82,70},{64,12}},"Cancel",[](MenuFuncData data){
Menu::CloseMenu(); Menu::CloseMenu();
return true; return true;
})END; })END;

@ -15,19 +15,19 @@ then
em++ -std=c++20 -O2 ${EMSCRIPTEN_CUSTOM_PARAMS} -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_SDL_MIXER=2 -s USE_LIBPNG=1 -s USE_FREETYPE=1 -c pixelGameEngine.cpp -o pixelGameEngine_wasm.o em++ -std=c++20 -O2 ${EMSCRIPTEN_CUSTOM_PARAMS} -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_SDL_MIXER=2 -s USE_LIBPNG=1 -s USE_FREETYPE=1 -c pixelGameEngine.cpp -o pixelGameEngine_wasm.o
fi fi
if [ -d "assets" ]; then if [ -d "assets" ]; then
em++ -std=c++20 -O2 ${EMSCRIPTEN_CUSTOM_PARAMS} -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_SDL_MIXER=2 -s USE_LIBPNG=1 -s USE_FREETYPE=1 $(find . -type f -name "*.cpp" -not -path "./test/*" -not -name "pixelGameEngine.cpp") pixelGameEngine_wasm.o -o ${PROJECT_NAME}.html --preload-file ./assets em++ -std=c++20 -O2 ${EMSCRIPTEN_CUSTOM_PARAMS} -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_SDL_MIXER=2 -s USE_LIBPNG=1 -s USE_FREETYPE=1 $(find . -type f -name "*.cpp" -not -path "./discord-files/*" -not -path "./test/*" -not -name "pixelGameEngine.cpp") pixelGameEngine_wasm.o -o "${PROJECT_NAME}.html" --preload-file ./assets
else else
em++ -std=c++20 -O2 ${EMSCRIPTEN_CUSTOM_PARAMS} -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_SDL_MIXER=2 -s USE_LIBPNG=1 -s USE_FREETYPE=1 $(find . -type f -name "*.cpp" -not -path "./test/*" -not -name "pixelGameEngine.cpp") pixelGameEngine_wasm.o -o ${PROJECT_NAME}.html em++ -std=c++20 -O2 ${EMSCRIPTEN_CUSTOM_PARAMS} -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_SDL_MIXER=2 -s USE_LIBPNG=1 -s USE_FREETYPE=1 $(find . -type f -name "*.cpp" -not -path "./discord-files/*" -not -path "./test/*" -not -name "pixelGameEngine.cpp") pixelGameEngine_wasm.o -o "${PROJECT_NAME}.html"
fi fi
cp buildtemplate.html ${PROJECT_NAME}.html cp buildtemplate.html "${PROJECT_NAME}.html"
sed -i "s/_REPLACEME_/$PROJECT_NAME.js/" ${PROJECT_NAME}.html sed -i "s/_REPLACEME_/$PROJECT_NAME.js/" "${PROJECT_NAME}.html"
if [[ "$1" == "headless" || "$2" == "headless" ]]; then if [[ "$1" == "headless" || "$2" == "headless" ]]; then
echo "Running as headless web server" echo "Running as headless web server"
emrun --no_browser ${PROJECT_NAME}.html emrun --no_browser "${PROJECT_NAME}.html"
else else
emrun --serve_after_close ${PROJECT_NAME}.html emrun --serve_after_close "${PROJECT_NAME}.html"
fi fi
if [ $? -eq 127 ] if [ $? -eq 127 ]

@ -53,23 +53,23 @@ void Menu::InitializeClassInfoWindow(){
Menu*classSelectionWindow=Menu::menus[CLASS_SELECTION]; Menu*classSelectionWindow=Menu::menus[CLASS_SELECTION];
ClassInfo data=classutils::GetClassInfo(classSelectionWindow->S(A::CLASS_SELECTION)); ClassInfo data=classutils::GetClassInfo(classSelectionWindow->S(A::CLASS_SELECTION));
auto label=classInfoWindow->ADD("Class Name",MenuLabel)({{0,0},{classInfoWindow->size.x-1,24}},data.className,2,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; auto label=classInfoWindow->ADD("Class Name",MenuLabel)(geom2d::rect<float>{{0,0},{classInfoWindow->size.x-1,24}},data.className,2,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
classInfoWindow->ADD("Rotating Character Display",CharacterRotatingDisplay)({{15,48},{72,120}},GFX[data.classFullImgName].Decal())END; classInfoWindow->ADD("Rotating Character Display",CharacterRotatingDisplay)(geom2d::rect<float>{{15,48},{72,120}},GFX[data.classFullImgName].Decal())END;
vf2d healthDisplayLabelPos={classInfoWindow->size.x/3,label->GetPos().y+24}; vf2d healthDisplayLabelPos={classInfoWindow->size.x/3,label->GetPos().y+24};
vf2d labelSize={2*classInfoWindow->size.x/3-1,16}; vf2d labelSize={2*classInfoWindow->size.x/3-1,16};
classInfoWindow->ADD("Base Stats Text",MenuLabel)({{0,label->GetPos().y+24},{classInfoWindow->size.x/3,labelSize.y}},"Base Stats",1,ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END; classInfoWindow->ADD("Base Stats Text",MenuLabel)(geom2d::rect<float>{{0,label->GetPos().y+24},{classInfoWindow->size.x/3,labelSize.y}},"Base Stats",1,ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END;
classInfoWindow->ADD("Health Display Text",MenuLabel)({healthDisplayLabelPos+vf2d{0,16*0},labelSize},"Health: "+std::to_string(data.baseHealth)+" + "+std::to_string(data.healthGrowthRate).substr(0,3)+" per level",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END; classInfoWindow->ADD("Health Display Text",MenuLabel)(geom2d::rect<float>{healthDisplayLabelPos+vf2d{0,16*0},labelSize},"Health: "+std::to_string(data.baseHealth)+" + "+std::to_string(data.healthGrowthRate).substr(0,3)+" per level",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END;
classInfoWindow->ADD("Attack Display Text",MenuLabel)({healthDisplayLabelPos+vf2d{0,16*1},labelSize},"Attack: "+std::to_string(data.baseAtk)+" + "+std::to_string(data.atkGrowthRate).substr(0,3)+" per level",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END; classInfoWindow->ADD("Attack Display Text",MenuLabel)(geom2d::rect<float>{healthDisplayLabelPos+vf2d{0,16*1},labelSize},"Attack: "+std::to_string(data.baseAtk)+" + "+std::to_string(data.atkGrowthRate).substr(0,3)+" per level",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END;
vf2d abilityIconOffsets = {0,32}; vf2d abilityIconOffsets = {0,32};
classInfoWindow->ADD("Ability 1 Display",CharacterAbilityPreviewComponent)({healthDisplayLabelPos+vf2d{0,32*0}+abilityIconOffsets,labelSize*vf2d{1,2}},data.ability1)END; classInfoWindow->ADD("Ability 1 Display",CharacterAbilityPreviewComponent)(geom2d::rect<float>{healthDisplayLabelPos+vf2d{0,32*0}+abilityIconOffsets,labelSize*vf2d{1,2}},data.ability1)END;
classInfoWindow->ADD("Ability 2 Display",CharacterAbilityPreviewComponent)({healthDisplayLabelPos+vf2d{0,32*1}+abilityIconOffsets,labelSize*vf2d{1,2}},data.ability2)END; classInfoWindow->ADD("Ability 2 Display",CharacterAbilityPreviewComponent)(geom2d::rect<float>{healthDisplayLabelPos+vf2d{0,32*1}+abilityIconOffsets,labelSize*vf2d{1,2}},data.ability2)END;
classInfoWindow->ADD("Ability 3 Display",CharacterAbilityPreviewComponent)({healthDisplayLabelPos+vf2d{0,32*2}+abilityIconOffsets,labelSize*vf2d{1,2}},data.ability3)END; classInfoWindow->ADD("Ability 3 Display",CharacterAbilityPreviewComponent)(geom2d::rect<float>{healthDisplayLabelPos+vf2d{0,32*2}+abilityIconOffsets,labelSize*vf2d{1,2}},data.ability3)END;
classInfoWindow->ADD("Right Click Ability Display",CharacterAbilityPreviewComponent)({healthDisplayLabelPos+vf2d{0,32*3}+abilityIconOffsets,labelSize*vf2d{1,2}},data.rightClickAbility)END; classInfoWindow->ADD("Right Click Ability Display",CharacterAbilityPreviewComponent)(geom2d::rect<float>{healthDisplayLabelPos+vf2d{0,32*3}+abilityIconOffsets,labelSize*vf2d{1,2}},data.rightClickAbility)END;
classInfoWindow->ADD("Back Button",MenuComponent)({{classInfoWindow->center().x/2,healthDisplayLabelPos.y+32*4+abilityIconOffsets.y+12},{classInfoWindow->size.x/2,16}},"Back",[](MenuFuncData data){Menu::CloseMenu();return true;})END; classInfoWindow->ADD("Back Button",MenuComponent)(geom2d::rect<float>{{classInfoWindow->center().x/2,healthDisplayLabelPos.y+32*4+abilityIconOffsets.y+12},{classInfoWindow->size.x/2,16}},"Back",[](MenuFuncData data){Menu::CloseMenu();return true;})END;
} }

@ -30,7 +30,7 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE. SUCH DAMAGE.
Portions of this software are copyright © 2023 The FreeType Portions of this software are copyright <EFBFBD> 2023 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information. Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved. All rights reserved.
*/ */
@ -58,9 +58,9 @@ void Menu::InitializeCharacterMenuWindow(){
vf2d windowSize=game->GetScreenSize()-vf2d{52,52}; vf2d windowSize=game->GetScreenSize()-vf2d{52,52};
Menu*characterMenuWindow=CreateMenu(CHARACTER_MENU,CENTERED,windowSize); Menu*characterMenuWindow=CreateMenu(CHARACTER_MENU,CENTERED,windowSize);
characterMenuWindow->ADD("Character Label",MenuLabel)({{0,-4},{float(windowSize.x)-1,24}},"Character",2,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; characterMenuWindow->ADD("Character Label",MenuLabel)(geom2d::rect<float>{{0,-4},{float(windowSize.x)-1,24}},"Character",2,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
characterMenuWindow->ADD("Equip Slot Outline",MenuComponent)({{0,28},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; characterMenuWindow->ADD("Equip Slot Outline",MenuComponent)(geom2d::rect<float>{{0,28},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
characterMenuWindow->ADD("Character Rotating Display",CharacterRotatingDisplay)({{135,28},{90,windowSize.y-48}},GFX[classutils::GetClassInfo(game->GetPlayer()->GetClassName()).classFullImgName].Decal())END; characterMenuWindow->ADD("Character Rotating Display",CharacterRotatingDisplay)(geom2d::rect<float>{{135,28},{90,windowSize.y-48}},GFX[classutils::GetClassInfo(game->GetPlayer()->GetClassName()).classFullImgName].Decal())END;
const static std::array<std::string,7>displayAttrs{ const static std::array<std::string,7>displayAttrs{
"Health", "Health",
@ -73,22 +73,22 @@ void Menu::InitializeCharacterMenuWindow(){
}; };
characterMenuWindow->ADD("Equip Selection Outline",MenuComponent)({{123,28},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END characterMenuWindow->ADD("Equip Selection Outline",MenuComponent)(geom2d::rect<float>{{123,28},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END
->Enable(false); ->Enable(false);
characterMenuWindow->ADD("Equip List",ScrollableWindowComponent)({{123,28},{120,windowSize.y-37-24}})DEPTH -1 END characterMenuWindow->ADD("Equip List",ScrollableWindowComponent)(geom2d::rect<float>{{123,28},{120,windowSize.y-37-24}})DEPTH -1 END
->Enable(false); ->Enable(false);
characterMenuWindow->ADD("Equip Selection Bottom Outline",MenuComponent)({{123,28+(windowSize.y-37-24)},{120,24}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END characterMenuWindow->ADD("Equip Selection Bottom Outline",MenuComponent)(geom2d::rect<float>{{123,28+(windowSize.y-37-24)},{120,24}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END
->Enable(false); ->Enable(false);
auto equipSelectionSelectButton=characterMenuWindow->ADD("Equip Selection Select Button",MenuComponent)({{123+12,28+(windowSize.y-37-24)+6},{96,12}},"Select", auto equipSelectionSelectButton=characterMenuWindow->ADD("Equip Selection Select Button",MenuComponent)(geom2d::rect<float>{{123+12,28+(windowSize.y-37-24)+6},{96,12}},"Select",
[](MenuFuncData data){ [](MenuFuncData data){
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Outline")->Enable(false); Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Outline")->Enable(false);
Component<ScrollableWindowComponent>(data.component->parentMenu,"Equip List")->Enable(false); Component<ScrollableWindowComponent>(data.component.lock()->parentMenu,"Equip List")->Enable(false);
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Bottom Outline")->Enable(false); Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Bottom Outline")->Enable(false);
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Select Button")->Enable(false); Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Select Button")->Enable(false);
Component<CharacterRotatingDisplay>(data.component->parentMenu,"Character Rotating Display")->Enable(true); Component<CharacterRotatingDisplay>(data.component.lock()->parentMenu,"Character Rotating Display")->Enable(true);
for(int counter=0;const std::string&attribute:displayAttrs){ for(int counter=0;const std::string&attribute:displayAttrs){
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label"); std::weak_ptr<StatLabel>statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label");
statDisplayLabel->SetStatChangeAmt(0); statDisplayLabel.lock()->SetStatChangeAmt(0);
} }
equipmentWindowOpened=false; equipmentWindowOpened=false;
return true; return true;
@ -117,9 +117,9 @@ void Menu::InitializeCharacterMenuWindow(){
} }
const static std::array<std::string,8>slotNames{"Helmet","Weapon","Armor","Gloves","Pants","Shoes","Ring 1","Ring 2"}; const static std::array<std::string,8>slotNames{"Helmet","Weapon","Armor","Gloves","Pants","Shoes","Ring 1","Ring 2"};
EquipSlot slot=EquipSlot(equipSlot); EquipSlot slot=EquipSlot(equipSlot);
auto equipmentSlot=characterMenuWindow->ADD("Equip Slot "+slotNames[i],EquipSlotButton)({{x,y+28},{24,24}},slot,MenuType::ENUM_END, auto equipmentSlot=characterMenuWindow->ADD("Equip Slot "+slotNames[i],EquipSlotButton)(geom2d::rect<float>{{x,y+28},{24,24}},slot,MenuType::ENUM_END,
[&](MenuFuncData data){ [&](MenuFuncData data){
EquipSlot slot=EquipSlot(data.component->I(Attribute::EQUIP_TYPE)); EquipSlot slot=EquipSlot(data.component.lock()->I(Attribute::EQUIP_TYPE));
const std::vector<std::shared_ptr<Item>>&equips=Inventory::get("Equipment"); const std::vector<std::shared_ptr<Item>>&equips=Inventory::get("Equipment");
const std::vector<std::shared_ptr<Item>>&accessories=Inventory::get("Accessories"); const std::vector<std::shared_ptr<Item>>&accessories=Inventory::get("Accessories");
@ -131,40 +131,36 @@ void Menu::InitializeCharacterMenuWindow(){
return it->GetEquipSlot()&slot; return it->GetEquipSlot()&slot;
}); });
ScrollableWindowComponent*equipList=Component<ScrollableWindowComponent>(data.component->parentMenu,"Equip List"); std::shared_ptr<ScrollableWindowComponent>equipList=Component<ScrollableWindowComponent>(data.component.lock()->parentMenu,"Equip List");
equipList->RemoveAllComponents(); equipList->RemoveAllComponents();
for(int counter=0;const std::weak_ptr<Item>it:availableEquipment){ for(int counter=0;const std::weak_ptr<Item>it:availableEquipment){
const static auto OppositeRingSlotDoesNotMatchCurrentEquip=[](RowItemDisplay*comp){ const static auto OppositeRingSlotDoesNotMatchCurrentEquip=[](std::weak_ptr<RowItemDisplay>comp){
EquipSlot slot=EquipSlot(comp->I(Attribute::EQUIP_TYPE)); EquipSlot slot=EquipSlot(comp.lock()->I(Attribute::EQUIP_TYPE));
std::weak_ptr<Item>otherItem; std::weak_ptr<Item>otherItem;
if(slot&EquipSlot::RING1)otherItem=Inventory::GetEquip(EquipSlot::RING2); if(slot&EquipSlot::RING1)otherItem=Inventory::GetEquip(EquipSlot::RING2);
else else
if(slot&EquipSlot::RING2)otherItem=Inventory::GetEquip(EquipSlot::RING1); if(slot&EquipSlot::RING2)otherItem=Inventory::GetEquip(EquipSlot::RING1);
return ISBLANK(otherItem)||(&*comp->GetItem().lock()!=&*otherItem.lock()); return ISBLANK(otherItem)||(&*comp.lock()->GetItem().lock()!=&*otherItem.lock());
}; };
auto equip=equipList->ADD("Equip Item "+std::to_string(counter),RowItemDisplay)({{2,2+counter*29.f},{120-15,28}},it, auto equip=equipList->ADD("Equip Item "+std::to_string(counter),RowItemDisplay)(geom2d::rect<float>{{2,2+counter*29.f},{120-15,28}},it,
[](MenuFuncData data){ [](MenuFuncData data){
RowItemDisplay*comp=DYNAMIC_CAST<RowItemDisplay*>(data.component); std::weak_ptr<RowItemDisplay>comp=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
if(comp!=nullptr){ if(!comp.expired()){
if(OppositeRingSlotDoesNotMatchCurrentEquip(comp)){ //If we find that the opposite ring slot is equipped to us, this would be an item swap or the exact same ring, therefore no stat calculations apply. if(OppositeRingSlotDoesNotMatchCurrentEquip(comp.lock())){ //If we find that the opposite ring slot is equipped to us, this would be an item swap or the exact same ring, therefore no stat calculations apply.
Inventory::EquipItem(comp->GetItem(),EquipSlot(comp->I(Attribute::EQUIP_TYPE))); Inventory::EquipItem(comp.lock()->GetItem(),EquipSlot(comp.lock()->I(Attribute::EQUIP_TYPE)));
SoundEffect::PlaySFX(comp->GetItem().lock()->UseSound(),SoundEffect::CENTERED); SoundEffect::PlaySFX(comp.lock()->GetItem().lock()->UseSound(),SoundEffect::CENTERED);
for(MenuComponent*button:((ScrollableWindowComponent*)data.parentComponent)->GetComponents()){ for(std::weak_ptr<MenuComponent>button:DYNAMIC_POINTER_CAST<ScrollableWindowComponent>(data.parentComponent.lock())->GetComponents()){
RowItemDisplay*comp=DYNAMIC_CAST<RowItemDisplay*>(button); std::weak_ptr<RowItemDisplay>comp=DYNAMIC_POINTER_CAST<RowItemDisplay>(button.lock());
if(comp!=nullptr){ comp.lock()->SetSelected(false);
comp->SetSelected(false);
}else{
ERR("WARNING! Attempting to cast a button that isn't a RowItemDisplay!");
}
} }
comp->SetSelected(true); comp.lock()->SetSelected(true);
for(int counter=0;const std::string&attribute:displayAttrs){ for(int counter=0;const std::string&attribute:displayAttrs){
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label"); std::shared_ptr<StatLabel>statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label");
statDisplayLabel->SetStatChangeAmt(0); statDisplayLabel->SetStatChangeAmt(0);
} }
MenuItemItemButton*equipButton=Component<MenuItemItemButton>(CHARACTER_MENU,"Equip Slot "+slotNames[data.parentComponent->I(A::INDEXED_THEME)]); std::shared_ptr<MenuItemItemButton>equipButton=Component<MenuItemItemButton>(CHARACTER_MENU,"Equip Slot "+slotNames[data.parentComponent.lock()->I(A::INDEXED_THEME)]);
equipButton->SetItem(comp->GetItem(),false); equipButton->SetItem(comp.lock()->GetItem(),false);
} }
}else{ }else{
ERR("WARNING! Attempting to cast a button that isn't a RowItemDisplay!"); ERR("WARNING! Attempting to cast a button that isn't a RowItemDisplay!");
@ -174,11 +170,11 @@ void Menu::InitializeCharacterMenuWindow(){
equip->SetHoverFunc( equip->SetHoverFunc(
[&](MenuFuncData data){ [&](MenuFuncData data){
RowItemDisplay*button=DYNAMIC_CAST<RowItemDisplay*>(data.component); std::weak_ptr<RowItemDisplay>button=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
if(button!=nullptr){ if(!button.expired()){
const std::weak_ptr<Item>buttonItem=button->GetItem(); const std::weak_ptr<Item>buttonItem=button.lock()->GetItem();
std::vector<float>statsBeforeEquip; std::vector<float>statsBeforeEquip;
EquipSlot slot=EquipSlot(button->I(Attribute::EQUIP_TYPE)); EquipSlot slot=EquipSlot(button.lock()->I(Attribute::EQUIP_TYPE));
for(const std::string&attribute:displayAttrs){ for(const std::string&attribute:displayAttrs){
statsBeforeEquip.push_back(game->GetPlayer()->GetStat(attribute)); statsBeforeEquip.push_back(game->GetPlayer()->GetStat(attribute));
} }
@ -188,12 +184,12 @@ void Menu::InitializeCharacterMenuWindow(){
if(slot==EquipSlot::RING1)otherItem=Inventory::GetEquip(EquipSlot::RING2); if(slot==EquipSlot::RING1)otherItem=Inventory::GetEquip(EquipSlot::RING2);
else else
if(slot==EquipSlot::RING2)otherItem=Inventory::GetEquip(EquipSlot::RING1); if(slot==EquipSlot::RING2)otherItem=Inventory::GetEquip(EquipSlot::RING1);
if(OppositeRingSlotDoesNotMatchCurrentEquip(button)){ //If we find that the opposite ring slot is equipped to us, this would be an item swap or the exact same ring, therefore no stat calculations apply. if(OppositeRingSlotDoesNotMatchCurrentEquip(button.lock())){ //If we find that the opposite ring slot is equipped to us, this would be an item swap or the exact same ring, therefore no stat calculations apply.
Inventory::EquipItem(buttonItem,slot); Inventory::EquipItem(buttonItem,slot);
for(int counter=0;const std::string&attribute:displayAttrs){ for(int counter=0;const std::string&attribute:displayAttrs){
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label"); std::weak_ptr<StatLabel>statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label");
int statChangeAmt=game->GetPlayer()->GetStat(attribute)-statsBeforeEquip[counter]; int statChangeAmt=game->GetPlayer()->GetStat(attribute)-statsBeforeEquip[counter];
statDisplayLabel->SetStatChangeAmt(statChangeAmt); statDisplayLabel.lock()->SetStatChangeAmt(statChangeAmt);
counter++; counter++;
} }
Inventory::UnequipItem(slot); Inventory::UnequipItem(slot);
@ -214,8 +210,8 @@ void Menu::InitializeCharacterMenuWindow(){
equip->SetMouseOutFunc( equip->SetMouseOutFunc(
[](MenuFuncData data){ [](MenuFuncData data){
for(int counter=0;const std::string&attribute:displayAttrs){ for(int counter=0;const std::string&attribute:displayAttrs){
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label"); std::weak_ptr<StatLabel>statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label");
statDisplayLabel->SetStatChangeAmt(0); statDisplayLabel.lock()->SetStatChangeAmt(0);
counter++; counter++;
} }
return true; return true;
@ -232,27 +228,27 @@ void Menu::InitializeCharacterMenuWindow(){
counter++; counter++;
} }
equipList->I(Attribute::INDEXED_THEME)=data.component->I(Attribute::INDEXED_THEME); equipList->I(Attribute::INDEXED_THEME)=data.component.lock()->I(Attribute::INDEXED_THEME);
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Outline")->Enable(true); Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Outline")->Enable(true);
equipList->Enable(true); equipList->Enable(true);
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Bottom Outline")->Enable(true); Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Bottom Outline")->Enable(true);
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Select Button")->Enable(true); Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Select Button")->Enable(true);
Component<CharacterRotatingDisplay>(data.component->parentMenu,"Character Rotating Display")->Enable(false); Component<CharacterRotatingDisplay>(data.component.lock()->parentMenu,"Character Rotating Display")->Enable(false);
equipmentWindowOpened=true; equipmentWindowOpened=true;
return true; return true;
},[](MenuFuncData data){//On Mouse Hover },[](MenuFuncData data){//On Mouse Hover
EquipSlot slot=DYNAMIC_CAST<EquipSlotButton*>(data.component)->GetSlot(); EquipSlot slot=DYNAMIC_POINTER_CAST<EquipSlotButton>(data.component.lock())->GetSlot();
const std::weak_ptr<Item>equip=Inventory::GetEquip(slot); const std::weak_ptr<Item>equip=Inventory::GetEquip(slot);
if(!ISBLANK(equip)){ if(!ISBLANK(equip)){
Component<CharacterRotatingDisplay>(data.component->parentMenu,"Character Rotating Display")->Enable(false); Component<CharacterRotatingDisplay>(data.component.lock()->parentMenu,"Character Rotating Display")->Enable(false);
} }
return true; return true;
},[](MenuFuncData data){//On Mouse Out },[](MenuFuncData data){//On Mouse Out
if(!equipmentWindowOpened){ if(!equipmentWindowOpened){
Component<MenuLabel>(data.component->parentMenu,"Item Equip Description")->SetLabel(""); Component<MenuLabel>(data.component.lock()->parentMenu,"Item Equip Description")->SetLabel("");
Component<MenuLabel>(data.component->parentMenu,"Item Equip Name")->Enable(false); Component<MenuLabel>(data.component.lock()->parentMenu,"Item Equip Name")->Enable(false);
Component<MenuLabel>(data.component->parentMenu,"Item Equip Description")->Enable(false); Component<MenuLabel>(data.component.lock()->parentMenu,"Item Equip Description")->Enable(false);
Component<CharacterRotatingDisplay>(data.component->parentMenu,"Character Rotating Display")->Enable(true); Component<CharacterRotatingDisplay>(data.component.lock()->parentMenu,"Character Rotating Display")->Enable(true);
} }
return true; return true;
},"Item Equip Name","Item Equip Description")END; },"Item Equip Name","Item Equip Description")END;
@ -262,26 +258,26 @@ void Menu::InitializeCharacterMenuWindow(){
equipmentSlot->SetShowQuantity(false); equipmentSlot->SetShowQuantity(false);
equipmentSlot->SetCompactDescriptions(false); equipmentSlot->SetCompactDescriptions(false);
equipSlot<<=1; equipSlot<<=1;
characterMenuWindow->ADD("Equip Label "+slotNames[i],PopupMenuLabel)({{labelX,labelY},{24,16}},slotNames[i],{0.5,1},ComponentAttr::SHADOW)END; characterMenuWindow->ADD("Equip Label "+slotNames[i],PopupMenuLabel)(geom2d::rect<float>{{labelX,labelY},{24,16}},slotNames[i],vf2d{0.5,1.f},ComponentAttr::SHADOW)END;
Menu::AddEquipStatListener(equipmentSlot); Menu::AddEquipStatListener(equipmentSlot);
} }
characterMenuWindow->ADD("Stat Display Outline",MenuComponent)({{245,28},{62,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; characterMenuWindow->ADD("Stat Display Outline",MenuComponent)(geom2d::rect<float>{{245,28},{62,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
int yOffset=0; int yOffset=0;
for(const std::string&attribute:displayAttrs){ for(const std::string&attribute:displayAttrs){
std::string attrStr=GetLabelText(ItemAttribute::Get(attribute)); std::string attrStr=GetLabelText(ItemAttribute::Get(attribute));
auto attrLabel=characterMenuWindow->ADD("Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label",StatLabel)({{245,28+2+float(yOffset)},{62,18}},ItemAttribute::Get(attribute),1,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END; auto attrLabel=characterMenuWindow->ADD("Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label",StatLabel)(geom2d::rect<float>{{245,28+2+float(yOffset)},{62,18}},ItemAttribute::Get(attribute),1,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END;
Menu::AddEquipStatListener(attrLabel); Menu::AddEquipStatListener(attrLabel);
yOffset+=20; yOffset+=20;
} }
characterMenuWindow->ADD("Back button",MenuComponent)({{windowSize.x/2-64,windowSize.y},{128,12}},"Back",[](MenuFuncData data){Menu::stack.pop_back();return true;})END; characterMenuWindow->ADD("Back button",MenuComponent)(geom2d::rect<float>{{windowSize.x/2-64,windowSize.y},{128,12}},"Back",[](MenuFuncData data){Menu::stack.pop_back();return true;})END;
auto itemNameDisplay=characterMenuWindow->ADD("Item Name",MenuLabel)({{0,28},{120,12}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END; auto itemNameDisplay=characterMenuWindow->ADD("Item Name",MenuLabel)(geom2d::rect<float>{{0,28},{120,12}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END;
auto itemDescriptionDisplay=characterMenuWindow->ADD("Item Description",MenuLabel)({{0,40},{120,windowSize.y-49}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW)END; auto itemDescriptionDisplay=characterMenuWindow->ADD("Item Description",MenuLabel)(geom2d::rect<float>{{0,40},{120,windowSize.y-49}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW)END;
auto itemEquipNameDisplay=characterMenuWindow->ADD("Item Equip Name",MenuLabel)({{123,28},{120,12}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END; auto itemEquipNameDisplay=characterMenuWindow->ADD("Item Equip Name",MenuLabel)(geom2d::rect<float>{{123,28},{120,12}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END;
auto itemEquipDescriptionDisplay=characterMenuWindow->ADD("Item Equip Description",MenuLabel)({{123,40},{120,windowSize.y-49}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW)END; auto itemEquipDescriptionDisplay=characterMenuWindow->ADD("Item Equip Description",MenuLabel)(geom2d::rect<float>{{123,40},{120,windowSize.y-49}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW)END;
itemNameDisplay->Enable(false); itemNameDisplay->Enable(false);
itemDescriptionDisplay->Enable(false); itemDescriptionDisplay->Enable(false);

@ -53,16 +53,16 @@ void Menu::InitializeClassSelectionWindow(){
vf2d outlineSize=classSelectionWindow->size-vf2d{13,13}; vf2d outlineSize=classSelectionWindow->size-vf2d{13,13};
classSelectionWindow->ADD("Class Selection Title Label",MenuLabel)({{4,20},{outlineSize.x,32}},"Choose a Character Class",2,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; classSelectionWindow->ADD("Class Selection Title Label",MenuLabel)(geom2d::rect<float>{{4,20},{outlineSize.x,32}},"Choose a Character Class",2,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
auto outline=classSelectionWindow->ADD("Outline Border",MenuLabel)({{4,4},outlineSize},"",1,ComponentAttr::OUTLINE)END; auto outline=classSelectionWindow->ADD("Outline Border",MenuLabel)(geom2d::rect<float>{{4,4},outlineSize},"",1,ComponentAttr::OUTLINE)END;
vf2d navigationButtonSize={24*2.5f,16}; vf2d navigationButtonSize={24*2.5f,16};
classSelectionWindow->ADD("Back Button",MenuComponent)({{4+2,outlineSize.y+4-navigationButtonSize.y-2},navigationButtonSize},"Back",[](MenuFuncData data){Menu::CloseMenu();return true;})END; classSelectionWindow->ADD("Back Button",MenuComponent)(geom2d::rect<float>{{4+2,outlineSize.y+4-navigationButtonSize.y-2},navigationButtonSize},"Back",[](MenuFuncData data){Menu::CloseMenu();return true;})END;
classSelectionWindow->ADD("Confirm",MenuComponent)({{outlineSize.x+4-navigationButtonSize.x-2,outlineSize.y+4-navigationButtonSize.y-2},navigationButtonSize},"Confirm",[](MenuFuncData data){ classSelectionWindow->ADD("Confirm",MenuComponent)(geom2d::rect<float>{{outlineSize.x+4-navigationButtonSize.x-2,outlineSize.y+4-navigationButtonSize.y-2},navigationButtonSize},"Confirm",[](MenuFuncData data){
std::string selectedClass=data.component->S(A::CLASS_SELECTION); std::string selectedClass=data.component.lock()->S(A::CLASS_SELECTION);
data.game->ChangePlayerClass(classutils::StringToClass(selectedClass)); data.game->ChangePlayerClass(classutils::StringToClass(selectedClass));
GameState::ChangeState(States::OVERWORLD_MAP); GameState::ChangeState(States::OVERWORLD_MAP);
return true; return true;
@ -93,7 +93,7 @@ void Menu::InitializeClassSelectionWindow(){
Witch::walk_s, Witch::walk_s,
}; };
std::vector<IToggleable*>toggleGroup; std::vector<std::weak_ptr<IToggleable>>toggleGroup;
for(int i=0;i<6;i++){ for(int i=0;i<6;i++){
std::string className=classNames[i]; std::string className=classNames[i];
@ -109,19 +109,19 @@ void Menu::InitializeClassSelectionWindow(){
vf2d backgroundSize={floor(outlineSize.y/3-buttonPadding.y*3),outlineSize.y/3-buttonPadding.y*3}; //The floor is for fixing a small pixel rounding bug. vf2d backgroundSize={floor(outlineSize.y/3-buttonPadding.y*3),outlineSize.y/3-buttonPadding.y*3}; //The floor is for fixing a small pixel rounding bug.
classSelectionWindow->ADD(className+" Background",MenuLabel)({backgroundOffsetPos,backgroundSize},"",1,ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; classSelectionWindow->ADD(className+" Background",MenuLabel)(geom2d::rect<float>{backgroundOffsetPos,backgroundSize},"",1,ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
classSelectionWindow->ADD(className+" Button",MenuComponent)({offsetPos,buttonSize},"Info",CLASS_INFO, classSelectionWindow->ADD(className+" Button",MenuComponent)(geom2d::rect<float>{offsetPos,buttonSize},"Info",CLASS_INFO,
[](MenuFuncData data){ [](MenuFuncData data){
data.menu.S(A::CLASS_SELECTION)=data.component->S(A::CLASS_SELECTION); data.menu.S(A::CLASS_SELECTION)=data.component.lock()->S(A::CLASS_SELECTION);
delete Menu::menus[CLASS_INFO]; delete Menu::menus[CLASS_INFO];
Menu::InitializeClassInfoWindow(); Menu::InitializeClassInfoWindow();
return true; return true;
})END })END
->S(A::CLASS_SELECTION)=className; ->S(A::CLASS_SELECTION)=className;
classSelectionWindow->ADD(className+" Label",MenuLabel)({backgroundOffsetPos,buttonSize},className,1,ComponentAttr::SHADOW)END; classSelectionWindow->ADD(className+" Label",MenuLabel)(geom2d::rect<float>{backgroundOffsetPos,buttonSize},className,1,ComponentAttr::SHADOW)END;
auto classSprite=classSelectionWindow->ADD(className+" Icon",MenuAnimatedIconToggleButton)({backgroundOffsetPos+vf2d{0,12},backgroundSize+vf2d{0,-buttonSize.y-12}},classAnimationName,[](MenuFuncData data){ auto classSprite=classSelectionWindow->ADD(className+" Icon",MenuAnimatedIconToggleButton)(geom2d::rect<float>{backgroundOffsetPos+vf2d{0,12},backgroundSize+vf2d{0,-buttonSize.y-12}},classAnimationName,[](MenuFuncData data){
data.menu.components["Confirm"]->Enable(true); data.menu.components["Confirm"]->Enable(true);
data.menu.components["Confirm"]->S(A::CLASS_SELECTION)=data.component->S(A::CLASS_SELECTION); data.menu.components["Confirm"]->S(A::CLASS_SELECTION)=data.component.lock()->S(A::CLASS_SELECTION);
return true; return true;
})END; })END;
@ -130,7 +130,7 @@ void Menu::InitializeClassSelectionWindow(){
toggleGroup.push_back(classSprite); toggleGroup.push_back(classSprite);
} }
for(IToggleable*item:toggleGroup){ for(std::weak_ptr<IToggleable>item:toggleGroup){
item->SetToggleGroup(toggleGroup); item.lock()->SetToggleGroup(toggleGroup);
} }
} }

@ -47,7 +47,7 @@ using A=Attribute;
void Menu::InitializeConsumableCraftItemWindow(){ void Menu::InitializeConsumableCraftItemWindow(){
Menu*consumableCraftItemWindow=CreateMenu(CONSUMABLE_CRAFT_ITEM,CENTERED,{240,96}); Menu*consumableCraftItemWindow=CreateMenu(CONSUMABLE_CRAFT_ITEM,CENTERED,{240,96});
consumableCraftItemWindow->ADD("Item Name Header",MenuLabel)({{2,-4},{consumableCraftItemWindow->size.x-4,12}},"Item Name",1.0f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; consumableCraftItemWindow->ADD("Item Name Header",MenuLabel)(geom2d::rect<float>{{2,-4},{consumableCraftItemWindow->size.x-4,12}},"Item Name",1.0f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
static auto GetQuantity=[&](){ static auto GetQuantity=[&](){
return std::stoi(Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Amount to Craft Amount Label")->GetLabel()); return std::stoi(Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Amount to Craft Amount Label")->GetLabel());
@ -65,28 +65,28 @@ void Menu::InitializeConsumableCraftItemWindow(){
Component<MenuComponent>(CONSUMABLE_CRAFT_ITEM,"Craft Button")->SetGrayedOut(!canCraft); Component<MenuComponent>(CONSUMABLE_CRAFT_ITEM,"Craft Button")->SetGrayedOut(!canCraft);
}; };
consumableCraftItemWindow->ADD("Amount to Craft Label",MenuLabel)({{4,34},{188,12}},"Craft Amount",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; consumableCraftItemWindow->ADD("Amount to Craft Label",MenuLabel)(geom2d::rect<float>{{4,34},{188,12}},"Craft Amount",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
consumableCraftItemWindow->ADD("Amount to Craft Amount Label",MenuLabel)({{consumableCraftItemWindow->size.x/2+48,34},{32,12}},"1" consumableCraftItemWindow->ADD("Amount to Craft Amount Label",MenuLabel)(geom2d::rect<float>{{consumableCraftItemWindow->size.x/2+48,34},{32,12}},"1"
,UpdateMenu,1.0f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::FIT_TO_LABEL)END; ,UpdateMenu,1.0f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::FIT_TO_LABEL)END;
consumableCraftItemWindow->ADD("Increase Craft amount Button",MenuComponent)({{consumableCraftItemWindow->size.x/2+80+2,34},{12,12}},"+",[&](MenuFuncData data){ consumableCraftItemWindow->ADD("Increase Craft amount Button",MenuComponent)(geom2d::rect<float>{{consumableCraftItemWindow->size.x/2+80+2,34},{12,12}},"+",[&](MenuFuncData data){
uint8_t qty=std::clamp(GetQuantity()+1,1,99); uint8_t qty=std::clamp(GetQuantity()+1,1,99);
Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Amount to Craft Amount Label")->SetLabel(std::to_string(qty)); Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Amount to Craft Amount Label")->SetLabel(std::to_string(qty));
return true; return true;
})END; })END;
consumableCraftItemWindow->ADD("Decrease Craft amount Button",MenuComponent)({{consumableCraftItemWindow->size.x/2+48-14,34},{12,12}},"-",[](MenuFuncData data){ consumableCraftItemWindow->ADD("Decrease Craft amount Button",MenuComponent)(geom2d::rect<float>{{consumableCraftItemWindow->size.x/2+48-14,34},{12,12}},"-",[](MenuFuncData data){
uint8_t qty=std::clamp(GetQuantity()-1,1,99); uint8_t qty=std::clamp(GetQuantity()-1,1,99);
Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Amount to Craft Amount Label")->SetLabel(std::to_string(qty)); Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Amount to Craft Amount Label")->SetLabel(std::to_string(qty));
return true; return true;
})END; })END;
consumableCraftItemWindow->ADD("Materials Requirement Outline",MenuComponent)({{2,86-18},{consumableCraftItemWindow->size.x-4,26}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; consumableCraftItemWindow->ADD("Materials Requirement Outline",MenuComponent)(geom2d::rect<float>{{2,86-18},{consumableCraftItemWindow->size.x-4,26}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
consumableCraftItemWindow->ADD("Required Materials Label",MenuLabel)({{4,80-18},{consumableCraftItemWindow->size.x-8,0}},"Required Materials",1.0f,ComponentAttr::SHADOW)END; consumableCraftItemWindow->ADD("Required Materials Label",MenuLabel)(geom2d::rect<float>{{4,80-18},{consumableCraftItemWindow->size.x-8,0}},"Required Materials",1.0f,ComponentAttr::SHADOW)END;
consumableCraftItemWindow->ADD("Required Materials List",RequiredMaterialsList)({{4,88-18},{consumableCraftItemWindow->size.x-8,22}},Item::BLANK)END; consumableCraftItemWindow->ADD("Required Materials List",RequiredMaterialsList)(geom2d::rect<float>{{4,88-18},{consumableCraftItemWindow->size.x-8,22}},Item::BLANK)END;
consumableCraftItemWindow->ADD("Back Button",MenuComponent)({{36,116-18},{48,12}},"Back",[](MenuFuncData data){Menu::CloseMenu();return true;})END; consumableCraftItemWindow->ADD("Back Button",MenuComponent)(geom2d::rect<float>{{36,116-18},{48,12}},"Back",[](MenuFuncData data){Menu::CloseMenu();return true;})END;
consumableCraftItemWindow->ADD("Craft Button",MenuComponent)({{consumableCraftItemWindow->size.x-84,116-18},{48,12}},"Craft",[](MenuFuncData data){ consumableCraftItemWindow->ADD("Craft Button",MenuComponent)(geom2d::rect<float>{{consumableCraftItemWindow->size.x-84,116-18},{48,12}},"Craft",[](MenuFuncData data){
const std::weak_ptr<Item>item=Component<RequiredMaterialsList>(CONSUMABLE_CRAFT_ITEM,"Required Materials List")->GetItem(); const std::weak_ptr<Item>item=Component<RequiredMaterialsList>(CONSUMABLE_CRAFT_ITEM,"Required Materials List")->GetItem();
uint8_t qty=stoi(Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Amount to Craft Amount Label")->GetLabel()); uint8_t qty=stoi(Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Amount to Craft Amount Label")->GetLabel());
@ -95,7 +95,7 @@ void Menu::InitializeConsumableCraftItemWindow(){
item.lock()->EnhanceItem(qty); item.lock()->EnhanceItem(qty);
} }
} }
data.component->SetGrayedOut(!item.lock()->CanEnhanceItem(qty)); data.component.lock()->SetGrayedOut(!item.lock()->CanEnhanceItem(qty));
return true; return true;
})END; })END;
} }

@ -30,7 +30,7 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE. SUCH DAMAGE.
Portions of this software are copyright © 2023 The FreeType Portions of this software are copyright <EFBFBD> 2023 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information. Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved. All rights reserved.
*/ */
@ -53,10 +53,10 @@ void Menu::InitializeConsumableCraftingWindow(){
Menu*consumableCraftingWindow=CreateMenu(CRAFT_CONSUMABLE,CENTERED,game->GetScreenSize()-vi2d{52,52}); Menu*consumableCraftingWindow=CreateMenu(CRAFT_CONSUMABLE,CENTERED,game->GetScreenSize()-vi2d{52,52});
#pragma region Craftables Inventory Display #pragma region Craftables Inventory Display
auto craftingItemsDisplay=consumableCraftingWindow->ADD("Crafting Inventory Display",RowInventoryScrollableWindowComponent)({{2,28},{220,consumableCraftingWindow->size.y-44}},"Item Name Label","Item Description Label", auto craftingItemsDisplay=consumableCraftingWindow->ADD("Crafting Inventory Display",RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{2,28},{220,consumableCraftingWindow->size.y-44}},"Item Name Label","Item Description Label",
[](MenuFuncData data){ [](MenuFuncData data){
RowItemDisplay*comp=DYNAMIC_CAST<RowItemDisplay*>(data.component); std::weak_ptr<RowItemDisplay>comp=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
const std::weak_ptr<Item>item=comp->GetItem(); const std::weak_ptr<Item>item=comp.lock()->GetItem();
Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Item Name Header")->GetString(A::ITEM_NAME)=item.lock()->ActualName(); Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Item Name Header")->GetString(A::ITEM_NAME)=item.lock()->ActualName();
Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Amount to Craft Amount Label")->SetLabel("1"); Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Amount to Craft Amount Label")->SetLabel("1");
Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Item Name Header")->SetLabel(std::format("Crafting {}",item.lock()->DisplayName())); Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Item Name Header")->SetLabel(std::format("Crafting {}",item.lock()->DisplayName()));
@ -68,13 +68,13 @@ void Menu::InitializeConsumableCraftingWindow(){
return true; return true;
}, },
[](MenuFuncData data){ [](MenuFuncData data){
RowItemDisplay*rowItem=DYNAMIC_CAST<RowItemDisplay*>(data.component); std::weak_ptr<RowItemDisplay>rowItem=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
if(rowItem->GetItem().lock()->GetEnhancementInfo().AvailableChapter()<=game->GetCurrentChapter()){ if(rowItem.lock()->GetItem().lock()->GetEnhancementInfo().AvailableChapter()<=game->GetCurrentChapter()){
Component<MenuItemItemButton>(CRAFT_CONSUMABLE,"Item Icon")->SetHideDetails(false); Component<MenuItemItemButton>(CRAFT_CONSUMABLE,"Item Icon")->SetHideDetails(false);
}else{ }else{
Component<MenuItemItemButton>(CRAFT_CONSUMABLE,"Item Icon")->SetHideDetails(true); Component<MenuItemItemButton>(CRAFT_CONSUMABLE,"Item Icon")->SetHideDetails(true);
} }
Component<MenuItemItemButton>(CRAFT_CONSUMABLE,"Item Icon")->SetItem(rowItem->GetItem()); Component<MenuItemItemButton>(CRAFT_CONSUMABLE,"Item Icon")->SetItem(rowItem.lock()->GetItem());
return true; return true;
}, },
[](MenuFuncData data){ [](MenuFuncData data){
@ -82,7 +82,7 @@ void Menu::InitializeConsumableCraftingWindow(){
return true; return true;
}, },
InventoryCreator::RowPlayerWeapons_InventoryUpdate, InventoryCreator::RowPlayerWeapons_InventoryUpdate,
{.padding=1,.size={207,28}} InventoryWindowOptions{.padding=1,.size={207,28}}
)END; )END;
craftingItemsDisplay->SetCompactDescriptions(CRAFTING_INFO); craftingItemsDisplay->SetCompactDescriptions(CRAFTING_INFO);
@ -96,7 +96,7 @@ void Menu::InitializeConsumableCraftingWindow(){
int x=int((invSize-1)%invWidth); int x=int((invSize-1)%invWidth);
int y=int((invSize-1)/invWidth); int y=int((invSize-1)/invWidth);
int itemIndex=y*invWidth+x; int itemIndex=y*invWidth+x;
auto newItem=craftingItemsDisplay->ADD("item_Craftables_"+std::to_string(itemIndex),RowItemDisplay)({totalSpacing*vf2d{float(x),float(y)},buttonSize},item,craftingItemsDisplay->inventoryButtonClickAction,craftingItemsDisplay->itemNameLabelName,craftingItemsDisplay->itemDescriptionLabelName,craftingItemsDisplay->inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END; auto newItem=craftingItemsDisplay->ADD("item_Craftables_"+std::to_string(itemIndex),RowItemDisplay)(geom2d::rect<float>{totalSpacing*vf2d{float(x),float(y)},buttonSize},item,craftingItemsDisplay->inventoryButtonClickAction,craftingItemsDisplay->itemNameLabelName,craftingItemsDisplay->itemDescriptionLabelName,craftingItemsDisplay->inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END;
newItem->SetShowQuantity(false); newItem->SetShowQuantity(false);
newItem->SetCompactDescriptions(craftingItemsDisplay->compact); newItem->SetCompactDescriptions(craftingItemsDisplay->compact);
newItem->SetPriceLabelType(craftingItemsDisplay->priceLabel); newItem->SetPriceLabelType(craftingItemsDisplay->priceLabel);
@ -111,26 +111,26 @@ void Menu::InitializeConsumableCraftingWindow(){
#pragma region Inventory Description #pragma region Inventory Description
float inventoryDescriptionWidth=consumableCraftingWindow->pos.x+consumableCraftingWindow->size.x-26-224; float inventoryDescriptionWidth=consumableCraftingWindow->pos.x+consumableCraftingWindow->size.x-26-224;
consumableCraftingWindow->ADD("Item Description Outline",MenuLabel)({{224,28},{inventoryDescriptionWidth,consumableCraftingWindow->size.y-44}},"",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; consumableCraftingWindow->ADD("Item Description Outline",MenuLabel)(geom2d::rect<float>{{224,28},{inventoryDescriptionWidth,consumableCraftingWindow->size.y-44}},"",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
consumableCraftingWindow->ADD("Item Icon",MenuItemItemButton)({{226+inventoryDescriptionWidth/2-24,30},{48,48}},Item::BLANK,MenuType::ENUM_END,DO_NOTHING,"","",IconButtonAttr::NOT_SELECTABLE)END consumableCraftingWindow->ADD("Item Icon",MenuItemItemButton)(geom2d::rect<float>{{226+inventoryDescriptionWidth/2-24,30},{48,48}},Item::BLANK,MenuType::ENUM_END,DO_NOTHING,"","",IconButtonAttr::NOT_SELECTABLE)END
->SetShowQuantity(false); ->SetShowQuantity(false);
consumableCraftingWindow->ADD("Item Name Label",MenuLabel)({{226,84},{inventoryDescriptionWidth-6,12}},"",0.75f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; consumableCraftingWindow->ADD("Item Name Label",MenuLabel)(geom2d::rect<float>{{226,84},{inventoryDescriptionWidth-6,12}},"",0.75f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
consumableCraftingWindow->ADD("Item Description Label",MenuLabel)({{226,94},{inventoryDescriptionWidth-6,consumableCraftingWindow->size.y-44-66}},"",0.5f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; consumableCraftingWindow->ADD("Item Description Label",MenuLabel)(geom2d::rect<float>{{226,94},{inventoryDescriptionWidth-6,consumableCraftingWindow->size.y-44-66}},"",0.5f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
#pragma endregion #pragma endregion
#pragma region Money Display #pragma region Money Display
vf2d moneyIconPos={224+inventoryDescriptionWidth-24,28+consumableCraftingWindow->size.y-44+6}; vf2d moneyIconPos={224+inventoryDescriptionWidth-24,28+consumableCraftingWindow->size.y-44+6};
auto moneyIcon=consumableCraftingWindow->ADD("Money Icon",MenuIconButton)({moneyIconPos,{24,24}},GFX["money.png"].Decal(),DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE|IconButtonAttr::NO_BACKGROUND)END; auto moneyIcon=consumableCraftingWindow->ADD("Money Icon",MenuIconButton)(geom2d::rect<float>{moneyIconPos,{24,24}},GFX["money.png"].Decal(),DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE|IconButtonAttr::NO_BACKGROUND)END;
std::string moneyText=std::to_string(game->GetPlayer()->GetMoney()); std::string moneyText=std::to_string(game->GetPlayer()->GetMoney());
vf2d moneyTextSize=game->GetTextSizeProp(moneyText)*2; vf2d moneyTextSize=game->GetTextSizeProp(moneyText)*2;
auto moneyDisplay=consumableCraftingWindow->ADD("Money Label",PlayerMoneyLabel)({moneyIconPos-vf2d{2+moneyTextSize.x,-2},moneyTextSize},2,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN|ComponentAttr::FIT_TO_LABEL)END; auto moneyDisplay=consumableCraftingWindow->ADD("Money Label",PlayerMoneyLabel)(geom2d::rect<float>{moneyIconPos-vf2d{2+moneyTextSize.x,-2},moneyTextSize},2,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN|ComponentAttr::FIT_TO_LABEL)END;
moneyDisplay->SetRightAlignment(true); moneyDisplay->SetRightAlignment(true);
Player::AddMoneyListener(moneyDisplay); Player::AddMoneyListener(moneyDisplay);
#pragma endregion #pragma endregion
consumableCraftingWindow->ADD("Leave Button",MenuComponent)({{consumableCraftingWindow->size.x/2-48,28+consumableCraftingWindow->size.y-44+6},{96,24}},"Leave",MenuType::ENUM_END, consumableCraftingWindow->ADD("Leave Button",MenuComponent)(geom2d::rect<float>{{consumableCraftingWindow->size.x/2-48,28+consumableCraftingWindow->size.y-44+6},{96,24}},"Leave",MenuType::ENUM_END,
[](MenuFuncData data){ [](MenuFuncData data){
Menu::CloseMenu(); Menu::CloseMenu();
return true; return true;
},{2,2})END; },vf2d{2.f,2.f})END;
} }

@ -43,19 +43,19 @@ All rights reserved.
void Menu::InitializeCraftItemWindow(){ void Menu::InitializeCraftItemWindow(){
Menu*craftItemWindow=CreateMenu(CRAFT_ITEM,CENTERED,{240,120}); Menu*craftItemWindow=CreateMenu(CRAFT_ITEM,CENTERED,{240,120});
craftItemWindow->ADD("Item Name Header",MenuLabel)({{2,-16},{craftItemWindow->size.x-4,12}},"Item Name",1.0f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; craftItemWindow->ADD("Item Name Header",MenuLabel)(geom2d::rect<float>{{2,-16},{craftItemWindow->size.x-4,12}},"Item Name",1.0f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
craftItemWindow->ADD("Enhancement Level Header",MenuLabel)({{2,0},{craftItemWindow->size.x-4,12}},"Level X ->#00AA00 Y",1.f,ComponentAttr::SHADOW|ComponentAttr::FIXED_WIDTH_FONT)END; craftItemWindow->ADD("Enhancement Level Header",MenuLabel)(geom2d::rect<float>{{2,0},{craftItemWindow->size.x-4,12}},"Level X ->#00AA00 Y",1.f,ComponentAttr::SHADOW|ComponentAttr::FIXED_WIDTH_FONT)END;
craftItemWindow->ADD("Enhancement Stats Label",EnhancementStatsLabel)({{2,16},{craftItemWindow->size.x-4,72}},Item::BLANK,1.f)END; craftItemWindow->ADD("Enhancement Stats Label",EnhancementStatsLabel)(geom2d::rect<float>{{2,16},{craftItemWindow->size.x-4,72}},Item::BLANK,1.f)END;
craftItemWindow->ADD("Materials Requirement Outline",MenuComponent)({{2,86},{craftItemWindow->size.x-4,26}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; craftItemWindow->ADD("Materials Requirement Outline",MenuComponent)(geom2d::rect<float>{{2,86},{craftItemWindow->size.x-4,26}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
craftItemWindow->ADD("Required Materials Label",MenuLabel)({{4,80},{craftItemWindow->size.x-8,0}},"Required Materials",1.0f,ComponentAttr::SHADOW)END; craftItemWindow->ADD("Required Materials Label",MenuLabel)(geom2d::rect<float>{{4,80},{craftItemWindow->size.x-8,0}},"Required Materials",1.0f,ComponentAttr::SHADOW)END;
craftItemWindow->ADD("Required Materials List",RequiredMaterialsList)({{4,88},{craftItemWindow->size.x-8,22}},Item::BLANK)END; craftItemWindow->ADD("Required Materials List",RequiredMaterialsList)(geom2d::rect<float>{{4,88},{craftItemWindow->size.x-8,22}},Item::BLANK)END;
craftItemWindow->ADD("Back Button",MenuComponent)({{36,116},{48,12}},"Back",[](MenuFuncData data){Menu::CloseMenu();return true;})END; craftItemWindow->ADD("Back Button",MenuComponent)(geom2d::rect<float>{{36,116},{48,12}},"Back",[](MenuFuncData data){Menu::CloseMenu();return true;})END;
craftItemWindow->ADD("Craft Button",MenuComponent)({{craftItemWindow->size.x-84,116},{48,12}},"Craft",[](MenuFuncData data){ craftItemWindow->ADD("Craft Button",MenuComponent)(geom2d::rect<float>{{craftItemWindow->size.x-84,116},{48,12}},"Craft",[](MenuFuncData data){
const std::weak_ptr<Item>item=Component<EnhancementStatsLabel>(data.menu.GetType(),"Enhancement Stats Label")->GetItem(); const std::weak_ptr<Item>item=Component<EnhancementStatsLabel>(data.menu.GetType(),"Enhancement Stats Label")->GetItem();
if(item.lock()->CanEnhanceItem()){ if(item.lock()->CanEnhanceItem()){
item.lock()->EnhanceItem(); item.lock()->EnhanceItem();
@ -65,7 +65,7 @@ void Menu::InitializeCraftItemWindow(){
label=std::format("Level {} ->#00AA00 {}",item.lock()->EnhancementLevel(),item.lock()->EnhancementLevel()+1); label=std::format("Level {} ->#00AA00 {}",item.lock()->EnhancementLevel(),item.lock()->EnhancementLevel()+1);
} }
Component<MenuLabel>(data.menu.GetType(),"Enhancement Level Header")->SetLabel(label); Component<MenuLabel>(data.menu.GetType(),"Enhancement Level Header")->SetLabel(label);
data.component->SetGrayedOut(!item.lock()->CanEnhanceItem()); data.component.lock()->SetGrayedOut(!item.lock()->CanEnhanceItem());
return true; return true;
})END; })END;
} }

@ -54,15 +54,14 @@ public:
virtual inline void UpdateSpawns(std::vector<std::string>&spawns){ virtual inline void UpdateSpawns(std::vector<std::string>&spawns){
Menu::menus.at(parentMenu)->components.erase_if([&](auto key){ Menu::menus.at(parentMenu)->components.erase_if([&](auto key){
if(key.first.starts_with("Spawn ")){ if(key.first.starts_with("Spawn ")){
std::erase_if(components,[&](MenuComponent*component){return key.second==component;}); std::erase_if(components,[&](std::weak_ptr<MenuComponent>component){return &*key.second==&*component.lock();});
delete key.second;
return true; return true;
} }
return false;}); return false;});
int offsetY=0; int offsetY=0;
vf2d parentSize=Menu::menus.at(OVERWORLD_LEVEL_SELECT)->size; vf2d parentSize=Menu::menus.at(OVERWORLD_LEVEL_SELECT)->size;
for(std::string spawn:spawns){ for(std::string spawn:spawns){
ADD("Spawn "+spawn,SpawnEncounterLabel)({vf2d{0,float(offsetY)},{parentSize.x,12}},MONSTER_DATA.at(spawn).GetDisplayName(),spawn)END; ADD("Spawn "+spawn,SpawnEncounterLabel)(geom2d::rect<float>{vf2d{0,float(offsetY)},{parentSize.x,12}},MONSTER_DATA.at(spawn).GetDisplayName(),spawn)END;
offsetY+=14; offsetY+=14;
} }
} }

@ -40,6 +40,7 @@ All rights reserved.
#include <sstream> #include <sstream>
#include <format> #include <format>
#include <any> #include <any>
#include <memory>
#ifndef __EMSCRIPTEN__ #ifndef __EMSCRIPTEN__
#include <source_location> #include <source_location>
#endif #endif
@ -82,4 +83,18 @@ type DYNAMIC_CAST(auto variable){
type pointer=dynamic_cast<type>(variable); type pointer=dynamic_cast<type>(variable);
if(pointer==nullptr)ERR("Could not dynamic cast to type "<<typeid(variable).name()<<"!"); if(pointer==nullptr)ERR("Could not dynamic cast to type "<<typeid(variable).name()<<"!");
return pointer; return pointer;
}
template<typename T,typename U>
std::shared_ptr<T>DYNAMIC_POINTER_CAST(const std::shared_ptr<U>&variable){
std::shared_ptr<T> newVariable=dynamic_pointer_cast<T>(variable);
if(!newVariable)ERR("Could not dynamic cast to pointer type "<<typeid(variable).name()<<"!");
return newVariable;
}
template<typename T,typename U>
std::shared_ptr<T>DYNAMIC_POINTER_CAST(const std::weak_ptr<U>&variable){
std::shared_ptr<T> newVariable=dynamic_pointer_cast<T>(variable.lock());
if(!newVariable)ERR("Could not dynamic cast to pointer type "<<typeid(variable).name()<<"!");
return newVariable;
} }

@ -54,35 +54,35 @@ void Menu::InitializeConsumableInventoryWindow(){
inventoryWindow->I(A::LOADOUT_SLOT)=0; inventoryWindow->I(A::LOADOUT_SLOT)=0;
auto consumableWindow=inventoryWindow->ADD("inventory",InventoryScrollableWindowComponent)({{0,15},{windowSize.x,96.f}},"itemName","itemDescription", auto consumableWindow=inventoryWindow->ADD("inventory",InventoryScrollableWindowComponent)(geom2d::rect<float>{{0,15},{windowSize.x,96.f}},"itemName","itemDescription",
[&](MenuFuncData data){ [&](MenuFuncData data){
MenuItemButton*button=(MenuItemButton*)data.component; std::weak_ptr<MenuItemButton>button=DYNAMIC_POINTER_CAST<MenuItemButton>(data.component.lock());
data.game->ClearLoadoutItem(data.menu.I(A::LOADOUT_SLOT)); data.game->ClearLoadoutItem(data.menu.I(A::LOADOUT_SLOT));
for(MenuComponent*component:data.parentComponent->GetComponents()){ //HACK ALERT! If we are accessing a parent component, it's because we are dealing with a scrolling window component, which has sub-components. So this should be a safe cast to make. for(std::weak_ptr<MenuComponent>component:data.parentComponent.lock()->GetComponents()){ //HACK ALERT! If we are accessing a parent component, it's because we are dealing with a scrolling window component, which has sub-components. So this should be a safe cast to make.
if(component->GetName().starts_with("item")){ if(component.lock()->GetName().starts_with("item")){
MenuItemButton*button2=DYNAMIC_CAST<MenuItemButton*>(component);//HACK ALERT! This is probably an item since we populated item lists using this name for the components. So we assume these are MenuItemButton classes. std::weak_ptr<MenuItemButton>button2=DYNAMIC_POINTER_CAST<MenuItemButton>(component.lock());//HACK ALERT! This is probably an item since we populated item lists using this name for the components. So we assume these are MenuItemButton classes.
if(button2==nullptr)ERR("Could not cast item to a MenuItemButton*!"); if(button2.expired())ERR("Could not cast item to a MenuItemButton*!");
if(button2==button){ if(&*button2.lock()==&*button.lock()){
if(button2->selected!=-1){ if(button2.lock()->selected!=-1){
data.game->ClearLoadoutItem(button2->selected); data.game->ClearLoadoutItem(button2.lock()->selected);
} }
button2->selected=-1; button2.lock()->selected=-1;
} }
if(button2->selected==data.menu.I(A::LOADOUT_SLOT)){ if(button2.lock()->selected==data.menu.I(A::LOADOUT_SLOT)){
button2->selected=-1; button2.lock()->selected=-1;
} }
} }
} }
button->selected=data.menu.I(A::LOADOUT_SLOT); button.lock()->selected=data.menu.I(A::LOADOUT_SLOT);
data.game->SetLoadoutItem(button->selected,button->GetItem().lock()->ActualName()); data.game->SetLoadoutItem(button.lock()->selected,button.lock()->GetItem().lock()->ActualName());
return true; return true;
},InventoryCreator::Player_InventoryUpdate)END; },InventoryCreator::Player_InventoryUpdate)END;
Menu::AddInventoryListener(consumableWindow,"Consumables"); Menu::AddInventoryListener(consumableWindow,"Consumables");
//We don't have to actually populate the inventory list because now when an item gets added, it will automatically add the correct component in for us. //We don't have to actually populate the inventory list because now when an item gets added, it will automatically add the correct component in for us.
inventoryWindow->ADD("Inventory Type Label",MenuLabel)({{0,-5},{windowSize.x-1,18}},"Consumables",2,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END; inventoryWindow->ADD("Inventory Type Label",MenuLabel)(geom2d::rect<float>{{0,-5},{windowSize.x-1,18}},"Consumables",2,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END;
inventoryWindow->ADD("itemName",MenuLabel)(geom2d::rect<float>(vf2d{2,90.f},{windowSize.x-4,windowSize.y-108}),"",1,ComponentAttr::SHADOW)END; inventoryWindow->ADD("itemName",MenuLabel)(geom2d::rect<float>(vf2d{2,90.f},{windowSize.x-4,windowSize.y-108}),"",1,ComponentAttr::SHADOW)END;
inventoryWindow->ADD("itemDescription",MenuLabel)(geom2d::rect<float>(vf2d{2,117.f},{windowSize.x-4,windowSize.y-108}),"",1,ComponentAttr::SHADOW)END; inventoryWindow->ADD("itemDescription",MenuLabel)(geom2d::rect<float>(vf2d{2,117.f},{windowSize.x-4,windowSize.y-108}),"",1,ComponentAttr::SHADOW)END;
auto okButton=inventoryWindow->ADD("OK Button",MenuComponent)({{windowSize.x/2-24,173.f},{48,12}},"Ok",[](MenuFuncData data){Menu::CloseMenu();return true;})END; auto okButton=inventoryWindow->ADD("OK Button",MenuComponent)(geom2d::rect<float>{{windowSize.x/2-24,173.f},{48,12}},"Ok",[](MenuFuncData data){Menu::CloseMenu();return true;})END;
} }

@ -72,7 +72,7 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
vf2d buttonSize=component.options.size; vf2d buttonSize=component.options.size;
int totalSpacing=component.options.padding+buttonSize.x; int totalSpacing=component.options.padding+buttonSize.x;
component.ADD("item_"+cat+"_"+std::to_string(itemIndex),MenuItemButton)({{float(totalSpacing*x),float(totalSpacing*y)},buttonSize},Inventory::get(cat),itemIndex,component.inventoryButtonClickAction,component.inventoryButtonHoverAction,component.inventoryButtonMouseOutAction,component.parentMenu,component.itemNameLabelName,component.itemDescriptionLabelName,component.inventoryButtonsActive?IconButtonAttr::SELECTABLE:IconButtonAttr::NOT_SELECTABLE)END component.ADD("item_"+cat+"_"+std::to_string(itemIndex),MenuItemButton)(geom2d::rect<float>{{float(totalSpacing*x),float(totalSpacing*y)},buttonSize},Inventory::get(cat),itemIndex,component.inventoryButtonClickAction,component.inventoryButtonHoverAction,component.inventoryButtonMouseOutAction,component.parentMenu,component.itemNameLabelName,component.itemDescriptionLabelName,component.inventoryButtonsActive?IconButtonAttr::SELECTABLE:IconButtonAttr::NOT_SELECTABLE)END
->SetCompactDescriptions(component.compact); ->SetCompactDescriptions(component.compact);
}; };
#pragma endregion #pragma endregion
@ -91,7 +91,7 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
vf2d buttonSize=c->options.size; vf2d buttonSize=c->options.size;
vf2d totalSpacing={c->options.padding+buttonSize.x,c->options.padding+buttonSize.y}; vf2d totalSpacing={c->options.padding+buttonSize.x,c->options.padding+buttonSize.y};
auto newItem=c->ADD("item_"+cat+"_"+std::to_string(itemIndex),RowItemDisplay)({totalSpacing*vf2d{float(x),float(y)},buttonSize},Inventory::GetInventorySlot(cat,itemIndex),c->inventoryButtonClickAction,c->itemNameLabelName,c->itemDescriptionLabelName,c->inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END; auto newItem=c->ADD("item_"+cat+"_"+std::to_string(itemIndex),RowItemDisplay)(geom2d::rect<float>{totalSpacing*vf2d{float(x),float(y)},buttonSize},Inventory::GetInventorySlot(cat,itemIndex),c->inventoryButtonClickAction,c->itemNameLabelName,c->itemDescriptionLabelName,c->inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END;
newItem->SetCompactDescriptions(c->compact); newItem->SetCompactDescriptions(c->compact);
newItem->SetPriceLabelType(c->priceLabel); newItem->SetPriceLabelType(c->priceLabel);
newItem->SetHoverFunc(c->inventoryButtonHoverAction); newItem->SetHoverFunc(c->inventoryButtonHoverAction);
@ -128,7 +128,7 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
vf2d buttonSize=c->options.size; vf2d buttonSize=c->options.size;
vf2d totalSpacing={c->options.padding+buttonSize.x,c->options.padding+buttonSize.y}; vf2d totalSpacing={c->options.padding+buttonSize.x,c->options.padding+buttonSize.y};
auto newItem=c->ADD("merchant_item_"+cat+"_"+std::to_string(itemIndex),RowItemDisplay)({totalSpacing*vf2d{float(x),float(y)},buttonSize},Merchant::GetCurrentTravelingMerchant().GetShopItems()[itemIndex],c->inventoryButtonClickAction,c->itemNameLabelName,c->itemDescriptionLabelName,c->inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END; auto newItem=c->ADD("merchant_item_"+cat+"_"+std::to_string(itemIndex),RowItemDisplay)(geom2d::rect<float>{totalSpacing*vf2d{float(x),float(y)},buttonSize},Merchant::GetCurrentTravelingMerchant().GetShopItems()[itemIndex],c->inventoryButtonClickAction,c->itemNameLabelName,c->itemDescriptionLabelName,c->inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END;
newItem->SetCompactDescriptions(c->compact); newItem->SetCompactDescriptions(c->compact);
newItem->SetPriceLabelType(c->priceLabel); newItem->SetPriceLabelType(c->priceLabel);
newItem->SetHoverFunc(c->inventoryButtonHoverAction); newItem->SetHoverFunc(c->inventoryButtonHoverAction);
@ -139,15 +139,13 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
#pragma region Row Player Weapons Updates #pragma region Row Player Weapons Updates
std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::RowPlayerWeapons_InventorySlotsUpdate= std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::RowPlayerWeapons_InventorySlotsUpdate=
[](InventoryScrollableWindowComponent&component,ITCategory cat){ [](InventoryScrollableWindowComponent&component,ITCategory cat){
std::vector<std::weak_ptr<Item>>weapons;
std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(weapons),[](std::weak_ptr<Item> item){return item.lock()->IsWeapon();});
component.RemoveAllComponents(); component.RemoveAllComponents();
component.AddButtonOnSlotUpdate(cat); component.AddButtonOnSlotUpdate(cat);
}; };
std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::RowPlayerWeapons_AddButtonOnSlotUpdate= std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::RowPlayerWeapons_AddButtonOnSlotUpdate=
[](InventoryScrollableWindowComponent&component,ITCategory cat){ [](InventoryScrollableWindowComponent&component,ITCategory cat){
std::vector<std::weak_ptr<Item>>weapons; std::vector<std::weak_ptr<Item>>weapons;
std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(weapons),[](std::weak_ptr<Item> item){return item.lock()->IsWeapon();}); std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(weapons),[](std::shared_ptr<Item>item){return item->IsWeapon();});
RowInventoryScrollableWindowComponent*c=DYNAMIC_CAST<RowInventoryScrollableWindowComponent*>(&component); RowInventoryScrollableWindowComponent*c=DYNAMIC_CAST<RowInventoryScrollableWindowComponent*>(&component);
@ -160,7 +158,7 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
int x=int((invSize-1)%invWidth); int x=int((invSize-1)%invWidth);
int y=int((invSize-1)/invWidth); int y=int((invSize-1)/invWidth);
int itemIndex=y*invWidth+x; int itemIndex=y*invWidth+x;
auto newItem=c->ADD("item_Weapon_"+std::to_string(itemIndex),RowItemDisplay)({totalSpacing*vf2d{float(x),float(y)},buttonSize},weapon,c->inventoryButtonClickAction,c->itemNameLabelName,c->itemDescriptionLabelName,c->inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END; auto newItem=c->ADD("item_Weapon_"+std::to_string(itemIndex),RowItemDisplay)(geom2d::rect<float>{totalSpacing*vf2d{float(x),float(y)},buttonSize},weapon,c->inventoryButtonClickAction,c->itemNameLabelName,c->itemDescriptionLabelName,c->inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END;
newItem->SetCompactDescriptions(c->compact); newItem->SetCompactDescriptions(c->compact);
newItem->SetPriceLabelType(c->priceLabel); newItem->SetPriceLabelType(c->priceLabel);
newItem->SetHoverFunc(c->inventoryButtonHoverAction); newItem->SetHoverFunc(c->inventoryButtonHoverAction);
@ -173,15 +171,13 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
#pragma region Row Player Armor Updates #pragma region Row Player Armor Updates
std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::RowPlayerArmor_InventorySlotsUpdate= std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::RowPlayerArmor_InventorySlotsUpdate=
[](InventoryScrollableWindowComponent&component,ITCategory cat){ [](InventoryScrollableWindowComponent&component,ITCategory cat){
std::vector<std::weak_ptr<Item>>armor;
std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(armor),[](std::weak_ptr<Item> item){return item.lock()->IsArmor();});
component.RemoveAllComponents(); component.RemoveAllComponents();
component.AddButtonOnSlotUpdate(cat); component.AddButtonOnSlotUpdate(cat);
}; };
std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::RowPlayerArmor_AddButtonOnSlotUpdate= std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::RowPlayerArmor_AddButtonOnSlotUpdate=
[](InventoryScrollableWindowComponent&component,ITCategory cat){ [](InventoryScrollableWindowComponent&component,ITCategory cat){
std::vector<std::weak_ptr<Item>>armor; std::vector<std::weak_ptr<Item>>armor;
std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(armor),[](std::weak_ptr<Item> item){return item.lock()->IsArmor();}); std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(armor),[](std::shared_ptr<Item>item){return item->IsArmor();});
RowInventoryScrollableWindowComponent*c=DYNAMIC_CAST<RowInventoryScrollableWindowComponent*>(&component); RowInventoryScrollableWindowComponent*c=DYNAMIC_CAST<RowInventoryScrollableWindowComponent*>(&component);
@ -194,7 +190,7 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
int x=int((invSize-1)%invWidth); int x=int((invSize-1)%invWidth);
int y=int((invSize-1)/invWidth); int y=int((invSize-1)/invWidth);
int itemIndex=y*invWidth+x; int itemIndex=y*invWidth+x;
auto newItem=c->ADD("item_Armor_"+std::to_string(itemIndex),RowItemDisplay)({totalSpacing*vf2d{float(x),float(y)},buttonSize},armor,c->inventoryButtonClickAction,c->itemNameLabelName,c->itemDescriptionLabelName,c->inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END; auto newItem=c->ADD("item_Armor_"+std::to_string(itemIndex),RowItemDisplay)(geom2d::rect<float>{totalSpacing*vf2d{float(x),float(y)},buttonSize},armor,c->inventoryButtonClickAction,c->itemNameLabelName,c->itemDescriptionLabelName,c->inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END;
newItem->SetCompactDescriptions(c->compact); newItem->SetCompactDescriptions(c->compact);
newItem->SetPriceLabelType(c->priceLabel); newItem->SetPriceLabelType(c->priceLabel);
newItem->SetHoverFunc(c->inventoryButtonHoverAction); newItem->SetHoverFunc(c->inventoryButtonHoverAction);

@ -80,8 +80,8 @@ public:
virtual inline void Update(AiL*game)override{ virtual inline void Update(AiL*game)override{
ScrollableWindowComponent::Update(game); ScrollableWindowComponent::Update(game);
bool noneHovered=true; bool noneHovered=true;
for(MenuComponent*component:components){ for(std::weak_ptr<MenuComponent>component:components){
if(component->hovered){ if(component.lock()->hovered){
noneHovered=false; noneHovered=false;
break; break;
} }
@ -93,9 +93,9 @@ public:
} }
virtual inline void SetCompactDescriptions(CompactText compact){ virtual inline void SetCompactDescriptions(CompactText compact){
this->compact=compact; this->compact=compact;
for(MenuComponent*component:components){ for(std::weak_ptr<MenuComponent>component:components){
MenuItemButton*itemButton=DYNAMIC_CAST<MenuItemButton*>(component); std::weak_ptr<MenuItemButton>itemButton=DYNAMIC_POINTER_CAST<MenuItemButton>(component.lock());
itemButton->SetCompactDescriptions(compact); itemButton.lock()->SetCompactDescriptions(compact);
} }
} }
protected: protected:

@ -30,7 +30,7 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE. SUCH DAMAGE.
Portions of this software are copyright © 2023 The FreeType Portions of this software are copyright <EFBFBD> 2023 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information. Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved. All rights reserved.
*/ */
@ -58,8 +58,8 @@ using ButtonAttr::UNSELECTABLE_VIA_KEYBOARD;
void Menu::InitializeInventoryWindow(){ void Menu::InitializeInventoryWindow(){
Menu*inventoryWindow=CreateMenu(INVENTORY,CENTERED,game->GetScreenSize()-vi2d{52,52}); Menu*inventoryWindow=CreateMenu(INVENTORY,CENTERED,game->GetScreenSize()-vi2d{52,52});
inventoryWindow->ADD("Inventory Label",MenuLabel)({{0,0},{inventoryWindow->size.x-1,24}},"Inventory",2,SHADOW|OUTLINE|BACKGROUND)END; inventoryWindow->ADD("Inventory Label",MenuLabel)(geom2d::rect<float>{{0,0},{inventoryWindow->size.x-1,24}},"Inventory",2,SHADOW|OUTLINE|BACKGROUND)END;
inventoryWindow->ADD("Inventory Tabs Outline",MenuComponent)({{0,28},{72,inventoryWindow->size.y-44}},"",DO_NOTHING,UNSELECTABLE)END; inventoryWindow->ADD("Inventory Tabs Outline",MenuComponent)(geom2d::rect<float>{{0,28},{72,inventoryWindow->size.y-44}},"",DO_NOTHING,UNSELECTABLE)END;
std::vector<std::pair<std::string,int>>categories; std::vector<std::pair<std::string,int>>categories;
for(auto&[category,items]:ITEM_CATEGORIES){ for(auto&[category,items]:ITEM_CATEGORIES){
@ -75,22 +75,22 @@ void Menu::InitializeInventoryWindow(){
float buttonWidth=64; float buttonWidth=64;
float textScaling=std::min(1.f,buttonWidth/textWidth); float textScaling=std::min(1.f,buttonWidth/textWidth);
auto button=inventoryWindow->ADD(category+" Inventory Tab",MenuComponent)({{2,30+yOffset},{68,16}},category,MenuType::ENUM_END, auto button=inventoryWindow->ADD(category+" Inventory Tab",MenuComponent)(geom2d::rect<float>{{2,30+yOffset},{68,16}},category,MenuType::ENUM_END,
[&](MenuFuncData data){ [&](MenuFuncData data){
//Close the old inventory window and show the proper one. //Close the old inventory window and show the proper one.
Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.menu.S(A::LAST_INVENTORY_TYPE_OPENED))->Enable(false); Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.menu.S(A::LAST_INVENTORY_TYPE_OPENED))->Enable(false);
Component<MenuComponent>(data.menu.GetType(),data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)+" Inventory Tab")->SetSelected(false); Component<MenuComponent>(data.menu.GetType(),data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)+" Inventory Tab")->SetSelected(false);
Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.component->S(A::CATEGORY_NAME))->Enable(true); Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.component.lock()->S(A::CATEGORY_NAME))->Enable(true);
Component<MenuComponent>(data.menu.GetType(),data.component->S(A::CATEGORY_NAME)+" Inventory Tab")->SetSelected(true); Component<MenuComponent>(data.menu.GetType(),data.component.lock()->S(A::CATEGORY_NAME)+" Inventory Tab")->SetSelected(true);
data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)=data.component->S(A::CATEGORY_NAME); data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)=data.component.lock()->S(A::CATEGORY_NAME);
return true; return true;
},{textScaling,1.f})END; },vf2d{textScaling,1.f})END;
button->SetSelectionType(HIGHLIGHT); button->SetSelectionType(HIGHLIGHT);
button->S(A::CATEGORY_NAME)=category; button->S(A::CATEGORY_NAME)=category;
auto inventoryDisplay=inventoryWindow->ADD("Inventory Display - "+category,RowInventoryScrollableWindowComponent)({{72,28},{150,inventoryWindow->size.y-44}},"Item Name Label","Item Description Label",DO_NOTHING, auto inventoryDisplay=inventoryWindow->ADD("Inventory Display - "+category,RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{72,28},{150,inventoryWindow->size.y-44}},"Item Name Label","Item Description Label",DO_NOTHING,
[](MenuFuncData data){ [](MenuFuncData data){
Component<MenuItemItemButton>(data.menu.GetType(),"Item Icon")->SetItem(DYNAMIC_CAST<RowItemDisplay*>(data.component)->GetItem()); Component<MenuItemItemButton>(data.menu.GetType(),"Item Icon")->SetItem(DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock())->GetItem());
return true; return true;
}, },
[](MenuFuncData data){ [](MenuFuncData data){
@ -98,7 +98,7 @@ void Menu::InitializeInventoryWindow(){
return true; return true;
}, },
InventoryCreator::RowPlayer_InventoryUpdate, InventoryCreator::RowPlayer_InventoryUpdate,
{.padding=1,.size={137,28}})END; InventoryWindowOptions{.padding=1,.size={137,28}})END;
if(first){ if(first){
inventoryWindow->S(A::LAST_INVENTORY_TYPE_OPENED)=category; inventoryWindow->S(A::LAST_INVENTORY_TYPE_OPENED)=category;
@ -116,25 +116,25 @@ void Menu::InitializeInventoryWindow(){
#pragma region Inventory Description #pragma region Inventory Description
float inventoryDescriptionWidth=inventoryWindow->pos.x+inventoryWindow->size.x-26-224; float inventoryDescriptionWidth=inventoryWindow->pos.x+inventoryWindow->size.x-26-224;
inventoryWindow->ADD("Item Description Outline",MenuLabel)({{224,28},{inventoryDescriptionWidth,inventoryWindow->size.y-44}},"",1,LEFT_ALIGN|OUTLINE|BACKGROUND)END; inventoryWindow->ADD("Item Description Outline",MenuLabel)(geom2d::rect<float>{{224,28},{inventoryDescriptionWidth,inventoryWindow->size.y-44}},"",1,LEFT_ALIGN|OUTLINE|BACKGROUND)END;
inventoryWindow->ADD("Item Icon",MenuItemItemButton)({{226+inventoryDescriptionWidth/2-24,30},{48,48}},Item::BLANK,MenuType::ENUM_END,DO_NOTHING,"","",IconButtonAttr::NOT_SELECTABLE)END; inventoryWindow->ADD("Item Icon",MenuItemItemButton)(geom2d::rect<float>{{226+inventoryDescriptionWidth/2-24,30},{48,48}},Item::BLANK,MenuType::ENUM_END,DO_NOTHING,"","",IconButtonAttr::NOT_SELECTABLE)END;
inventoryWindow->ADD("Item Name Label",MenuLabel)({{226,84},{inventoryDescriptionWidth-6,12}},"",0.75f,LEFT_ALIGN|SHADOW)END; inventoryWindow->ADD("Item Name Label",MenuLabel)(geom2d::rect<float>{{226,84},{inventoryDescriptionWidth-6,12}},"",0.75f,LEFT_ALIGN|SHADOW)END;
inventoryWindow->ADD("Item Description Label",MenuLabel)({{226,94},{inventoryDescriptionWidth-6,inventoryWindow->size.y-44-66}},"",0.5f,LEFT_ALIGN|SHADOW)END; inventoryWindow->ADD("Item Description Label",MenuLabel)(geom2d::rect<float>{{226,94},{inventoryDescriptionWidth-6,inventoryWindow->size.y-44-66}},"",0.5f,LEFT_ALIGN|SHADOW)END;
#pragma endregion #pragma endregion
#pragma region Money Display #pragma region Money Display
vf2d moneyIconPos={224+inventoryDescriptionWidth-24,28+inventoryWindow->size.y-44+6}; vf2d moneyIconPos={224+inventoryDescriptionWidth-24,28+inventoryWindow->size.y-44+6};
auto moneyIcon=inventoryWindow->ADD("Money Icon",MenuIconButton)({moneyIconPos,{24,24}},GFX["money.png"].Decal(),DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE|IconButtonAttr::NO_BACKGROUND)END; auto moneyIcon=inventoryWindow->ADD("Money Icon",MenuIconButton)(geom2d::rect<float>{moneyIconPos,{24,24}},GFX["money.png"].Decal(),DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE|IconButtonAttr::NO_BACKGROUND)END;
std::string moneyText=std::to_string(game->GetPlayer()->GetMoney()); std::string moneyText=std::to_string(game->GetPlayer()->GetMoney());
vf2d moneyTextSize=game->GetTextSizeProp(moneyText)*2; vf2d moneyTextSize=game->GetTextSizeProp(moneyText)*2;
auto moneyDisplay=inventoryWindow->ADD("Money Label",PlayerMoneyLabel)({moneyIconPos-vf2d{2+moneyTextSize.x,-2},moneyTextSize},2,SHADOW|LEFT_ALIGN|FIT_TO_LABEL)END; auto moneyDisplay=inventoryWindow->ADD("Money Label",PlayerMoneyLabel)(geom2d::rect<float>{moneyIconPos-vf2d{2+moneyTextSize.x,-2},moneyTextSize},2,SHADOW|LEFT_ALIGN|FIT_TO_LABEL)END;
moneyDisplay->SetRightAlignment(true); moneyDisplay->SetRightAlignment(true);
Player::AddMoneyListener(moneyDisplay); Player::AddMoneyListener(moneyDisplay);
#pragma endregion #pragma endregion
inventoryWindow->ADD("Back Button",MenuComponent)({{inventoryWindow->size.x/2-48,28+inventoryWindow->size.y-44+6},{96,24}},"Back",MenuType::ENUM_END, inventoryWindow->ADD("Back Button",MenuComponent)(geom2d::rect<float>{{inventoryWindow->size.x/2-48,28+inventoryWindow->size.y-44+6},{96,24}},"Back",MenuType::ENUM_END,
[](MenuFuncData data){ [](MenuFuncData data){
Menu::CloseMenu(); Menu::CloseMenu();
return true; return true;
},{2,2})END; },vf2d{2.f,2.f})END;
} }

@ -50,29 +50,29 @@ void Menu::InitializeItemLoadoutWindow(){
float itemLoadoutWindowWidth=(game->GetScreenSize().x-5.f); float itemLoadoutWindowWidth=(game->GetScreenSize().x-5.f);
itemLoadoutWindow->ADD("Loadout Label",MenuLabel)({{0,24},{itemLoadoutWindowWidth,24}},"Loadout",2,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END; itemLoadoutWindow->ADD("Loadout Label",MenuLabel)(geom2d::rect<float>{{0,24},{itemLoadoutWindowWidth,24}},"Loadout",2,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END;
float buttonBorderPadding=64; float buttonBorderPadding=64;
itemLoadoutWindow->ADD("Loadout Item 1",MenuItemItemButton)({{64,96},{48,48}},game->GetLoadoutItem(0),INVENTORY_CONSUMABLES,[](MenuFuncData data){Menu::menus.at(INVENTORY_CONSUMABLES)->I(A::LOADOUT_SLOT)=0; return true;},[](MenuFuncData data){return true;},[](MenuFuncData data){ itemLoadoutWindow->ADD("Loadout Item 1",MenuItemItemButton)(geom2d::rect<float>{{64,96},{48,48}},game->GetLoadoutItem(0),INVENTORY_CONSUMABLES,[](MenuFuncData data){Menu::menus.at(INVENTORY_CONSUMABLES)->I(A::LOADOUT_SLOT)=0; return true;},[](MenuFuncData data){return true;},[](MenuFuncData data){
Component<MenuLabel>(ITEM_LOADOUT,"Item Name Label")->SetLabel(""); Component<MenuLabel>(ITEM_LOADOUT,"Item Name Label")->SetLabel("");
Component<MenuLabel>(ITEM_LOADOUT,"Item Description")->SetLabel(""); Component<MenuLabel>(ITEM_LOADOUT,"Item Description")->SetLabel("");
return true; return true;
},"Item Name Label","Item Description")END; },"Item Name Label","Item Description")END;
itemLoadoutWindow->ADD("Loadout Item 2",MenuItemItemButton)({{itemLoadoutWindowWidth/2-24,96},{48,48}},game->GetLoadoutItem(1),INVENTORY_CONSUMABLES,[](MenuFuncData data){Menu::menus.at(INVENTORY_CONSUMABLES)->I(A::LOADOUT_SLOT)=1;return true;},[](MenuFuncData data){return true;},[](MenuFuncData data){ itemLoadoutWindow->ADD("Loadout Item 2",MenuItemItemButton)(geom2d::rect<float>{{itemLoadoutWindowWidth/2-24,96},{48,48}},game->GetLoadoutItem(1),INVENTORY_CONSUMABLES,[](MenuFuncData data){Menu::menus.at(INVENTORY_CONSUMABLES)->I(A::LOADOUT_SLOT)=1;return true;},[](MenuFuncData data){return true;},[](MenuFuncData data){
Component<MenuLabel>(ITEM_LOADOUT,"Item Name Label")->SetLabel(""); Component<MenuLabel>(ITEM_LOADOUT,"Item Name Label")->SetLabel("");
Component<MenuLabel>(ITEM_LOADOUT,"Item Description")->SetLabel(""); Component<MenuLabel>(ITEM_LOADOUT,"Item Description")->SetLabel("");
return true; return true;
},"Item Name Label","Item Description")END; },"Item Name Label","Item Description")END;
itemLoadoutWindow->ADD("Loadout Item 3",MenuItemItemButton)({{itemLoadoutWindowWidth-48-64,96},{48,48}},game->GetLoadoutItem(2),INVENTORY_CONSUMABLES,[](MenuFuncData data){Menu::menus.at(INVENTORY_CONSUMABLES)->I(A::LOADOUT_SLOT)=2;return true;},[](MenuFuncData data){return true;},[](MenuFuncData data){ itemLoadoutWindow->ADD("Loadout Item 3",MenuItemItemButton)(geom2d::rect<float>{{itemLoadoutWindowWidth-48-64,96},{48,48}},game->GetLoadoutItem(2),INVENTORY_CONSUMABLES,[](MenuFuncData data){Menu::menus.at(INVENTORY_CONSUMABLES)->I(A::LOADOUT_SLOT)=2;return true;},[](MenuFuncData data){return true;},[](MenuFuncData data){
Component<MenuLabel>(ITEM_LOADOUT,"Item Name Label")->SetLabel(""); Component<MenuLabel>(ITEM_LOADOUT,"Item Name Label")->SetLabel("");
Component<MenuLabel>(ITEM_LOADOUT,"Item Description")->SetLabel(""); Component<MenuLabel>(ITEM_LOADOUT,"Item Description")->SetLabel("");
return true; return true;
},"Item Name Label","Item Description")END; },"Item Name Label","Item Description")END;
itemLoadoutWindow->ADD("Item Name Label",MenuLabel)({{0,158},{itemLoadoutWindowWidth,12}},"",1,ComponentAttr::SHADOW)END; itemLoadoutWindow->ADD("Item Name Label",MenuLabel)(geom2d::rect<float>{{0,158},{itemLoadoutWindowWidth,12}},"",1,ComponentAttr::SHADOW)END;
itemLoadoutWindow->ADD("Item Description",MenuLabel)({{0,170},{itemLoadoutWindowWidth,24}},"",1,ComponentAttr::SHADOW)END; itemLoadoutWindow->ADD("Item Description",MenuLabel)(geom2d::rect<float>{{0,170},{itemLoadoutWindowWidth,24}},"",1,ComponentAttr::SHADOW)END;
itemLoadoutWindow->ADD("Start Level Button",MenuComponent)({{itemLoadoutWindowWidth/2-32,214},{64,16}},"Start",[](MenuFuncData data){State_OverworldMap::StartLevel();return true;})END; itemLoadoutWindow->ADD("Start Level Button",MenuComponent)(geom2d::rect<float>{{itemLoadoutWindowWidth/2-32,214},{64,16}},"Start",[](MenuFuncData data){State_OverworldMap::StartLevel();return true;})END;
} }

@ -51,16 +51,16 @@ void Menu::InitializeLevelCompleteWindow(){
Menu*levelCompleteWindow=Menu::CreateMenu(LEVEL_COMPLETE,windowSize.pos,windowSize.size); Menu*levelCompleteWindow=Menu::CreateMenu(LEVEL_COMPLETE,windowSize.pos,windowSize.size);
levelCompleteWindow->ADD("Stage Complete Label",MenuLabel)({{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,4},{windowSize.size.x-1.f,20}},"Stage Completed",2,ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND|ComponentAttr::SHADOW)END;
levelCompleteWindow->ADD("Monster Loot Outline",MenuComponent)({{0,32},{windowSize.size.x-80.f,72}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; levelCompleteWindow->ADD("Monster Loot Outline",MenuComponent)(geom2d::rect<float>{{0,32},{windowSize.size.x-80.f,72}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
levelCompleteWindow->ADD("Monster Loot Label",MenuLabel)({{0,32},{windowSize.size.x-80.f,12}},"Monster Loot",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END; levelCompleteWindow->ADD("Monster Loot Label",MenuLabel)(geom2d::rect<float>{{0,32},{windowSize.size.x-80.f,12}},"Monster Loot",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END;
auto monsterLootWindow=levelCompleteWindow->ADD("Monster Loot Window",InventoryScrollableWindowComponent)({{0,44},{windowSize.size.x-80.f,60}},"Monster Loot Popup Item Name","Monster Loot Popup Item Description",DO_NOTHING,InventoryCreator::Player_InventoryUpdate)END; auto monsterLootWindow=levelCompleteWindow->ADD("Monster Loot Window",InventoryScrollableWindowComponent)(geom2d::rect<float>{{0,44},{windowSize.size.x-80.f,60}},"Monster Loot Popup Item Name","Monster Loot Popup Item Description",DO_NOTHING,InventoryCreator::Player_InventoryUpdate)END;
Menu::AddInventoryListener(monsterLootWindow,"Monster Loot"); Menu::AddInventoryListener(monsterLootWindow,"Monster Loot");
levelCompleteWindow->ADD("Stage Loot Outline",MenuComponent)({{0,108},{windowSize.size.x-80.f,72}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; levelCompleteWindow->ADD("Stage Loot Outline",MenuComponent)(geom2d::rect<float>{{0,108},{windowSize.size.x-80.f,72}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
levelCompleteWindow->ADD("Stage Loot Label",MenuLabel)({{0,108},{windowSize.size.x-80.f,12}},"Stage Loot",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END; levelCompleteWindow->ADD("Stage Loot Label",MenuLabel)(geom2d::rect<float>{{0,108},{windowSize.size.x-80.f,12}},"Stage Loot",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END;
auto stageLootWindow=levelCompleteWindow->ADD("Stage Loot Window",InventoryScrollableWindowComponent)({{0,120},{windowSize.size.x-80.f,60}},"Stage Loot Popup Item Name","Stage Loot Popup Item Description",DO_NOTHING,InventoryCreator::Player_InventoryUpdate)END; auto stageLootWindow=levelCompleteWindow->ADD("Stage Loot Window",InventoryScrollableWindowComponent)(geom2d::rect<float>{{0,120},{windowSize.size.x-80.f,60}},"Stage Loot Popup Item Name","Stage Loot Popup Item Description",DO_NOTHING,InventoryCreator::Player_InventoryUpdate)END;
Menu::AddInventoryListener(stageLootWindow,"Stage Loot"); Menu::AddInventoryListener(stageLootWindow,"Stage Loot");
auto nextButtonAction=[](MenuFuncData data){ auto nextButtonAction=[](MenuFuncData data){
@ -69,13 +69,13 @@ void Menu::InitializeLevelCompleteWindow(){
return true; return true;
}; };
levelCompleteWindow->ADD("Level Details Outline",MenuComponent)({{windowSize.size.x-72.f,32},{71,72}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; levelCompleteWindow->ADD("Level Details Outline",MenuComponent)(geom2d::rect<float>{{windowSize.size.x-72.f,32},{71,72}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
levelCompleteWindow->ADD("Level EXP Gain Outline",MenuLabel)({{windowSize.size.x-72.f,104},{71,36}},"+ Exp",1,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; levelCompleteWindow->ADD("Level EXP Gain Outline",MenuLabel)(geom2d::rect<float>{{windowSize.size.x-72.f,104},{71,36}},"+ Exp",1,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
levelCompleteWindow->ADD("Next Button",MenuComponent)({{windowSize.size.x-72.f,144},{71,32}},"Next",nextButtonAction)END; levelCompleteWindow->ADD("Next Button",MenuComponent)(geom2d::rect<float>{{windowSize.size.x-72.f,144},{71,32}},"Next",nextButtonAction)END;
levelCompleteWindow->ADD("Monster Loot Popup Item Name",PopupMenuLabel)({{0,108},{windowSize.size.x-80.f,12}},"",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; levelCompleteWindow->ADD("Monster Loot Popup Item Name",PopupMenuLabel)(geom2d::rect<float>{{0,108},{windowSize.size.x-80.f,12}},"",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
levelCompleteWindow->ADD("Monster Loot Popup Item Description",PopupMenuLabel)({{0,120},{windowSize.size.x-80.f,60}},"",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; levelCompleteWindow->ADD("Monster Loot Popup Item Description",PopupMenuLabel)(geom2d::rect<float>{{0,120},{windowSize.size.x-80.f,60}},"",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
levelCompleteWindow->ADD("Stage Loot Popup Item Name",PopupMenuLabel)({{0,32},{windowSize.size.x-80.f,12}},"",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; levelCompleteWindow->ADD("Stage Loot Popup Item Name",PopupMenuLabel)(geom2d::rect<float>{{0,32},{windowSize.size.x-80.f,12}},"",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
levelCompleteWindow->ADD("Stage Loot Popup Item Description",PopupMenuLabel)({{0,44},{windowSize.size.x-80.f,60}},"",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; levelCompleteWindow->ADD("Stage Loot Popup Item Description",PopupMenuLabel)(geom2d::rect<float>{{0,44},{windowSize.size.x-80.f,60}},"",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
} }

@ -42,7 +42,7 @@ All rights reserved.
void Menu::InitializeLoadGameWindow(){ void Menu::InitializeLoadGameWindow(){
Menu*loadGameWindow=CreateMenu(LOAD_GAME,CENTERED,vi2d{96,120}); Menu*loadGameWindow=CreateMenu(LOAD_GAME,CENTERED,vi2d{96,120});
loadGameWindow->ADD("Game Files Label",MenuLabel)({{-8,-12},{112,12}},"Load Game",1.0f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; loadGameWindow->ADD("Game Files Label",MenuLabel)(geom2d::rect<float>{{-8,-12},{112,12}},"Load Game",1.0f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
loadGameWindow->ADD("Game Files List",ScrollableWindowComponent)({{-8,4},{112,116}})END; loadGameWindow->ADD("Game Files List",ScrollableWindowComponent)(geom2d::rect<float>{{-8,4},{112,116}})END;
loadGameWindow->ADD("Go Back Button",MenuComponent)({{24,124},{48,12}},"Back",[](MenuFuncData menu){Menu::CloseMenu();return true;})END; loadGameWindow->ADD("Go Back Button",MenuComponent)(geom2d::rect<float>{{24,124},{48,12}},"Back",[](MenuFuncData menu){Menu::CloseMenu();return true;})END;
} }

@ -47,7 +47,7 @@ using A=Attribute;
void Menu::InitializeMainMenuWindow(){ void Menu::InitializeMainMenuWindow(){
Menu*mainMenuWindow=CreateMenu(MAIN_MENU,vi2d{132,120},vi2d{96,96}); Menu*mainMenuWindow=CreateMenu(MAIN_MENU,vi2d{132,120},vi2d{96,96});
mainMenuWindow->ADD("New Game Button",MenuComponent)({{12,4},{72,24}},"New Game",[&](MenuFuncData data){ mainMenuWindow->ADD("New Game Button",MenuComponent)(geom2d::rect<float>{{12,4},{72,24}},"New Game",[&](MenuFuncData data){
game->TextEntryEnable(true); game->TextEntryEnable(true);
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
data.menu.S(A::NEXT_MENU)="New Game"; data.menu.S(A::NEXT_MENU)="New Game";
@ -61,7 +61,7 @@ void Menu::InitializeMainMenuWindow(){
#endif #endif
return true; return true;
})END; })END;
mainMenuWindow->ADD("Load Game Button",MenuComponent)({{12,36},{72,24}},"Load Game",[](MenuFuncData data){ mainMenuWindow->ADD("Load Game Button",MenuComponent)(geom2d::rect<float>{{12,36},{72,24}},"Load Game",[](MenuFuncData data){
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
data.menu.S(A::NEXT_MENU)="Load Game"; data.menu.S(A::NEXT_MENU)="Load Game";
if(SaveFile::GetUserID().length()==0){ if(SaveFile::GetUserID().length()==0){
@ -77,7 +77,7 @@ void Menu::InitializeMainMenuWindow(){
#endif #endif
return true; return true;
})END; })END;
mainMenuWindow->ADD("Quit Game Button",MenuComponent)({{12,68},{72,24}},"Quit Game",[](MenuFuncData data){ mainMenuWindow->ADD("Quit Game Button",MenuComponent)(geom2d::rect<float>{{12,68},{72,24}},"Quit Game",[](MenuFuncData data){
game->EndGame(); game->EndGame();
return true; return true;
})END; })END;

@ -53,12 +53,11 @@ std::vector<Menu*>Menu::stack;
std::map<MenuType,Menu*>Menu::menus; std::map<MenuType,Menu*>Menu::menus;
std::string Menu::themeSelection="BlueDefault"; std::string Menu::themeSelection="BlueDefault";
safeunorderedmap<std::string,Theme>Menu::themes; safeunorderedmap<std::string,Theme>Menu::themes;
safemap<ITCategory,std::vector<MenuComponent*>>Menu::inventoryListeners; safemap<ITCategory,std::vector<std::weak_ptr<MenuComponent>>>Menu::inventoryListeners;
safemap<ITCategory,std::vector<MenuComponent*>>Menu::merchantInventoryListeners; safemap<ITCategory,std::vector<std::weak_ptr<MenuComponent>>>Menu::merchantInventoryListeners;
std::vector<MenuComponent*>Menu::equipStatListeners; std::vector<std::weak_ptr<MenuComponent>>Menu::equipStatListeners;
std::vector<MenuComponent*>Menu::chapterListeners; std::vector<std::weak_ptr<MenuComponent>>Menu::chapterListeners;
const vf2d Menu::CENTERED = {-456,-456}; const vf2d Menu::CENTERED = {-456,-456};
std::vector<MenuComponent*>Menu::unhandledComponents;
////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////
///__///////////////////////////////////////////////////////////////////////////////////////////// ///__/////////////////////////////////////////////////////////////////////////////////////////////
//| |//////////////////////////////////////////////////////////////////////////////////////////// //| |////////////////////////////////////////////////////////////////////////////////////////////
@ -85,12 +84,6 @@ Menu::Menu(vf2d pos,vf2d size)
this->window=ViewPort::rectViewPort({-24,-24},this->size+vi2d{48,48},this->pos); this->window=ViewPort::rectViewPort({-24,-24},this->size+vi2d{48,48},this->pos);
} }
Menu::~Menu(){
for(auto&[key,value]:components){
delete value;
}
}
void Menu::InitializeMenus(){ void Menu::InitializeMenus(){
#define MAX_MENUS 32 #define MAX_MENUS 32
stack.reserve(MAX_MENUS); stack.reserve(MAX_MENUS);
@ -119,28 +112,11 @@ void Menu::InitializeMenus(){
if(menus.count(type)==0){ if(menus.count(type)==0){
ERR("WARNING! Menu Type "<<type<<" does not exist!") ERR("WARNING! Menu Type "<<type<<" does not exist!")
} }
//Lock up everything once it's done.
menus[type]->buttons.SetInitialized();
menus[type]->keyboardButtons.SetInitialized();
for(auto&[key,value]:menus[type]->components){ for(auto&[key,value]:menus[type]->components){
MenuComponent*component=value; value->AfterCreate();
component->AfterCreate();
} }
if(menus.size()>MAX_MENUS)ERR("WARNING! Exceeded maximum expected menu count of "<<MAX_MENUS<<"!"); if(menus.size()>MAX_MENUS)ERR("WARNING! Exceeded maximum expected menu count of "<<MAX_MENUS<<"!");
} }
if(Menu::unhandledComponents.size()>0){
std::cout<<"WARNING! There are "<<Menu::unhandledComponents.size()<<" components that were not added to any Menu! These have been leaked and should not be happening!"<<std::endl;
std::cout<<"See below for a report of unhandled components:"<<std::endl;
int count=0;
for(MenuComponent*component:Menu::unhandledComponents){
std::cout<<"\tComponent "<<(count+1)<<": "<<std::endl;
std::cout<<"\t-Parent Menu:"<<component->parentMenu<<" Label: "<<component->label<<std::endl;
std::cout<<"\t\tCreated Inside of Menu: "<<component->memoryLeakInfo.first<<" // Last Component: "<<component->memoryLeakInfo.second<<std::endl;
count++;
}
ERR("")
}
} }
Menu*Menu::CreateMenu(MenuType type,vf2d pos,vf2d size){ Menu*Menu::CreateMenu(MenuType type,vf2d pos,vf2d size){
@ -157,12 +133,13 @@ void Menu::CheckClickAndPerformMenuSelect(AiL*game){
} }
void Menu::HoverMenuSelect(AiL*game){ void Menu::HoverMenuSelect(AiL*game){
if(!game->IsFocused()||selection==vi2d{-1,-1}||buttons[selection.y][selection.x]->disabled)return; if(!game->IsFocused()||selection.expired()||selection.lock()->disabled)return;
if(buttons[selection.y][selection.x]->draggable){
if(selection.lock()->draggable){
if(buttonHoldTime<"ThemeGlobal.MenuHoldTime"_F){ if(buttonHoldTime<"ThemeGlobal.MenuHoldTime"_F){
CheckClickAndPerformMenuSelect(game); CheckClickAndPerformMenuSelect(game);
}else{ }else{
draggingComponent=buttons[selection.y][selection.x]->PickUpDraggableItem(); draggingComponent=std::move(selection.lock()->PickUpDraggableItem());
buttonHoldTime=0; buttonHoldTime=0;
} }
}else{ }else{
@ -171,12 +148,12 @@ void Menu::HoverMenuSelect(AiL*game){
} }
void Menu::MenuSelect(AiL*game){ void Menu::MenuSelect(AiL*game){
if(!game->IsFocused()||selection==vi2d{-1,-1}||(buttons[selection.y][selection.x]->disabled||buttons[selection.y][selection.x]->grayedOut))return; if(!game->IsFocused()||selection.expired()||selection.lock()->disabled||selection.lock()->grayedOut)return;
bool buttonStillValid=buttons[selection.y][selection.x]->onClick(MenuFuncData{*this,game,buttons[selection.y][selection.x],(ScrollableWindowComponent*)buttons[selection.y][selection.x]->parentComponent}); bool buttonStillValid=selection.lock()->onClick(MenuFuncData{*this,game,selection,dynamic_pointer_cast<ScrollableWindowComponent>(selection.lock()->parentComponent.lock())});
if(buttonStillValid){ if(buttonStillValid){
if(buttons[selection.y][selection.x]->menuDest!=MenuType::ENUM_END){ if(selection.lock()->menuDest!=MenuType::ENUM_END){
if(stack.size()<32){ if(stack.size()<32){
stack.push_back(menus[buttons[selection.y][selection.x]->menuDest]);//Navigate to the next menu. stack.push_back(menus[selection.lock()->menuDest]);//Navigate to the next menu.
}else{ }else{
ERR("WARNING! Exceeded menu stack size limit!") ERR("WARNING! Exceeded menu stack size limit!")
} }
@ -185,10 +162,7 @@ void Menu::MenuSelect(AiL*game){
} }
void Menu::Update(AiL*game){ void Menu::Update(AiL*game){
if(buttons.count(selection.y)==0)selection={-1,-1}; if(!draggingComponent){
if(selection.x<0||selection.x>=buttons[selection.y].size()){selection={-1,-1};}
if(draggingComponent==nullptr){
HoverMenuSelect(game); HoverMenuSelect(game);
} }
@ -196,11 +170,9 @@ void Menu::Update(AiL*game){
SetMouseNavigation(true); SetMouseNavigation(true);
} }
for(auto&[key,value]:buttons){ for(auto&[key,button]:components){
for(auto&button:value){ if(!button->disabled){
if(!button->disabled){ button->hovered=false;
button->hovered=false;
}
} }
} }
@ -208,64 +180,54 @@ void Menu::Update(AiL*game){
if(!UsingMouseNavigation()){ if(!UsingMouseNavigation()){
if(!game->IsTextEntryEnabled()){ if(!game->IsTextEntryEnabled()){
if(selection!=vi2d{-1,-1}){ if(!selection.expired()){
buttons[selection.y][selection.x]->hovered=true; selection.lock()->hovered=true;
itemHovered=true; itemHovered=true;
} }
} }
}else{ }else{
selection={-1,-1}; selection={};
for(auto&[key,value]:buttons){ for(auto&[key,component]:components){
int index=0; if(component->selectable){
for(auto&button:value){ if(!component->disabled&&!component->grayedOut){
if(!button->disabled&&!button->grayedOut){ if(component->GetHoverState(game)){
if(button->GetHoverState(game)){ component->hovered=true;
button->hovered=true;
itemHovered=true; itemHovered=true;
selection.y=key; selection=component;
selection.x=index;
} }
} }
index++;
} }
} }
} }
if(itemHovered&&draggingComponent==nullptr&&selection!=vi2d{-1,-1}&&!UsingMouseNavigation()&&(game->KEY_CONFIRM.Held())){ if(itemHovered&&draggingComponent&&!selection.expired()&&!UsingMouseNavigation()&&(game->KEY_CONFIRM.Held())){
buttonHoldTime+=game->GetElapsedTime(); buttonHoldTime+=game->GetElapsedTime();
}else{ }else{
buttonHoldTime=0; buttonHoldTime=0;
} }
if(draggingComponent!=nullptr){ if(draggingComponent){
MenuComponent*selectedComponent=nullptr;
if(selection!=vi2d{-1,-1}){
selectedComponent=buttons[selection.y][selection.x];
}
auto ClearDraggingComponent=[&](){ auto ClearDraggingComponent=[&](){
delete draggingComponent; //We know we allocated a new instance of this, so we will now free it. draggingComponent={};
draggingComponent=nullptr;
}; };
if(!UsingMouseNavigation()){ if(!UsingMouseNavigation()){
if(!game->IsTextEntryEnabled()){ if(!game->IsTextEntryEnabled()){
if(game->KEY_CONFIRM.Released()){ if(game->KEY_CONFIRM.Released()){
if(selectedComponent==nullptr){//Dropping over an empty area. if(selection.expired()){//Dropping over an empty area.
ClearDraggingComponent(); ClearDraggingComponent();
}else }else
if(selectedComponent->DropDraggableItem(draggingComponent)){ if(selection.lock()->DropDraggableItem(std::move(draggingComponent))){
ClearDraggingComponent(); ClearDraggingComponent();
} }
} }
} }
}else{ }else{
if(game->KEY_CONFIRM.Released()){ if(game->KEY_CONFIRM.Released()){
if(selectedComponent==nullptr){//Dropping over an empty area. if(selection.expired()){//Dropping over an empty area.
ClearDraggingComponent(); ClearDraggingComponent();
}else }else
if(selectedComponent->DropDraggableItem(draggingComponent)){ if(selection.lock()->DropDraggableItem(std::move(draggingComponent))){
ClearDraggingComponent(); ClearDraggingComponent();
} }
} }
@ -276,29 +238,11 @@ void Menu::Update(AiL*game){
KeyboardButtonNavigation(game,pos); KeyboardButtonNavigation(game,pos);
} }
for(auto&[key,value]:buttons){ for(auto&[key,component]:components){
for(auto&button:value){ component->_BeforeUpdate(game);
if(button->renderInMain){
button->_BeforeUpdate(game);
}
}
}
for(auto&component:displayComponents){
if(component->renderInMain){
component->_BeforeUpdate(game);
}
} }
for(auto&[key,value]:buttons){ for(auto&[key,component]:components){
for(auto&button:value){ component->_Update(game);
if(button->renderInMain){
button->_Update(game);
}
}
}
for(auto&component:displayComponents){
if(component->renderInMain){
component->_Update(game);
}
} }
}; };
@ -309,10 +253,9 @@ void Menu::Draw(AiL*game){
DrawTiledWindowBackground(game,pos,size,GetRenderColor()); DrawTiledWindowBackground(game,pos,size,GetRenderColor());
} }
std::vector<MenuComponent*>allComponents; std::vector<std::weak_ptr<MenuComponent>>allComponents;
std::copy(displayComponents.begin(),displayComponents.end(),std::back_inserter(allComponents)); std::for_each(components.begin(),components.end(),[&](auto&pair){allComponents.push_back(pair.second);});
std::for_each(buttons.begin(),buttons.end(),[&](auto&pair){std::copy(pair.second.begin(),pair.second.end(),std::back_inserter(allComponents));}); std::sort(allComponents.begin(),allComponents.end(),[](std::weak_ptr<MenuComponent>c1,std::weak_ptr<MenuComponent>c2){return c1.lock()->depth>c2.lock()->depth;});
std::sort(allComponents.begin(),allComponents.end(),[](MenuComponent*c1,MenuComponent*c2){return c1->depth>c2->depth;});
if(GetCurrentTheme().IsScaled()){ if(GetCurrentTheme().IsScaled()){
DrawScaledWindowBorder(game,pos,size,GetRenderColor()); DrawScaledWindowBorder(game,pos,size,GetRenderColor());
@ -321,23 +264,22 @@ void Menu::Draw(AiL*game){
} }
for(const auto&component:allComponents){ for(const auto&component:allComponents){
if(component->renderInMain){ if(!component.expired()&&component.lock()->renderInMain){
component->_DrawDecal(window,this==Menu::stack.back()); component.lock()->_DrawDecal(window,this==Menu::stack.back());
} }
} }
if(draggingComponent!=nullptr){ if(draggingComponent){
vf2d offsetPos=draggingComponent->rect.pos; vf2d offsetPos=draggingComponent->rect.pos;
if(!UsingMouseNavigation()){ if(!UsingMouseNavigation()){
MenuComponent*selectedComponent=buttons[selection.y][selection.x];
vf2d drawOffset{}; vf2d drawOffset{};
if(selectedComponent->parentComponent!=nullptr){ if(!selection.expired()){
ScrollableWindowComponent*scrollableComponent=DYNAMIC_CAST<ScrollableWindowComponent*>(selectedComponent->parentComponent); if(!selection.lock()->parentComponent.expired()){
if(scrollableComponent!=nullptr){ std::weak_ptr<ScrollableWindowComponent>scrollableComponent=DYNAMIC_POINTER_CAST<ScrollableWindowComponent>(selection.lock()->parentComponent.lock());
drawOffset+=scrollableComponent->GetScrollAmount(); drawOffset+=scrollableComponent.lock()->GetScrollAmount();
} }
draggingComponent->V(A::DRAW_OFFSET)=drawOffset+pos-offsetPos+selection.lock()->rect.pos+vi2d{1,-4};
} }
draggingComponent->V(A::DRAW_OFFSET)=drawOffset+pos-offsetPos+selectedComponent->rect.pos+vi2d{1,-4};
draggingComponent->DrawDecal(window,this==Menu::stack.back()); draggingComponent->DrawDecal(window,this==Menu::stack.back());
}else{ }else{
draggingComponent->V(A::DRAW_OFFSET)-offsetPos+game->GetMousePos(); draggingComponent->V(A::DRAW_OFFSET)-offsetPos+game->GetMousePos();
@ -352,132 +294,19 @@ void Menu::OpenMenu(MenuType menu,bool cover){
} }
void Menu::KeyboardButtonNavigation(AiL*game,vf2d menuPos){ void Menu::KeyboardButtonNavigation(AiL*game,vf2d menuPos){
vi2d prevSelection=selection; std::weak_ptr<MenuComponent>prevSelection=selection;
if(game->RightPressed()){
if(selection==vi2d{-1,-1})return;
SetMouseNavigation(false);
selection.x=(size_t(selection.x)+1)%keyboardButtons[selection.y].size();
}
if(game->LeftPressed()){
if(selection==vi2d{-1,-1})return;
selection.x--;
SetMouseNavigation(false);
if(selection.x<0)selection.x+=int32_t(keyboardButtons[selection.y].size());
}
if(game->DownPressed()||game->UpPressed()){
if(game->DownPressed()){
SetMouseNavigation(false);
bool found=false;
bool selectedItem=false;
if(selection==vi2d{-1,-1}){
//Highlight first item.
for(auto&[key,value]:keyboardButtons){
selection.y=key;
break;
}
}else{
for(auto&[key,value]:keyboardButtons){
if(found){ //Once we discover the previous element, the next element becomes our next selection.
int previousButtonX=int(keyboardButtons[selection.y][selection.x]->rect.pos.x);
selection.y=key;
int index=0;
for(auto&button:value){ //Try to match a button in the same column as this button first.
if(previousButtonX==button->rect.pos.x){
selection.x=index;
break;
}
index++;
}
selectedItem=true;
break;
}
if(key==selection.y
//It's entirely possible this button was selected from the button selection list and may be out-of-bounds here.
&&selection.x>=0&&selection.x<keyboardButtons[selection.y].size()){
found=true;
}
}
if(!selectedItem){ //This means we need to loop around instead and pick the first one.
for(auto&[key,value]:keyboardButtons){
selection.y=key;
break;
}
}
}
}
if(game->UpPressed()){
SetMouseNavigation(false);
if(selection==vi2d{-1,-1}){
//Highlight last item.
for(auto&[key,value]:keyboardButtons){
selection.y=key;
}
}else{
int prevInd=-1;
for(auto&[key,value]:keyboardButtons){
if(key==selection.y&&
//It's entirely possible this button was selected from the button selection list and may be out-of-bounds here.
selection.x>=0&&selection.x<keyboardButtons[selection.y].size()){
break;
}
prevInd=key;
}
if(prevInd!=-1){
int previousButtonX=int(keyboardButtons[selection.y][selection.x]->rect.pos.x);
selection.y=prevInd;
int index=0;
for(auto&button:keyboardButtons[prevInd]){ //Try to match a button in the same column as this button first.
if(previousButtonX==button->rect.pos.x){
selection.x=index;
break;
}
index++;
}
}else{ //Since we didn't find it, it means we're at the top of the list or the list is empty. Go to the last element and use that one.
int lastInd=-1;
for(auto&[key,value]:keyboardButtons){
lastInd=key;
}
selection.y=lastInd;
}
}
}
//In both cases, we should clamp the X index to make sure it's still valid.
if(selection.y!=-1){
selection.x=std::clamp(selection.x,0,int(keyboardButtons[selection.y].size())-1);
}else{
selection.x=-1;
}
}
if(game->KEY_CONFIRM.Pressed()){ if(game->KEY_CONFIRM.Pressed()){
SetMouseNavigation(game->GetMouse(0).bPressed); //If a click occurs we use mouse controls. SetMouseNavigation(game->GetMouse(0).bPressed); //If a click occurs we use mouse controls.
if(!UsingMouseNavigation()){ buttonHoldTime=0;
buttonHoldTime=0;
//Key presses automatically highlight the first button if it's not highlighted.
if(selection==vi2d{-1,-1}&&buttons.size()>0){
//Find the first possible button entry in the map...
int firstInd=-1;
for(auto&[key,value]:buttons){
if(buttons[key].size()>0){
firstInd=key;
break;
}
}
if(firstInd!=-1){ //This means we found a valid menu item. If we didn't find one don't highlight any menu item...
selection={0,firstInd};
}
}
}else{
buttonHoldTime=0;
}
} }
if(prevSelection!=selection){ if(&*prevSelection.lock()!=&*selection.lock()){
if(selection!=vi2d{-1,-1}&&(buttons[selection.y][selection.x]->disabled||buttons[selection.y][selection.x]->grayedOut)){ if(!selection.expired()&&(selection.lock()->disabled||selection.lock()->grayedOut)){
bool handled=false; bool handled=false;
if(!UsingMouseNavigation()){ if(!UsingMouseNavigation()){
//Let's transfer some information about our selection being off the screen. Our intention with keyboard controls is that the screen will scroll to the correct location instead. //Let's transfer some information about our selection being off the screen. Our intention with keyboard controls is that the screen will scroll to the correct location instead.
//If we return false, then we handled it ourselves, no need to go back to the previous selection. //If we return false, then we handled it ourselves, no need to go back to the previous selection.
if(HandleOutsideDisabledButtonSelection(buttons[selection.y][selection.x])){ if(HandleOutsideDisabledButtonSelection(selection)){
handled=true; handled=true;
} }
} }
@ -572,9 +401,9 @@ Pixel Menu::GetRenderColor(){
return col; return col;
} }
bool Menu::HandleOutsideDisabledButtonSelection(MenuComponent*disabledButton){ bool Menu::HandleOutsideDisabledButtonSelection(std::weak_ptr<MenuComponent>disabledButton){
if(disabledButton->parentComponent!=nullptr){ if(!disabledButton.expired()){
return disabledButton->parentComponent->HandleOutsideDisabledButtonSelection(disabledButton); return disabledButton.lock()->parentComponent.lock()->HandleOutsideDisabledButtonSelection(disabledButton);
}else{ }else{
return false; return false;
} }
@ -595,25 +424,25 @@ void Menu::SetMouseNavigation(bool mouseNavigation){
void Menu::InventorySlotsUpdated(ITCategory cat){ void Menu::InventorySlotsUpdated(ITCategory cat){
//Update the inventory with a new inventory slot, since there's one additional item to interact with now. //Update the inventory with a new inventory slot, since there's one additional item to interact with now.
for(MenuComponent*component:inventoryListeners.at(cat)){ for(std::weak_ptr<MenuComponent>component:inventoryListeners.at(cat)){
InventoryScrollableWindowComponent*comp=DYNAMIC_CAST<InventoryScrollableWindowComponent*>(component); //HACK ALERT! We're assuming that these must be these classes otherwise they have no reason to even be using these listeners. Make sure that the lowest base class that requires these implements these functions!!! std::weak_ptr<InventoryScrollableWindowComponent>comp=DYNAMIC_POINTER_CAST<InventoryScrollableWindowComponent>(component.lock()); //HACK ALERT! We're assuming that these must be these classes otherwise they have no reason to even be using these listeners. Make sure that the lowest base class that requires these implements these functions!!!
comp->OnInventorySlotsUpdate(cat); comp.lock()->OnInventorySlotsUpdate(cat);
} }
} }
void Menu::MerchantInventorySlotsUpdated(ITCategory cat){ void Menu::MerchantInventorySlotsUpdated(ITCategory cat){
//Update the inventory with a new inventory slot, since there's one additional item to interact with now. //Update the inventory with a new inventory slot, since there's one additional item to interact with now.
for(MenuComponent*component:merchantInventoryListeners.at(cat)){ for(std::weak_ptr<MenuComponent>component:merchantInventoryListeners.at(cat)){
InventoryScrollableWindowComponent*comp=DYNAMIC_CAST<InventoryScrollableWindowComponent*>(component); //HACK ALERT! We're assuming that these must be these classes otherwise they have no reason to even be using these listeners. Make sure that the lowest base class that requires these implements these functions!!! std::weak_ptr<InventoryScrollableWindowComponent>comp=DYNAMIC_POINTER_CAST<InventoryScrollableWindowComponent>(component.lock()); //HACK ALERT! We're assuming that these must be these classes otherwise they have no reason to even be using these listeners. Make sure that the lowest base class that requires these implements these functions!!!
comp->OnInventorySlotsUpdate(cat); comp.lock()->OnInventorySlotsUpdate(cat);
} }
} }
void Menu::AddInventoryListener(MenuComponent*component,ITCategory category){ void Menu::AddInventoryListener(std::weak_ptr<MenuComponent>component,ITCategory category){
if(inventoryListeners.count(category)){ if(inventoryListeners.count(category)){
std::vector<MenuComponent*>&listenerList=inventoryListeners.at(category); std::vector<std::weak_ptr<MenuComponent>>&listenerList=inventoryListeners.at(category);
if(std::find(listenerList.begin(),listenerList.end(),component)!=listenerList.end()){ if(std::find_if(listenerList.begin(),listenerList.end(),[&](auto&ptr){return &*ptr.lock()==&*component.lock();})!=listenerList.end()){
ERR("WARNING! Component "<<component->name<<" has already been added to the "<<category<<" listener list! There should not be any duplicates!!") ERR("WARNING! Component "<<component.lock()->name<<" has already been added to the "<<category<<" listener list! There should not be any duplicates!!")
} }
listenerList.push_back(component); listenerList.push_back(component);
}else{ }else{
@ -626,11 +455,11 @@ void Menu::InitializeMenuListenerCategory(const std::string&category){
merchantInventoryListeners[category]; merchantInventoryListeners[category];
} }
void Menu::AddMerchantInventoryListener(MenuComponent*component,ITCategory category){ void Menu::AddMerchantInventoryListener(std::weak_ptr<MenuComponent>component,ITCategory category){
if(merchantInventoryListeners.count(category)){ if(merchantInventoryListeners.count(category)){
std::vector<MenuComponent*>&listenerList=merchantInventoryListeners.at(category); std::vector<std::weak_ptr<MenuComponent>>&listenerList=merchantInventoryListeners.at(category);
if(std::find(listenerList.begin(),listenerList.end(),component)!=listenerList.end()){ if(std::find_if(listenerList.begin(),listenerList.end(),[&](auto&ptr){return &*ptr.lock()==&*component.lock();})!=listenerList.end()){
ERR("WARNING! Component "<<component->name<<" has already been added to the "<<category<<" merchant listener list! There should not be any duplicates!!") ERR("WARNING! Component "<<component.lock()->name<<" has already been added to the "<<category<<" merchant listener list! There should not be any duplicates!!")
} }
listenerList.push_back(component); listenerList.push_back(component);
}else{ }else{
@ -638,9 +467,9 @@ void Menu::AddMerchantInventoryListener(MenuComponent*component,ITCategory categ
} }
} }
void Menu::AddEquipStatListener(MenuComponent*component){ void Menu::AddEquipStatListener(std::weak_ptr<MenuComponent>component){
if(std::find(equipStatListeners.begin(),equipStatListeners.end(),component)!=equipStatListeners.end()){ if(std::find_if(equipStatListeners.begin(),equipStatListeners.end(),[&](auto&ptr){return &*ptr.lock()==&*component.lock();})!=equipStatListeners.end()){
ERR("WARNING! Component "<<component->name<<" has already been added to the Equip Stat listener list! There should not be any duplicates!!") ERR("WARNING! Component "<<component.lock()->name<<" has already been added to the Equip Stat listener list! There should not be any duplicates!!")
} }
equipStatListeners.push_back(component); equipStatListeners.push_back(component);
} }
@ -676,10 +505,8 @@ bool Menu::IsMenuOpen(){
void Menu::CleanupAllMenus(){ void Menu::CleanupAllMenus(){
for(auto&[key,value]:Menu::menus){ for(auto&[key,value]:Menu::menus){
Menu*menu=value; Menu*menu=value;
for(auto&componentKey:menu->components){ for(auto&[name,component]:menu->components){
MenuComponent*component=componentKey.second;
component->Cleanup(); component->Cleanup();
delete component;
} }
menu->components.Reset(); menu->components.Reset();
menu->Cleanup(); menu->Cleanup();
@ -702,7 +529,7 @@ void Menu::DrawThemedWindow(vf2d menuPos,vf2d size,Pixel renderColor){
void Menu::RecalculateComponentCount(){ void Menu::RecalculateComponentCount(){
componentCount=displayComponents.size()+buttons.size(); componentCount=components.size();
} }
const MenuType Menu::GetType()const{ const MenuType Menu::GetType()const{
@ -714,13 +541,13 @@ void Menu::LockInListeners(){
merchantInventoryListeners.SetInitialized(); merchantInventoryListeners.SetInitialized();
} }
void Menu::AddChapterListener(MenuComponent*component){ void Menu::AddChapterListener(std::weak_ptr<MenuComponent>component){
if(std::find(chapterListeners.begin(),chapterListeners.end(),component)!=chapterListeners.end()){ if(std::find_if(chapterListeners.begin(),chapterListeners.end(),[&](auto&ptr){return &*ptr.lock()==&*component.lock();})!=chapterListeners.end()){
ERR("WARNING! Component "<<component->name<<" has already been added to the Chapter listener list! There should not be any duplicates!!") ERR("WARNING! Component "<<component.lock()->name<<" has already been added to the Chapter listener list! There should not be any duplicates!!")
} }
chapterListeners.push_back(component); chapterListeners.push_back(component);
} }
MenuFuncData::MenuFuncData(Menu&menu,AiL*const game,MenuComponent*const component,ScrollableWindowComponent*const parentComponent) MenuFuncData::MenuFuncData(Menu&menu,AiL*const game,std::weak_ptr<MenuComponent>component,std::weak_ptr<ScrollableWindowComponent>parentComponent)
:menu(menu),game(game),component(component),parentComponent(parentComponent){} :menu(menu),game(game),component(component),parentComponent(parentComponent){}

@ -49,7 +49,7 @@ class MenuComponent;
class ScrollableWindowComponent; class ScrollableWindowComponent;
//Add a component to a menu using this macro. Follow-up with END at the end of it. //Add a component to a menu using this macro. Follow-up with END at the end of it.
#define ADD(key,componentType) _AddComponent<componentType>(key,NEW componentType #define ADD(key,componentType) _AddComponent<componentType>(key,std::make_shared<componentType>
#define END ) #define END )
#define DEPTH , #define DEPTH ,
@ -112,62 +112,28 @@ class Menu:public IAttributable{
friend class EntityStats; friend class EntityStats;
float buttonHoldTime=0; float buttonHoldTime=0;
vi2d selection={-1,-1}; std::weak_ptr<MenuComponent>selection;
vi2d lastActiveMousePos={}; vi2d lastActiveMousePos={};
int componentCount=0; int componentCount=0;
MenuComponent*draggingComponent=nullptr; std::unique_ptr<MenuComponent>draggingComponent;
ViewPort window; ViewPort window;
static safemap<ITCategory,std::vector<MenuComponent*>>inventoryListeners; //All menu components that care about inventory updates subscribe to this list indirectly (See Menu::AddInventoryListener()). static safemap<ITCategory,std::vector<std::weak_ptr<MenuComponent>>>inventoryListeners; //All menu components that care about inventory updates subscribe to this list indirectly (See Menu::AddInventoryListener()).
static safemap<ITCategory,std::vector<MenuComponent*>>merchantInventoryListeners; //All menu components that care about merchant inventory updates subscribe to this list indirectly (See Menu::AddMerchantInventoryListener()). static safemap<ITCategory,std::vector<std::weak_ptr<MenuComponent>>>merchantInventoryListeners; //All menu components that care about merchant inventory updates subscribe to this list indirectly (See Menu::AddMerchantInventoryListener()).
static std::vector<MenuComponent*>equipStatListeners; //All menu components that care about stat/equip updates subscribe to this list indirectly (See Menu::AddStatListener()). static std::vector<std::weak_ptr<MenuComponent>>equipStatListeners; //All menu components that care about stat/equip updates subscribe to this list indirectly (See Menu::AddStatListener()).
static std::vector<MenuComponent*>chapterListeners; //All menu components that care about story chapter updates subscribe to this list indirectly (See Menu::AddChapterListener()). static std::vector<std::weak_ptr<MenuComponent>>chapterListeners; //All menu components that care about story chapter updates subscribe to this list indirectly (See Menu::AddChapterListener()).
public: public:
//The constructor is private. Use CreateMenu() instead! //The constructor is private. Use CreateMenu() instead!
Menu()=default; Menu()=default;
~Menu();
//DO NOT USE DIRECTLY! You should be utilizing the ADD macro for adding components. //DO NOT USE DIRECTLY! You should be utilizing the ADD macro for adding components.
template<class T> template<class T>
T*_AddComponent(std::string componentKey,T*component,int depth=DEFAULT_DEPTH){ std::shared_ptr<T>_AddComponent(std::string componentKey,std::shared_ptr<T>component,int depth=DEFAULT_DEPTH){
component->parentMenu=type; component->parentMenu=type;
if(depth==DEFAULT_DEPTH){ if(depth==DEFAULT_DEPTH){
component->depth=STARTING_DEPTH-componentCount; component->depth=STARTING_DEPTH-componentCount;
}else{ }else{
component->depth=depth; component->depth=depth;
} }
if(component->selectable){
buttons.Unlock();
if(buttons.count(int(component->rect.pos.y))){
buttons.at(int(component->rect.pos.y)).push_back(component);
}else{
buttons[int(component->rect.pos.y)].push_back(component);
}
if(component->selectableViaKeyboard){
keyboardButtons.Unlock();
if(keyboardButtons.count(int(component->rect.pos.y))){
keyboardButtons.at(int(component->rect.pos.y)).push_back(component);
}else{
keyboardButtons[int(component->rect.pos.y)].push_back(component);
}
}
//We must lock the values before calling sort. Sort seems to try and create new accesses.
buttons.SetInitialized();
keyboardButtons.SetInitialized();
//We make an assumption that menu components are supposed to be in left-to-right order. Sometimes we may add things out-of-order, so this fixes the problem by sorting the items afterwards.
std::sort(buttons[int(component->rect.pos.y)].begin(),buttons[int(component->rect.pos.y)].end(),[](auto c1,auto c2){
return c1->GetPos().x<c2->GetPos().x;
});
if(keyboardButtons.count(int(component->rect.pos.y))){ //Keyboard buttons may not necessarily contain this key...Let's be sure.
std::sort(keyboardButtons[int(component->rect.pos.y)].begin(),keyboardButtons[int(component->rect.pos.y)].end(),[](auto c1,auto c2){
return c1->GetPos().x<c2->GetPos().x;
});
}
}else{
displayComponents.push_back(component);
}
RecalculateComponentCount(); RecalculateComponentCount();
if(components.count(componentKey)){ if(components.count(componentKey)){
@ -178,7 +144,6 @@ public:
components[componentKey]=component; components[componentKey]=component;
components.SetInitialized(); components.SetInitialized();
lastRegisteredComponent=componentKey; lastRegisteredComponent=componentKey;
std::erase_if(Menu::unhandledComponents,[&](auto b1){return b1==component;});
return component; return component;
} }
@ -194,27 +159,23 @@ public:
static std::vector<Menu*>stack; static std::vector<Menu*>stack;
static std::string themeSelection; static std::string themeSelection;
static safeunorderedmap<std::string,Theme>themes; static safeunorderedmap<std::string,Theme>themes;
static std::vector<MenuComponent*>unhandledComponents; //This list contains MenuComponents that are created and haven't been assigned via _AddComponent. If we get to the end of menu initialization and there are any components in this vector, we have leaked memory and will report this.
static const vf2d CENTERED; static const vf2d CENTERED;
static bool IsMenuOpen(); static bool IsMenuOpen();
const MenuType GetType()const; const MenuType GetType()const;
safemap<std::string,MenuComponent*>components; //A friendly way to interrogate any component we are interested in. safemap<std::string,std::shared_ptr<MenuComponent>>components; //A friendly way to interrogate any component we are interested in.
std::vector<MenuComponent*>displayComponents; //Components that are only for displaying purposes.
static std::map<MenuType,Menu*>menus; static std::map<MenuType,Menu*>menus;
vf2d pos; //Specify the upper-left corner of the window. Using CENTERED will always put this where the upper-left corner would center the window. vf2d pos; //Specify the upper-left corner of the window. Using CENTERED will always put this where the upper-left corner would center the window.
vf2d size; //Size in tiles (24x24), every menu will be tile-based vf2d size; //Size in tiles (24x24), every menu will be tile-based
safemap<int/*Y*/,std::vector<MenuComponent*>>buttons; //Buttons are stored in rows followed by their column order.
safemap<int/*Y*/,std::vector<MenuComponent*>>keyboardButtons; //Button ordered storage for keyboard/menu
static Theme&GetCurrentTheme(); static Theme&GetCurrentTheme();
static bool UsingMouseNavigation(); static bool UsingMouseNavigation();
void SetMouseNavigation(bool mouseNavigation); void SetMouseNavigation(bool mouseNavigation);
static void InventorySlotsUpdated(ITCategory cat); //Called whenever the player's inventory gets modified. static void InventorySlotsUpdated(ITCategory cat); //Called whenever the player's inventory gets modified.
static void MerchantInventorySlotsUpdated(ITCategory cat); //Called whenever a traveling merchant's inventory item gets updated. static void MerchantInventorySlotsUpdated(ITCategory cat); //Called whenever a traveling merchant's inventory item gets updated.
static void AddInventoryListener(MenuComponent*component,ITCategory category); //Adds a component to be in a given listener category. static void AddInventoryListener(std::weak_ptr<MenuComponent>component,ITCategory category); //Adds a component to be in a given listener category.
static void AddMerchantInventoryListener(MenuComponent*component,ITCategory category); //Adds a component to be in a given listener category. static void AddMerchantInventoryListener(std::weak_ptr<MenuComponent>component,ITCategory category); //Adds a component to be in a given listener category.
static void AddEquipStatListener(MenuComponent*component); //Adds a component to be in an equip stat listener. Will receive updates whenever stats are updated via equips. static void AddEquipStatListener(std::weak_ptr<MenuComponent>component); //Adds a component to be in an equip stat listener. Will receive updates whenever stats are updated via equips.
static void AddChapterListener(MenuComponent*component); //Adds a component to be in a chapter listener. Will receive updates anytime the chapter in-game changes. static void AddChapterListener(std::weak_ptr<MenuComponent>component); //Adds a component to be in a chapter listener. Will receive updates anytime the chapter in-game changes.
vf2d center(); vf2d center();
//Returns the last menu type created and last registered component, in case a component is detected as memory leaking, provides this information to each component for safety. //Returns the last menu type created and last registered component, in case a component is detected as memory leaking, provides this information to each component for safety.
static std::pair<MenuType,std::string>GetMemoryLeakReportInfo(); static std::pair<MenuType,std::string>GetMemoryLeakReportInfo();
@ -242,7 +203,7 @@ private:
static void DrawTiledWindowBorder(AiL*game,vf2d menuPos,vf2d size,Pixel renderColor); static void DrawTiledWindowBorder(AiL*game,vf2d menuPos,vf2d size,Pixel renderColor);
//This triggers if we use a keyboard/controller input to try and select some off-screen menu item. We should ideally follow the menu cursor. //This triggers if we use a keyboard/controller input to try and select some off-screen menu item. We should ideally follow the menu cursor.
bool HandleOutsideDisabledButtonSelection(MenuComponent*disabledButton); bool HandleOutsideDisabledButtonSelection(std::weak_ptr<MenuComponent>disabledButton);
Pixel GetRenderColor(); Pixel GetRenderColor();
MenuType type; MenuType type;
@ -252,17 +213,16 @@ private:
}; };
template<typename T> template<typename T>
T*Component(MenuType menu,std::string componentName){ std::shared_ptr<T>Component(MenuType menu,std::string componentName){
T*tmp=DYNAMIC_CAST<T*>(Menu::menus[menu]->components[componentName]); return DYNAMIC_POINTER_CAST<T>(Menu::menus[menu]->components[componentName]);
return tmp;
} }
struct MenuFuncData{ struct MenuFuncData{
Menu&menu; Menu&menu;
AiL*const game; AiL*const game;
MenuComponent*const component; const std::weak_ptr<MenuComponent> component;
ScrollableWindowComponent*const parentComponent=nullptr; const std::weak_ptr<ScrollableWindowComponent> parentComponent={};
MenuFuncData(Menu&menu,AiL*const game,MenuComponent*const component,ScrollableWindowComponent*const parentComponent=nullptr); MenuFuncData(Menu&menu,AiL*const game,std::weak_ptr<MenuComponent> component,std::weak_ptr<ScrollableWindowComponent>parentComponent={});
}; };
using MenuFunc=std::function<bool(MenuFuncData)>; using MenuFunc=std::function<bool(MenuFuncData)>;

@ -51,9 +51,9 @@ private:
public: public:
inline MenuAnimatedIconToggleButton(geom2d::rect<float>rect,std::string animation,MenuFunc onClick) inline MenuAnimatedIconToggleButton(geom2d::rect<float>rect,std::string animation,MenuFunc onClick)
:MenuAnimatedIconButton(rect,animation,[](MenuFuncData data){ :MenuAnimatedIconButton(rect,animation,[](MenuFuncData data){
MenuAnimatedIconToggleButton*button=(MenuAnimatedIconToggleButton*)data.component; std::weak_ptr<MenuAnimatedIconToggleButton>button=DYNAMIC_POINTER_CAST<MenuAnimatedIconToggleButton>(data.component.lock());
button->Select(); button.lock()->Select();
button->_onClick(data); button.lock()->_onClick(data);
return true; return true;
}),_onClick(onClick){} }),_onClick(onClick){}
protected: protected:

@ -39,15 +39,14 @@ All rights reserved.
#include "MenuComponent.h" #include "MenuComponent.h"
#include "drawutil.h" #include "drawutil.h"
#include "util.h" #include "util.h"
#include "ScrollableWindowComponent.h"
INCLUDE_game INCLUDE_game
using A=Attribute; using A=Attribute;
MenuComponent::MenuComponent(geom2d::rect<float>rect,std::string label,MenuFunc onClick,ButtonAttr attributes) MenuComponent::MenuComponent(geom2d::rect<float>rect,std::string label,MenuFunc onClick,ButtonAttr attributes)
:rect(rect),originalPos(rect.pos),label(label),menuDest(MenuType::ENUM_END),onClick(onClick),hoverEffect(0),selectable(!(attributes&ButtonAttr::UNSELECTABLE)),selectableViaKeyboard(!(attributes&ButtonAttr::UNSELECTABLE_VIA_KEYBOARD)),memoryLeakInfo(Menu::GetMemoryLeakReportInfo()),fitToLabel(attributes&ButtonAttr::FIT_TO_LABEL){ :rect(rect),originalPos(rect.pos),label(label),menuDest(MenuType::ENUM_END),onClick(onClick),hoverEffect(0),selectable(!(attributes&ButtonAttr::UNSELECTABLE)),selectableViaKeyboard(!(attributes&ButtonAttr::UNSELECTABLE_VIA_KEYBOARD)),memoryLeakInfo(Menu::GetMemoryLeakReportInfo()),fitToLabel(attributes&ButtonAttr::FIT_TO_LABEL){}
Menu::unhandledComponents.push_back(this);
}
MenuComponent::MenuComponent(geom2d::rect<float>rect,std::string label,MenuType menuDest,MenuFunc onClick,ButtonAttr attributes) MenuComponent::MenuComponent(geom2d::rect<float>rect,std::string label,MenuType menuDest,MenuFunc onClick,ButtonAttr attributes)
:MenuComponent(rect,label,onClick,attributes){ :MenuComponent(rect,label,onClick,attributes){
@ -61,19 +60,7 @@ MenuComponent::MenuComponent(geom2d::rect<float>rect,std::string label,MenuType
this->labelScaling=labelScaling; this->labelScaling=labelScaling;
} }
MenuComponent::~MenuComponent(){ MenuComponent::~MenuComponent(){}
Menu*pMenu=Menu::menus[parentMenu];
for(auto key:pMenu->buttons){
std::vector<MenuComponent*>&components=key.second;
std::erase_if(components,[&](MenuComponent*component){return component==this;});
}
for(auto key:pMenu->keyboardButtons){
std::vector<MenuComponent*>&components=key.second;
std::erase_if(components,[&](MenuComponent*component){return component==this;});
}
std::erase_if(pMenu->displayComponents,[&](MenuComponent*component){return component==this;});
//pMenu->components.erase(this->name); //We're not going to do this here because we are in the middle of a loop for another menu component when cleaning up.
}
void MenuComponent::AfterCreate(){} void MenuComponent::AfterCreate(){}
@ -146,17 +133,17 @@ void MenuComponent::_DrawDecal(ViewPort&window,bool focused){
} }
} }
MenuComponent*MenuComponent::PickUpDraggableItem(){ std::unique_ptr<MenuComponent>MenuComponent::PickUpDraggableItem(){
return nullptr; return {};
} }
bool MenuComponent::DropDraggableItem(MenuComponent*draggable){ bool MenuComponent::DropDraggableItem(std::unique_ptr<MenuComponent>draggable){
return false; return false;
} }
bool MenuComponent::GetHoverState(AiL*game){ bool MenuComponent::GetHoverState(AiL*game){
if(parentComponent!=nullptr){ if(!parentComponent.expired()){
return parentComponent->GetHoverState(game,this); return parentComponent.lock()->GetHoverState(game,this);
}else{ }else{
vf2d parentWindowPos=Menu::menus[parentMenu]->pos; vf2d parentWindowPos=Menu::menus[parentMenu]->pos;
return geom2d::overlaps(geom2d::rect<float>{rect.pos+parentWindowPos,rect.size},game->GetMousePos()); return geom2d::overlaps(geom2d::rect<float>{rect.pos+parentWindowPos,rect.size},game->GetMousePos());
@ -168,22 +155,22 @@ bool MenuComponent::GetHoverState(AiL*game,MenuComponent*child){
} }
bool MenuComponent::PointWithinParent(MenuComponent*child,vi2d drawPos){ bool MenuComponent::PointWithinParent(MenuComponent*child,vi2d drawPos){
if(parentComponent!=nullptr){ if(!parentComponent.expired()){
return parentComponent->PointWithinParent(child,drawPos); return parentComponent.lock()->PointWithinParent(child,drawPos);
}else{ }else{
return true; return true;
} }
} }
bool MenuComponent::PointWithinParent(MenuComponent*child,geom2d::rect<float> drawRect){ bool MenuComponent::PointWithinParent(MenuComponent*child,geom2d::rect<float> drawRect){
if(parentComponent!=nullptr){ if(!parentComponent.expired()){
return parentComponent->PointWithinParent(child,drawRect); return parentComponent.lock()->PointWithinParent(child,drawRect);
}else{ }else{
return true; return true;
} }
} }
bool MenuComponent::HandleOutsideDisabledButtonSelection(MenuComponent*disabledButton){ bool MenuComponent::HandleOutsideDisabledButtonSelection(std::weak_ptr<MenuComponent>disabledButton){
return false; return false;
}; };
@ -225,7 +212,7 @@ void MenuComponent::_OnMouseOut(){
if(runHoverFunctions){ if(runHoverFunctions){
if(hoverState){ if(hoverState){
hoverState=false; hoverState=false;
onMouseOut(MenuFuncData{*Menu::menus[parentMenu],game,this,(ScrollableWindowComponent*)(parentComponent)}); onMouseOut(MenuFuncData{*Menu::menus[parentMenu],game,std::make_shared<MenuComponent>(*this),DYNAMIC_POINTER_CAST<ScrollableWindowComponent>(parentComponent.lock())});
OnMouseOut(); OnMouseOut();
} }
} }
@ -235,7 +222,7 @@ void MenuComponent::_OnHover(){
if(hovered){ if(hovered){
if(runHoverFunctions&&!hoverState){ if(runHoverFunctions&&!hoverState){
hoverState=true; hoverState=true;
onHover(MenuFuncData{*Menu::menus[parentMenu],game,this,(ScrollableWindowComponent*)(parentComponent)}); onHover(MenuFuncData{*Menu::menus[parentMenu],game,std::make_shared<MenuComponent>(*this),DYNAMIC_POINTER_CAST<ScrollableWindowComponent>(parentComponent.lock())});
OnHover(); OnHover();
} }
} }
@ -259,5 +246,5 @@ void MenuComponent::OnPlayerMoneyUpdate(uint32_t newMoney){}
void MenuComponent::OnChapterUpdate(uint8_t newChapter){} void MenuComponent::OnChapterUpdate(uint8_t newChapter){}
void MenuComponent::Click(){ void MenuComponent::Click(){
onClick(MenuFuncData{*Menu::menus[parentMenu],game,this}); onClick(MenuFuncData{*Menu::menus[parentMenu],game,std::make_shared<MenuComponent>(*this)});
} }

@ -126,7 +126,7 @@ protected:
virtual void OnHover(); virtual void OnHover();
public: public:
MenuType parentMenu=MenuType::ENUM_END; MenuType parentMenu=MenuType::ENUM_END;
MenuComponent*parentComponent=nullptr; std::weak_ptr<MenuComponent>parentComponent{};
MenuComponent(geom2d::rect<float>rect,std::string label,MenuFunc onClick,ButtonAttr attributes=ButtonAttr::NONE); MenuComponent(geom2d::rect<float>rect,std::string label,MenuFunc onClick,ButtonAttr attributes=ButtonAttr::NONE);
MenuComponent(geom2d::rect<float>rect,std::string label,MenuType menuDest,MenuFunc onClick,ButtonAttr attributes=ButtonAttr::NONE); MenuComponent(geom2d::rect<float>rect,std::string label,MenuType menuDest,MenuFunc onClick,ButtonAttr attributes=ButtonAttr::NONE);
MenuComponent(geom2d::rect<float>rect,std::string label,MenuType menuDest,MenuFunc onClick,vf2d labelScaling,ButtonAttr attributes=ButtonAttr::NONE); MenuComponent(geom2d::rect<float>rect,std::string label,MenuType menuDest,MenuFunc onClick,vf2d labelScaling,ButtonAttr attributes=ButtonAttr::NONE);
@ -135,11 +135,11 @@ public:
const vf2d&GetSize()const; const vf2d&GetSize()const;
//We picked up a draggable component, we should make a copy and return it here. If a nullptr is returned here, the pickup is not allowed. //We picked up a draggable component, we should make a copy and return it here. If a nullptr is returned here, the pickup is not allowed.
//WARNING!!! This allocates a brand new component when successful!!! Be prepared to clear it! //WARNING!!! This allocates a brand new component when successful!!! Be prepared to clear it!
virtual MenuComponent*PickUpDraggableItem(); virtual std::unique_ptr<MenuComponent>PickUpDraggableItem();
//We are attempting to drop draggable onto this item. If it's not allowed, return false. //We are attempting to drop draggable onto this item. If it's not allowed, return false.
virtual bool DropDraggableItem(MenuComponent*draggable); virtual bool DropDraggableItem(std::unique_ptr<MenuComponent>draggable);
//A notification that a button outside the region has been selected. Return false if it's not allowed. //A notification that a button outside the region has been selected. Return false if it's not allowed.
virtual bool HandleOutsideDisabledButtonSelection(MenuComponent*disabledButton); virtual bool HandleOutsideDisabledButtonSelection(std::weak_ptr<MenuComponent>disabledButton);
//Called whenever equipment and base stats are updated, notifying a component that numbers that may be displayed have changed. //Called whenever equipment and base stats are updated, notifying a component that numbers that may be displayed have changed.
virtual void OnEquipStatsUpdate(); virtual void OnEquipStatsUpdate();
virtual const std::string&GetLabel()const; virtual const std::string&GetLabel()const;

@ -147,9 +147,9 @@ protected:
} }
} }
} }
inline MenuComponent*PickUpDraggableItem()override final{ inline std::unique_ptr<MenuComponent>PickUpDraggableItem()override final{
if(valid){ if(valid){
MenuItemButton*pickUp=NEW MenuItemButton(rect,invRef,inventoryIndex,onClick,itemDescriptionMenu,itemNameLabelName,itemDescriptionLabelName); std::unique_ptr<MenuItemButton>pickUp=std::make_unique<MenuItemButton>(rect,invRef,inventoryIndex,onClick,itemDescriptionMenu,itemNameLabelName,itemDescriptionLabelName);
valid=false; valid=false;
return pickUp; return pickUp;
}else{ }else{
@ -157,9 +157,9 @@ protected:
} }
} }
inline bool DropDraggableItem(MenuComponent*draggable)override final{ inline bool DropDraggableItem(std::unique_ptr<MenuComponent>draggable)override final{
//HACK Warning! We're making a bold assumption that every component that is draggable is of the same type! This may change in the future.... //HACK Warning! We're making a bold assumption that every component that is draggable is of the same type! This may change in the future....
MenuItemButton*draggedItem=(MenuItemButton*)draggable; MenuItemButton*draggedItem=DYNAMIC_CAST<MenuItemButton*>(draggable.get());
ITCategory cat=draggedItem->invRef.at(draggedItem->inventoryIndex)->Category(); ITCategory cat=draggedItem->invRef.at(draggedItem->inventoryIndex)->Category();
return Inventory::SwapItems(cat,draggedItem->inventoryIndex,inventoryIndex); return Inventory::SwapItems(cat,draggedItem->inventoryIndex,inventoryIndex);
} }

@ -30,7 +30,7 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE. SUCH DAMAGE.
Portions of this software are copyright © 2023 The FreeType Portions of this software are copyright <EFBFBD> 2023 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information. Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved. All rights reserved.
*/ */
@ -61,7 +61,7 @@ void Menu::InitializeMerchantWindow(){
} }
std::sort(categories.begin(),categories.end(),[](std::pair<std::string,int>&cat1,std::pair<std::string,int>&cat2){return cat1.second<cat2.second;}); std::sort(categories.begin(),categories.end(),[](std::pair<std::string,int>&cat1,std::pair<std::string,int>&cat2){return cat1.second<cat2.second;});
auto buyTab=merchantWindow->ADD("Buy Tab",MenuComponent)({{2,0},{merchantWindow->size.x/2-4,24}},"Buy",[](MenuFuncData data){ auto buyTab=merchantWindow->ADD("Buy Tab",MenuComponent)(geom2d::rect<float>{{2,0},{merchantWindow->size.x/2-4,24}},"Buy",[](MenuFuncData data){
Component<RowInventoryScrollableWindowComponent>(MERCHANT,"Merchant Inventory Display")->Enable(true); Component<RowInventoryScrollableWindowComponent>(MERCHANT,"Merchant Inventory Display")->Enable(true);
Component<MenuComponent>(MERCHANT,"Sell Tab")->selected=false; Component<MenuComponent>(MERCHANT,"Sell Tab")->selected=false;
Component<MenuComponent>(MERCHANT,"Inventory Tabs Outline")->Enable(false); Component<MenuComponent>(MERCHANT,"Inventory Tabs Outline")->Enable(false);
@ -71,13 +71,13 @@ void Menu::InitializeMerchantWindow(){
} }
Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.menu.S(A::LAST_INVENTORY_TYPE_OPENED))->Enable(false); Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.menu.S(A::LAST_INVENTORY_TYPE_OPENED))->Enable(false);
Component<MenuComponent>(data.menu.GetType(),data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)+" Inventory Tab")->Enable(false); Component<MenuComponent>(data.menu.GetType(),data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)+" Inventory Tab")->Enable(false);
data.component->selected=true; data.component.lock()->selected=true;
return true; return true;
})END; })END;
buyTab->selected=true; buyTab->selected=true;
buyTab->selectionType=SelectionType::HIGHLIGHT; buyTab->selectionType=SelectionType::HIGHLIGHT;
auto sellTab=merchantWindow->ADD("Sell Tab",MenuComponent)({{merchantWindow->size.x/2+2,0},{merchantWindow->size.x/2-4,24}},"Sell",[](MenuFuncData data){ auto sellTab=merchantWindow->ADD("Sell Tab",MenuComponent)(geom2d::rect<float>{{merchantWindow->size.x/2+2,0},{merchantWindow->size.x/2-4,24}},"Sell",[](MenuFuncData data){
Component<RowInventoryScrollableWindowComponent>(MERCHANT,"Merchant Inventory Display")->Enable(false); Component<RowInventoryScrollableWindowComponent>(MERCHANT,"Merchant Inventory Display")->Enable(false);
Component<MenuComponent>(MERCHANT,"Buy Tab")->selected=false; Component<MenuComponent>(MERCHANT,"Buy Tab")->selected=false;
Component<MenuComponent>(MERCHANT,"Inventory Tabs Outline")->Enable(true); Component<MenuComponent>(MERCHANT,"Inventory Tabs Outline")->Enable(true);
@ -87,31 +87,31 @@ void Menu::InitializeMerchantWindow(){
} }
Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.menu.S(A::LAST_INVENTORY_TYPE_OPENED))->Enable(true); Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.menu.S(A::LAST_INVENTORY_TYPE_OPENED))->Enable(true);
Component<MenuComponent>(data.menu.GetType(),data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)+" Inventory Tab")->Enable(true); Component<MenuComponent>(data.menu.GetType(),data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)+" Inventory Tab")->Enable(true);
data.component->selected=true; data.component.lock()->selected=true;
return true; return true;
})END; })END;
sellTab->selectionType=SelectionType::HIGHLIGHT; sellTab->selectionType=SelectionType::HIGHLIGHT;
auto inventoryDisplay=merchantWindow->ADD("Merchant Inventory Display",RowInventoryScrollableWindowComponent)({{2,28},{220,merchantWindow->size.y-44}},"Item Name Label","Item Description Label", auto inventoryDisplay=merchantWindow->ADD("Merchant Inventory Display",RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{2,28},{220,merchantWindow->size.y-44}},"Item Name Label","Item Description Label",
[](MenuFuncData data){ [](MenuFuncData data){
RowItemDisplay*item=DYNAMIC_CAST<RowItemDisplay*>(data.component); std::weak_ptr<RowItemDisplay>item=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
Component<MenuLabel>(BUY_ITEM,"Item Purchase Header")->S(A::ITEM_NAME)=item->GetItem().lock()->ActualName(); Component<MenuLabel>(BUY_ITEM,"Item Purchase Header")->S(A::ITEM_NAME)=item.lock()->GetItem().lock()->ActualName();
Component<MenuLabel>(BUY_ITEM,"Price per item Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->BuyValue())); Component<MenuLabel>(BUY_ITEM,"Price per item Amount Label")->SetLabel(std::to_string(item.lock()->GetItem().lock()->BuyValue()));
Component<MenuLabel>(BUY_ITEM,"Amount to buy Amount Label")->SetLabel("1"); Component<MenuLabel>(BUY_ITEM,"Amount to buy Amount Label")->SetLabel("1");
Component<MenuLabel>(BUY_ITEM,"Total Price Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->BuyValue())); Component<MenuLabel>(BUY_ITEM,"Total Price Amount Label")->SetLabel(std::to_string(item.lock()->GetItem().lock()->BuyValue()));
Merchant&merchant=Merchant::GetCurrentTravelingMerchant(); Merchant&merchant=Merchant::GetCurrentTravelingMerchant();
bool canPurchase=merchant.CanPurchaseItem(item->GetItem().lock()->ActualName(),1); bool canPurchase=merchant.CanPurchaseItem(item.lock()->GetItem().lock()->ActualName(),1);
std::string colorCode=""; std::string colorCode="";
if(!canPurchase)colorCode="#FF0000"; if(!canPurchase)colorCode="#FF0000";
Component<MenuLabel>(BUY_ITEM,"Total Price Amount Label")->SetLabel(colorCode+std::to_string(item->GetItem().lock()->BuyValue())); Component<MenuLabel>(BUY_ITEM,"Total Price Amount Label")->SetLabel(colorCode+std::to_string(item.lock()->GetItem().lock()->BuyValue()));
Component<MenuLabel>(BUY_ITEM,"Item Purchase Header")->SetLabel("Buying "+item->GetItem().lock()->DisplayName()); Component<MenuLabel>(BUY_ITEM,"Item Purchase Header")->SetLabel("Buying "+item.lock()->GetItem().lock()->DisplayName());
Component<MenuComponent>(BUY_ITEM,"Purchase Button")->SetGrayedOut(!merchant.CanPurchaseItem(item->GetItem().lock()->ActualName(),1)); Component<MenuComponent>(BUY_ITEM,"Purchase Button")->SetGrayedOut(!merchant.CanPurchaseItem(item.lock()->GetItem().lock()->ActualName(),1));
Menu::OpenMenu(BUY_ITEM); Menu::OpenMenu(BUY_ITEM);
return true; return true;
}, },
[](MenuFuncData data){ [](MenuFuncData data){
Component<MenuItemItemButton>(data.menu.GetType(),"Item Icon")->SetItem(DYNAMIC_CAST<RowItemDisplay*>(data.component)->GetItem()); Component<MenuItemItemButton>(data.menu.GetType(),"Item Icon")->SetItem(DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock())->GetItem());
return true; return true;
}, },
[](MenuFuncData data){ [](MenuFuncData data){
@ -119,14 +119,14 @@ void Menu::InitializeMerchantWindow(){
return true; return true;
}, },
InventoryCreator::RowMerchant_InventoryUpdate, InventoryCreator::RowMerchant_InventoryUpdate,
{.padding=1,.size={220-13,28}})END; InventoryWindowOptions{.padding=1,.size={220-13,28}})END;
inventoryDisplay->SetPriceLabelType(PriceLabel::BUY_LABEL); inventoryDisplay->SetPriceLabelType(PriceLabel::BUY_LABEL);
for(auto&[category,items]:ITEM_CATEGORIES){ for(auto&[category,items]:ITEM_CATEGORIES){
Menu::AddMerchantInventoryListener(inventoryDisplay,category); Menu::AddMerchantInventoryListener(inventoryDisplay,category);
} }
merchantWindow->ADD("Inventory Tabs Outline",MenuComponent)({{0,28},{72,merchantWindow->size.y-44}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; merchantWindow->ADD("Inventory Tabs Outline",MenuComponent)(geom2d::rect<float>{{0,28},{72,merchantWindow->size.y-44}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
std::sort(categories.begin(),categories.end(),[](std::pair<std::string,int>&cat1,std::pair<std::string,int>&cat2){return cat1.second<cat2.second;}); std::sort(categories.begin(),categories.end(),[](std::pair<std::string,int>&cat1,std::pair<std::string,int>&cat2){return cat1.second<cat2.second;});
@ -137,41 +137,41 @@ void Menu::InitializeMerchantWindow(){
float buttonWidth=64; float buttonWidth=64;
float textScaling=std::min(1.f,buttonWidth/textWidth); float textScaling=std::min(1.f,buttonWidth/textWidth);
auto button=merchantWindow->ADD(category+" Inventory Tab",MenuComponent)({{2,30+yOffset},{68,16}},category,MenuType::ENUM_END, auto button=merchantWindow->ADD(category+" Inventory Tab",MenuComponent)(geom2d::rect<float>{{2,30+yOffset},{68,16}},category,MenuType::ENUM_END,
[&](MenuFuncData data){ [&](MenuFuncData data){
//Close the old inventory window and show the proper one. //Close the old inventory window and show the proper one.
Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.menu.S(A::LAST_INVENTORY_TYPE_OPENED))->Enable(false); Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.menu.S(A::LAST_INVENTORY_TYPE_OPENED))->Enable(false);
Component<MenuComponent>(data.menu.GetType(),data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)+" Inventory Tab")->SetSelected(false); Component<MenuComponent>(data.menu.GetType(),data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)+" Inventory Tab")->SetSelected(false);
Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.component->S(A::CATEGORY_NAME))->Enable(true); Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.component.lock()->S(A::CATEGORY_NAME))->Enable(true);
Component<MenuComponent>(data.menu.GetType(),data.component->S(A::CATEGORY_NAME)+" Inventory Tab")->SetSelected(true); Component<MenuComponent>(data.menu.GetType(),data.component.lock()->S(A::CATEGORY_NAME)+" Inventory Tab")->SetSelected(true);
data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)=data.component->S(A::CATEGORY_NAME); data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)=data.component.lock()->S(A::CATEGORY_NAME);
return true; return true;
},{textScaling,1.f})END; },vf2d{textScaling,1.f})END;
button->SetSelectionType(HIGHLIGHT); button->SetSelectionType(HIGHLIGHT);
button->S(A::CATEGORY_NAME)=category; button->S(A::CATEGORY_NAME)=category;
auto inventoryDisplay=merchantWindow->ADD("Inventory Display - "+category,RowInventoryScrollableWindowComponent)({{72,28},{150,merchantWindow->size.y-44}},"Item Name Label","Item Description Label", auto inventoryDisplay=merchantWindow->ADD("Inventory Display - "+category,RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{72,28},{150,merchantWindow->size.y-44}},"Item Name Label","Item Description Label",
[](MenuFuncData data){ [](MenuFuncData data){
RowItemDisplay*item=DYNAMIC_CAST<RowItemDisplay*>(data.component); std::weak_ptr<RowItemDisplay>item=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
if(item->GetItem().lock()->CanBeSold()){ if(item.lock()->GetItem().lock()->CanBeSold()){
Component<ItemMenuLabel>(SELL_ITEM,"Item Sell Header")->SetItem(item->GetItem()); Component<ItemMenuLabel>(SELL_ITEM,"Item Sell Header")->SetItem(item.lock()->GetItem());
Component<MenuLabel>(SELL_ITEM,"Price per item Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->SellValue())); Component<MenuLabel>(SELL_ITEM,"Price per item Amount Label")->SetLabel(std::to_string(item.lock()->GetItem().lock()->SellValue()));
Component<MenuLabel>(SELL_ITEM,"Amount to sell Amount Label")->SetLabel("1"); Component<MenuLabel>(SELL_ITEM,"Amount to sell Amount Label")->SetLabel("1");
Component<MenuLabel>(SELL_ITEM,"Total Price Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->SellValue())); Component<MenuLabel>(SELL_ITEM,"Total Price Amount Label")->SetLabel(std::to_string(item.lock()->GetItem().lock()->SellValue()));
Merchant&merchant=Merchant::GetCurrentTravelingMerchant(); Merchant&merchant=Merchant::GetCurrentTravelingMerchant();
bool canPurchase=merchant.CanSellItem(item->GetItem(),1); bool canPurchase=merchant.CanSellItem(item.lock()->GetItem(),1);
std::string colorCode=""; std::string colorCode="";
if(!canPurchase)colorCode="#FF0000"; if(!canPurchase)colorCode="#FF0000";
Component<MenuLabel>(SELL_ITEM,"Total Price Amount Label")->SetLabel(colorCode+std::to_string(item->GetItem().lock()->SellValue())); Component<MenuLabel>(SELL_ITEM,"Total Price Amount Label")->SetLabel(colorCode+std::to_string(item.lock()->GetItem().lock()->SellValue()));
Component<MenuLabel>(SELL_ITEM,"Item Sell Header")->SetLabel("Selling "+item->GetItem().lock()->DisplayName()); Component<MenuLabel>(SELL_ITEM,"Item Sell Header")->SetLabel("Selling "+item.lock()->GetItem().lock()->DisplayName());
Component<MenuComponent>(SELL_ITEM,"Sell Button")->SetGrayedOut(!merchant.CanSellItem(item->GetItem(),1)); Component<MenuComponent>(SELL_ITEM,"Sell Button")->SetGrayedOut(!merchant.CanSellItem(item.lock()->GetItem(),1));
Menu::OpenMenu(SELL_ITEM); Menu::OpenMenu(SELL_ITEM);
} }
return true; return true;
}, },
[](MenuFuncData data){ [](MenuFuncData data){
Component<MenuItemItemButton>(data.menu.GetType(),"Item Icon")->SetItem(DYNAMIC_CAST<RowItemDisplay*>(data.component)->GetItem()); Component<MenuItemItemButton>(data.menu.GetType(),"Item Icon")->SetItem(DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock())->GetItem());
return true; return true;
}, },
[](MenuFuncData data){ [](MenuFuncData data){
@ -179,7 +179,7 @@ void Menu::InitializeMerchantWindow(){
return true; return true;
}, },
InventoryCreator::RowPlayer_InventoryUpdate, InventoryCreator::RowPlayer_InventoryUpdate,
{.padding=1,.size={137,28}})END; InventoryWindowOptions{.padding=1,.size={137,28}})END;
inventoryDisplay->SetPriceLabelType(PriceLabel::SELL_LABEL); inventoryDisplay->SetPriceLabelType(PriceLabel::SELL_LABEL);
if(first){ if(first){
@ -198,27 +198,27 @@ void Menu::InitializeMerchantWindow(){
#pragma region Inventory Description #pragma region Inventory Description
float inventoryDescriptionWidth=merchantWindow->pos.x+merchantWindow->size.x-26-224; float inventoryDescriptionWidth=merchantWindow->pos.x+merchantWindow->size.x-26-224;
merchantWindow->ADD("Item Description Outline",MenuLabel)({{224,28},{inventoryDescriptionWidth,merchantWindow->size.y-44}},"",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; merchantWindow->ADD("Item Description Outline",MenuLabel)(geom2d::rect<float>{{224,28},{inventoryDescriptionWidth,merchantWindow->size.y-44}},"",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
merchantWindow->ADD("Item Icon",MenuItemItemButton)({{226+inventoryDescriptionWidth/2-24,30},{48,48}},Item::BLANK,MenuType::ENUM_END,DO_NOTHING,"","",IconButtonAttr::NOT_SELECTABLE)END; merchantWindow->ADD("Item Icon",MenuItemItemButton)(geom2d::rect<float>{{226+inventoryDescriptionWidth/2-24,30},{48,48}},Item::BLANK,MenuType::ENUM_END,DO_NOTHING,"","",IconButtonAttr::NOT_SELECTABLE)END;
merchantWindow->ADD("Item Name Label",MenuLabel)({{226,84},{inventoryDescriptionWidth-6,12}},"",0.75f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; merchantWindow->ADD("Item Name Label",MenuLabel)(geom2d::rect<float>{{226,84},{inventoryDescriptionWidth-6,12}},"",0.75f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
merchantWindow->ADD("Item Description Label",MenuLabel)({{226,94},{inventoryDescriptionWidth-6,merchantWindow->size.y-44-66}},"",0.5f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; merchantWindow->ADD("Item Description Label",MenuLabel)(geom2d::rect<float>{{226,94},{inventoryDescriptionWidth-6,merchantWindow->size.y-44-66}},"",0.5f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
#pragma endregion #pragma endregion
#pragma region Money Display #pragma region Money Display
vf2d moneyIconPos={224+inventoryDescriptionWidth-24,28+merchantWindow->size.y-44+6}; vf2d moneyIconPos={224+inventoryDescriptionWidth-24,28+merchantWindow->size.y-44+6};
auto moneyIcon=merchantWindow->ADD("Money Icon",MenuIconButton)({moneyIconPos,{24,24}},GFX["money.png"].Decal(),DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE|IconButtonAttr::NO_BACKGROUND)END; auto moneyIcon=merchantWindow->ADD("Money Icon",MenuIconButton)(geom2d::rect<float>{moneyIconPos,{24,24}},GFX["money.png"].Decal(),DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE|IconButtonAttr::NO_BACKGROUND)END;
std::string moneyText=std::to_string(game->GetPlayer()->GetMoney()); std::string moneyText=std::to_string(game->GetPlayer()->GetMoney());
vf2d moneyTextSize=game->GetTextSizeProp(moneyText)*2; vf2d moneyTextSize=game->GetTextSizeProp(moneyText)*2;
auto moneyDisplay=merchantWindow->ADD("Money Label",PlayerMoneyLabel)({moneyIconPos-vf2d{2+moneyTextSize.x,-2},moneyTextSize},2,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN|ComponentAttr::FIT_TO_LABEL)END; auto moneyDisplay=merchantWindow->ADD("Money Label",PlayerMoneyLabel)(geom2d::rect<float>{moneyIconPos-vf2d{2+moneyTextSize.x,-2},moneyTextSize},2,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN|ComponentAttr::FIT_TO_LABEL)END;
moneyDisplay->SetRightAlignment(true); moneyDisplay->SetRightAlignment(true);
Player::AddMoneyListener(moneyDisplay); Player::AddMoneyListener(moneyDisplay);
#pragma endregion #pragma endregion
merchantWindow->ADD("Leave Button",MenuComponent)({{merchantWindow->size.x/2-48,28+merchantWindow->size.y-44+6},{96,24}},"Leave",MenuType::ENUM_END, merchantWindow->ADD("Leave Button",MenuComponent)(geom2d::rect<float>{{merchantWindow->size.x/2-48,28+merchantWindow->size.y-44+6},{96,24}},"Leave",MenuType::ENUM_END,
[](MenuFuncData data){ [](MenuFuncData data){
Menu::CloseMenu(); Menu::CloseMenu();
return true; return true;
},{2,2})END; },vf2d{2,2})END;
buyTab->onClick(MenuFuncData{*merchantWindow,game,buyTab}); buyTab->onClick(MenuFuncData{*merchantWindow,game,buyTab});
} }

@ -52,14 +52,14 @@ void Menu::InitializeOverworldMapLevelWindow(){
vf2d windowSize={game->GetScreenSize().x/3.f-24,float(game->GetScreenSize().y)-48}; vf2d windowSize={game->GetScreenSize().x/3.f-24,float(game->GetScreenSize().y)-48};
Menu*levelSelectWindow=CreateMenu(OVERWORLD_LEVEL_SELECT,{game->GetScreenSize().x-game->GetScreenSize().x/3.f,24},windowSize); Menu*levelSelectWindow=CreateMenu(OVERWORLD_LEVEL_SELECT,{game->GetScreenSize().x-game->GetScreenSize().x/3.f,24},windowSize);
levelSelectWindow->ADD("Panel 1 Back",MenuLabel)({{0,0},{windowSize.x-1,44}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END; levelSelectWindow->ADD("Panel 1 Back",MenuLabel)(geom2d::rect<float>{{0,0},{windowSize.x-1,44}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END;
levelSelectWindow->ADD("Chapter Label",MenuLabel)({{0,4},{windowSize.x,16}},"Chapter",1,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)DEPTH -1 END; levelSelectWindow->ADD("Chapter Label",MenuLabel)(geom2d::rect<float>{{0,4},{windowSize.x,16}},"Chapter",1,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)DEPTH -1 END;
levelSelectWindow->ADD("Stage Label",MenuLabel)({{0,24},{windowSize.x,16}},"Stage",1,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)DEPTH -1 END; levelSelectWindow->ADD("Stage Label",MenuLabel)(geom2d::rect<float>{{0,24},{windowSize.x,16}},"Stage",1,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)DEPTH -1 END;
levelSelectWindow->ADD("Panel 2 Back",MenuLabel)({{0,52},{windowSize.x-1,96}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END; levelSelectWindow->ADD("Panel 2 Back",MenuLabel)(geom2d::rect<float>{{0,52},{windowSize.x-1,96}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END;
levelSelectWindow->ADD("Encounters Label",MenuLabel)({{0,52},{windowSize.x-1,12}},"Encounters:",1,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END; levelSelectWindow->ADD("Encounters Label",MenuLabel)(geom2d::rect<float>{{0,52},{windowSize.x-1,12}},"Encounters:",1,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END;
levelSelectWindow->ADD("Spawns List",EncountersSpawnListScrollableWindowComponent)({{1,64},{windowSize.x-2,84}},ComponentAttr::BACKGROUND)END; levelSelectWindow->ADD("Spawns List",EncountersSpawnListScrollableWindowComponent)(geom2d::rect<float>{{1,64},{windowSize.x-2,84}},ComponentAttr::BACKGROUND)END;
levelSelectWindow->ADD("Change Loadout Button",MenuComponent)({{0,152},{windowSize.x-1,12}},"Change Loadout",ITEM_LOADOUT,[](MenuFuncData data){return true;})END; levelSelectWindow->ADD("Change Loadout Button",MenuComponent)(geom2d::rect<float>{{0,152},{windowSize.x-1,12}},"Change Loadout",ITEM_LOADOUT,[](MenuFuncData data){return true;})END;
levelSelectWindow->ADD("Enter Button",MenuComponent)({{0,166},{windowSize.x-1,16}},"Enter",[](MenuFuncData data){State_OverworldMap::StartLevel();return true;})END; levelSelectWindow->ADD("Enter Button",MenuComponent)(geom2d::rect<float>{{0,166},{windowSize.x-1,16}},"Enter",[](MenuFuncData data){State_OverworldMap::StartLevel();return true;})END;
} }

@ -48,16 +48,16 @@ INCLUDE_GFX
void Menu::InitializeOverworldMenuWindow(){ void Menu::InitializeOverworldMenuWindow(){
Menu*overworldMenuWindow=CreateMenu(OVERWORLD_MENU,CENTERED,vi2d{96,164}); Menu*overworldMenuWindow=CreateMenu(OVERWORLD_MENU,CENTERED,vi2d{96,164});
overworldMenuWindow->ADD("Resume Button",MenuComponent)({{4,12+28*0},{88,24}},"Resume",[](MenuFuncData data){Menu::CloseMenu();return true;})END; overworldMenuWindow->ADD("Resume Button",MenuComponent)(geom2d::rect<float>{{4,12+28*0},{88,24}},"Resume",[](MenuFuncData data){Menu::CloseMenu();return true;})END;
overworldMenuWindow->ADD("Character Button",MenuComponent)({{4,12+28*1},{88,24}},"Character", overworldMenuWindow->ADD("Character Button",MenuComponent)(geom2d::rect<float>{{4,12+28*1},{88,24}},"Character",
[](MenuFuncData data){ [](MenuFuncData data){
Component<CharacterRotatingDisplay>(CHARACTER_MENU,"Character Rotating Display")->SetIcon(GFX[classutils::GetClassInfo(game->GetPlayer()->GetClassName()).classFullImgName].Decal()); Component<CharacterRotatingDisplay>(CHARACTER_MENU,"Character Rotating Display")->SetIcon(GFX[classutils::GetClassInfo(game->GetPlayer()->GetClassName()).classFullImgName].Decal());
Menu::OpenMenu(CHARACTER_MENU); Menu::OpenMenu(CHARACTER_MENU);
return true; return true;
})END; })END;
overworldMenuWindow->ADD("Inventory Button",MenuComponent)({{4,12+28*2},{88,24}},"Inventory",[](MenuFuncData data){Menu::OpenMenu(INVENTORY);return true;})END; overworldMenuWindow->ADD("Inventory Button",MenuComponent)(geom2d::rect<float>{{4,12+28*2},{88,24}},"Inventory",[](MenuFuncData data){Menu::OpenMenu(INVENTORY);return true;})END;
overworldMenuWindow->ADD("Settings Button",MenuComponent)({{4,12+28*3},{88,24}},"Settings",[](MenuFuncData data){/*Menu::OpenMenu(SETTINGS_MENU);*/return true;})END; overworldMenuWindow->ADD("Settings Button",MenuComponent)(geom2d::rect<float>{{4,12+28*3},{88,24}},"Settings",[](MenuFuncData data){/*Menu::OpenMenu(SETTINGS_MENU);*/return true;})END;
overworldMenuWindow->ADD("Quit Button",MenuComponent)({{4,12+28*4},{88,24}},"Quit Game",[](MenuFuncData data){ overworldMenuWindow->ADD("Quit Button",MenuComponent)(geom2d::rect<float>{{4,12+28*4},{88,24}},"Quit Game",[](MenuFuncData data){
Menu::CloseAllMenus(); Menu::CloseAllMenus();
SaveFile::SaveGame(); SaveFile::SaveGame();
game->ResetGame(); game->ResetGame();

@ -73,7 +73,7 @@ InputGroup Player::KEY_ITEM1;
InputGroup Player::KEY_ITEM2; InputGroup Player::KEY_ITEM2;
InputGroup Player::KEY_ITEM3; InputGroup Player::KEY_ITEM3;
std::set<MenuComponent*>Player::moneyListeners; std::vector<std::weak_ptr<MenuComponent>>Player::moneyListeners;
Player::Player() Player::Player()
:lastReleasedMovementKey(DOWN),facingDirection(DOWN),state(State::NORMAL){ :lastReleasedMovementKey(DOWN),facingDirection(DOWN),state(State::NORMAL){
@ -1020,8 +1020,8 @@ void EntityStats::RecalculateEquipStats(){
equipStats.A(key)+=setStats.A_Read(key); equipStats.A(key)+=setStats.A_Read(key);
} }
} }
for(MenuComponent*component:Menu::equipStatListeners){ for(std::weak_ptr<MenuComponent>component:Menu::equipStatListeners){
component->OnEquipStatsUpdate(); component.lock()->OnEquipStatsUpdate();
} }
} }
@ -1065,12 +1065,14 @@ uint32_t Player::GetMoney()const{
void Player::SetMoney(uint32_t newMoney){ void Player::SetMoney(uint32_t newMoney){
money=newMoney; money=newMoney;
for(auto&component:moneyListeners){ for(auto&component:moneyListeners){
component->OnPlayerMoneyUpdate(newMoney); component.lock()->OnPlayerMoneyUpdate(newMoney);
} }
} }
void Player::AddMoneyListener(MenuComponent*component){ void Player::AddMoneyListener(std::weak_ptr<MenuComponent>component){
if(moneyListeners.count(component))ERR("WARNING! Trying to add a second copy of component "<<std::quoted(component->GetName())); if(std::find_if(moneyListeners.begin(),moneyListeners.end(),[&](auto&ptr){return &*ptr.lock()==&*component.lock();})!=moneyListeners.end()){
moneyListeners.insert(component); ERR("WARNING! Component "<<component.lock()->GetName()<<" has already been added to the Chapter listener list! There should not be any duplicates!!")
}
moneyListeners.push_back(component);
} }

@ -217,9 +217,9 @@ public:
void PerformHPRecovery(); void PerformHPRecovery();
static InputGroup KEY_ABILITY1, KEY_ABILITY2, KEY_ABILITY3, KEY_ABILITY4, KEY_DEFENSIVE, KEY_ITEM1, KEY_ITEM2, KEY_ITEM3; static InputGroup KEY_ABILITY1, KEY_ABILITY2, KEY_ABILITY3, KEY_ABILITY4, KEY_DEFENSIVE, KEY_ITEM1, KEY_ITEM2, KEY_ITEM3;
static std::set<MenuComponent*>moneyListeners; static std::vector<std::weak_ptr<MenuComponent>>moneyListeners;
static void AddMoneyListener(MenuComponent*component); static void AddMoneyListener(std::weak_ptr<MenuComponent>component);
uint32_t GetMoney()const; uint32_t GetMoney()const;
void SetMoney(uint32_t newMoney); void SetMoney(uint32_t newMoney);
void AddXP(const uint32_t xpGain); void AddXP(const uint32_t xpGain);

@ -30,7 +30,7 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE. SUCH DAMAGE.
Portions of this software are copyright © 2023 The FreeType Portions of this software are copyright <EFBFBD> 2023 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information. Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved. All rights reserved.
*/ */
@ -51,17 +51,17 @@ public:
virtual inline void SetCompactDescriptions(CompactText compact)override final{ virtual inline void SetCompactDescriptions(CompactText compact)override final{
this->compact=compact; this->compact=compact;
for(MenuComponent*component:components){ for(std::weak_ptr<MenuComponent>component:components){
RowItemDisplay*itemButton=DYNAMIC_CAST<RowItemDisplay*>(component); std::weak_ptr<RowItemDisplay>itemButton=DYNAMIC_POINTER_CAST<RowItemDisplay>(component.lock());
itemButton->SetCompactDescriptions(compact); itemButton.lock()->SetCompactDescriptions(compact);
} }
} }
virtual inline void SetPriceLabelType(PriceLabel::PriceLabel labelType)final{ virtual inline void SetPriceLabelType(PriceLabel::PriceLabel labelType)final{
this->priceLabel=labelType; this->priceLabel=labelType;
for(MenuComponent*component:components){ for(std::weak_ptr<MenuComponent>component:components){
RowItemDisplay*itemButton=DYNAMIC_CAST<RowItemDisplay*>(component); std::weak_ptr<RowItemDisplay>itemButton=DYNAMIC_POINTER_CAST<RowItemDisplay>(component.lock());
itemButton->SetPriceLabelType(labelType); itemButton.lock()->SetPriceLabelType(labelType);
} }
} }
}; };

@ -223,9 +223,9 @@ const void SaveFile::UpdateSaveGameData(){
float offsetY=0; float offsetY=0;
for(size_t i=0;i<saveFileCount;i++){ for(size_t i=0;i<saveFileCount;i++){
if(metadata.HasProperty(std::format("save{}",i))){ if(metadata.HasProperty(std::format("save{}",i))){
gameFilesList->ADD(std::format("Load File Button - Save {}",i),LoadFileButton)({{0,offsetY},{gameFilesList->GetSize().x-13,48}},metadata[std::format("save{}",i)],i,[](MenuFuncData data){ gameFilesList->ADD(std::format("Load File Button - Save {}",i),LoadFileButton)(geom2d::rect<float>{{0,offsetY},{gameFilesList->GetSize().x-13,48}},metadata[std::format("save{}",i)],i,[](MenuFuncData data){
LoadFileButton*comp=DYNAMIC_CAST<LoadFileButton*>(data.component); std::weak_ptr<LoadFileButton>comp=DYNAMIC_POINTER_CAST<LoadFileButton>(data.component.lock());
saveFileID=comp->getSaveFileID(); saveFileID=comp.lock()->getSaveFileID();
SaveFile::LoadGame(); SaveFile::LoadGame();
return true; return true;
},ButtonAttr::NONE)END; },ButtonAttr::NONE)END;

@ -42,10 +42,10 @@ All rights reserved.
void Menu::InitializeSaveFileWindow(){ void Menu::InitializeSaveFileWindow(){
Menu*saveFileWindow=CreateMenu(SAVE_FILE_NAME,CENTERED,vi2d{96,96}); Menu*saveFileWindow=CreateMenu(SAVE_FILE_NAME,CENTERED,vi2d{96,96});
saveFileWindow->ADD("Save File Name Entry Label",MenuLabel)({{-8,0},{112,36}},"Save File Name:",1.0f,ComponentAttr::SHADOW)END; saveFileWindow->ADD("Save File Name Entry Label",MenuLabel)(geom2d::rect<float>{{-8,0},{112,36}},"Save File Name:",1.0f,ComponentAttr::SHADOW)END;
saveFileWindow->ADD("Save File Name Text Entry",TextEntryLabel)({{-8,36},{112,24}},TEXTCHANGE_DONOTHING,false,16U,2.f,ComponentAttr::FIT_TO_LABEL|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::BACKGROUND)END; saveFileWindow->ADD("Save File Name Text Entry",TextEntryLabel)(geom2d::rect<float>{{-8,36},{112,24}},TEXTCHANGE_DONOTHING,false,16U,2.f,ComponentAttr::FIT_TO_LABEL|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::BACKGROUND)END;
saveFileWindow->ADD("Back Button",MenuComponent)({{-8,68},{48,12}},"Back",[](MenuFuncData data){Menu::CloseMenu();game->TextEntryEnable(false);return true;})END; saveFileWindow->ADD("Back Button",MenuComponent)(geom2d::rect<float>{{-8,68},{48,12}},"Back",[](MenuFuncData data){Menu::CloseMenu();game->TextEntryEnable(false);return true;})END;
saveFileWindow->ADD("Continue Button",MenuComponent)({{56,68},{48,12}},"Begin",MenuType::CLASS_SELECTION,[](MenuFuncData data){ saveFileWindow->ADD("Continue Button",MenuComponent)(geom2d::rect<float>{{56,68},{48,12}},"Begin",MenuType::CLASS_SELECTION,[](MenuFuncData data){
SaveFile::SetSaveFileName(game->TextEntryGetString()); SaveFile::SetSaveFileName(game->TextEntryGetString());
SaveFile::SetSaveFileID(SaveFile::GetSaveFileCount()); SaveFile::SetSaveFileID(SaveFile::GetSaveFileCount());
game->TextEntryEnable(false); game->TextEntryEnable(false);

@ -46,9 +46,9 @@ using A=Attribute;
class ScrollableWindowComponent:public MenuComponent{ class ScrollableWindowComponent:public MenuComponent{
protected: protected:
ViewPort subWindow; ViewPort subWindow;
std::vector<MenuComponent*>components; std::vector<std::weak_ptr<MenuComponent>>components;
MenuComponent*upButton=nullptr; std::weak_ptr<MenuComponent>upButton;
MenuComponent*downButton=nullptr; std::weak_ptr<MenuComponent>downButton;
geom2d::rect<float>bounds; //It's for the scrollbar. geom2d::rect<float>bounds; //It's for the scrollbar.
float scrollBarHeight=0; float scrollBarHeight=0;
float scrollBarTop=0; float scrollBarTop=0;
@ -56,8 +56,8 @@ protected:
float scrollBarHoverTime=0; float scrollBarHoverTime=0;
vf2d scrollOffset; vf2d scrollOffset;
protected: protected:
inline bool OnScreen(MenuComponent*component){ inline bool OnScreen(std::weak_ptr<MenuComponent>component){
return geom2d::overlaps(geom2d::rect<float>{{},rect.size},geom2d::rect<float>{component->rect.pos+vf2d{2,2},component->rect.size-vf2d{2,2}}); return geom2d::overlaps(geom2d::rect<float>{{},rect.size},geom2d::rect<float>{component.lock()->rect.pos+vf2d{2,2},component.lock()->rect.size-vf2d{2,2}});
} }
public: public:
inline ScrollableWindowComponent(geom2d::rect<float>rect,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) inline ScrollableWindowComponent(geom2d::rect<float>rect,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)
@ -70,37 +70,25 @@ public:
RemoveButton(components.back()); RemoveButton(components.back());
} }
} }
virtual inline void RemoveButton(MenuComponent*button){ virtual inline void RemoveButton(std::weak_ptr<MenuComponent>button){
std::vector<MenuComponent*>&buttonList=Menu::menus[button->parentMenu]->buttons.at(int(button->originalPos.y)); auto componentSearchResults=std::find_if(components.begin(),components.end(),[&](std::weak_ptr<MenuComponent>ptr){return &*ptr.lock()==&*button.lock();});
std::vector<MenuComponent*>&keyboardButtonList=Menu::menus[button->parentMenu]->keyboardButtons.at(int(button->originalPos.y)); if(componentSearchResults==components.end())ERR("Could not find Component"<<std::quoted(button.lock()->GetName())<<" inside the component list!");
components.erase(componentSearchResults);
size_t removedCount=0; size_t removedCount=0;
removedCount+=std::erase(buttonList,button);
removedCount+=std::erase(keyboardButtonList,button); MenuType parentMenu=button.lock()->parentMenu;
removedCount+=Menu::menus[button->parentMenu]->components.erase(button->GetName());
if(removedCount!=3){ removedCount+=Menu::menus[parentMenu]->components.erase(button.lock()->GetName());
if(removedCount!=1){
std::cout<<"WARNING! Attempted to remove buttons from button listing, but not found!"; std::cout<<"WARNING! Attempted to remove buttons from button listing, but not found!";
} }
if(buttonList.size()==0){ Menu::menus[parentMenu]->RecalculateComponentCount();
if(!Menu::menus[button->parentMenu]->buttons.erase(int(button->originalPos.y))){
ERR("WARNING! Attempted to erase key "<<button->originalPos.y<<" from button map, but the list still exists!")
}
}
if(keyboardButtonList.size()==0){
if(!Menu::menus[button->parentMenu]->keyboardButtons.erase(int(button->originalPos.y))){
ERR("WARNING! Attempted to erase key "<<button->originalPos.y<<" from button map, but the list still exists!")
}
}
auto componentSearchResults=std::find(components.begin(),components.end(),button);
if(componentSearchResults==components.end())ERR("Could not find Component"<<std::quoted(button->GetName())<<" inside the component list!");
components.erase(componentSearchResults);
Menu::menus[button->parentMenu]->RecalculateComponentCount();
delete button;
CalculateBounds(); CalculateBounds();
} }
virtual inline void SetScrollAmount(vf2d scrollOffset){ virtual inline void SetScrollAmount(vf2d scrollOffset){
this->scrollOffset=scrollOffset; this->scrollOffset=scrollOffset;
for(MenuComponent*component:components){ for(std::weak_ptr<MenuComponent>component:components){
component->rect.pos=component->originalPos+scrollOffset; component.lock()->rect.pos=component.lock()->originalPos+scrollOffset;
} }
} }
virtual inline vf2d GetScrollAmount(){ virtual inline vf2d GetScrollAmount(){
@ -109,16 +97,16 @@ public:
protected: protected:
virtual inline void AfterCreate()override{ virtual inline void AfterCreate()override{
//Let's use the internal name of this component to add unique names for sub-components. //Let's use the internal name of this component to add unique names for sub-components.
upButton=Menu::menus[parentMenu]->ADD(name+vf2d(rect.pos+vf2d{rect.size.x-12,0}).str()+"_"+vf2d(12,12).str(),MenuComponent)({rect.pos+vf2d{rect.size.x-12,0},{12,12}},"^",[&](MenuFuncData dat){SetScrollAmount(GetScrollAmount()+vf2d{0,"ThemeGlobal.MenuButtonScrollSpeed"_F});return true;},ButtonAttr::UNSELECTABLE_VIA_KEYBOARD)DEPTH depth-1 END; upButton=Menu::menus[parentMenu]->ADD(name+vf2d(rect.pos+vf2d{rect.size.x-12,0}).str()+"_"+vf2d(12,12).str(),MenuComponent)(geom2d::rect<float>{rect.pos+vf2d{rect.size.x-12,0},{12,12}},"^",[&](MenuFuncData dat){SetScrollAmount(GetScrollAmount()+vf2d{0,"ThemeGlobal.MenuButtonScrollSpeed"_F});return true;},ButtonAttr::UNSELECTABLE_VIA_KEYBOARD)DEPTH depth-1 END;
downButton=Menu::menus[parentMenu]->ADD(name+vf2d(rect.pos+rect.size-vf2d{12,12}).str()+"_"+vf2d(12,12).str(),MenuComponent)({rect.pos+rect.size-vf2d{12,12},{12,12}},"v",[&](MenuFuncData dat){SetScrollAmount(GetScrollAmount()-vf2d{0,"ThemeGlobal.MenuButtonScrollSpeed"_F});return true;},ButtonAttr::UNSELECTABLE_VIA_KEYBOARD)DEPTH depth-1 END; downButton=Menu::menus[parentMenu]->ADD(name+vf2d(rect.pos+rect.size-vf2d{12,12}).str()+"_"+vf2d(12,12).str(),MenuComponent)(geom2d::rect<float>{rect.pos+rect.size-vf2d{12,12},{12,12}},"v",[&](MenuFuncData dat){SetScrollAmount(GetScrollAmount()-vf2d{0,"ThemeGlobal.MenuButtonScrollSpeed"_F});return true;},ButtonAttr::UNSELECTABLE_VIA_KEYBOARD)DEPTH depth-1 END;
subWindow=ViewPort::rectViewPort({},rect.size,Menu::menus[parentMenu]->pos+rect.pos); subWindow=ViewPort::rectViewPort({},rect.size,Menu::menus[parentMenu]->pos+rect.pos);
if(upButton){upButton->Enable(!disabled);} if(!upButton.expired()){upButton.lock()->Enable(!disabled);}
if(downButton){downButton->Enable(!disabled);} if(!downButton.expired()){downButton.lock()->Enable(!disabled);}
} }
virtual inline void BeforeUpdate(AiL*game)override{ virtual inline void BeforeUpdate(AiL*game)override{
MenuComponent::BeforeUpdate(game); MenuComponent::BeforeUpdate(game);
for(MenuComponent*component:components){ for(std::weak_ptr<MenuComponent>component:components){
component->_BeforeUpdate(game); component.lock()->_BeforeUpdate(game);
} }
} }
virtual inline void Update(AiL*game)override{ virtual inline void Update(AiL*game)override{
@ -165,17 +153,17 @@ protected:
SetScrollAmount({GetScrollAmount().x,0}); SetScrollAmount({GetScrollAmount().x,0});
} }
std::sort(components.begin(),components.end(),[](MenuComponent*c1,MenuComponent*c2){return c1->depth>c2->depth;}); std::sort(components.begin(),components.end(),[](std::weak_ptr<MenuComponent>c1,std::weak_ptr<MenuComponent>c2){return c1.lock()->depth>c2.lock()->depth;});
for(MenuComponent*component:components){ for(std::weak_ptr<MenuComponent>component:components){
component->disabled=!OnScreen(component); component.lock()->disabled=!OnScreen(component.lock());
component->_Update(game); component.lock()->_Update(game);
} }
upButton->disabled=false; upButton.lock()->disabled=false;
downButton->disabled=false; downButton.lock()->disabled=false;
if(geom2d::contains(rect,bounds)){//This means we have no reason to show a scrollbar. if(geom2d::contains(rect,bounds)){//This means we have no reason to show a scrollbar.
upButton->disabled=true; upButton.lock()->disabled=true;
downButton->disabled=true; downButton.lock()->disabled=true;
} }
} }
inline void DrawScrollbar(ViewPort&window,vf2d parentPos,bool focused){ inline void DrawScrollbar(ViewPort&window,vf2d parentPos,bool focused){
@ -198,8 +186,8 @@ protected:
if(border){ if(border){
window.DrawRectDecal(rect.pos,rect.size); window.DrawRectDecal(rect.pos,rect.size);
} }
for(MenuComponent*component:components){ for(std::weak_ptr<MenuComponent>component:components){
component->_DrawDecal(subWindow,focused); component.lock()->_DrawDecal(subWindow,focused);
} }
if(!geom2d::contains(rect,bounds)){ if(!geom2d::contains(rect,bounds)){
DrawScrollbar(window,{},focused); DrawScrollbar(window,{},focused);
@ -212,33 +200,33 @@ protected:
//Calculates the bounds of all components. //Calculates the bounds of all components.
inline void CalculateBounds(){ inline void CalculateBounds(){
bounds={}; bounds={};
for(MenuComponent*component:components){ for(std::weak_ptr<MenuComponent>component:components){
if(component->rect.pos.x<bounds.pos.x){ if(component.lock()->rect.pos.x<bounds.pos.x){
float sizeIncrease=bounds.pos.x-component->rect.pos.x; float sizeIncrease=bounds.pos.x-component.lock()->rect.pos.x;
bounds.size.x+=sizeIncrease; bounds.size.x+=sizeIncrease;
bounds.pos.x=component->rect.pos.x; bounds.pos.x=component.lock()->rect.pos.x;
} }
if(component->rect.right().start.x>bounds.right().start.x){ if(component.lock()->rect.right().start.x>bounds.right().start.x){
float sizeIncrease=component->rect.right().start.x-bounds.right().start.x; float sizeIncrease=component.lock()->rect.right().start.x-bounds.right().start.x;
bounds.size.x+=sizeIncrease; bounds.size.x+=sizeIncrease;
} }
if(component->rect.pos.y<bounds.pos.y){ if(component.lock()->rect.pos.y<bounds.pos.y){
float sizeIncrease=bounds.pos.y-component->rect.pos.y; float sizeIncrease=bounds.pos.y-component.lock()->rect.pos.y;
bounds.size.y+=sizeIncrease; bounds.size.y+=sizeIncrease;
bounds.pos.y=component->rect.pos.y; bounds.pos.y=component.lock()->rect.pos.y;
} }
if(component->rect.bottom().start.y>bounds.bottom().start.y){ if(component.lock()->rect.bottom().start.y>bounds.bottom().start.y){
float sizeIncrease=component->rect.bottom().start.y-bounds.bottom().start.y; float sizeIncrease=component.lock()->rect.bottom().start.y-bounds.bottom().start.y;
bounds.size.y+=sizeIncrease; bounds.size.y+=sizeIncrease;
} }
} }
} }
public: public:
template<class T> template<class T>
T* _AddComponent(std::string key,T*button){ std::shared_ptr<T> _AddComponent(std::string key,std::shared_ptr<T>button){
components.push_back(button); components.push_back(button);
button->renderInMain=false; //Now we are in control! button->renderInMain=false; //Now we are in control!
button->parentComponent=this; button->parentComponent=Menu::menus[parentMenu]->components[this->GetName()];
button->disabled=disabled; button->disabled=disabled;
CalculateBounds(); CalculateBounds();
@ -252,21 +240,21 @@ public:
virtual inline bool PointWithinParent(MenuComponent*child,geom2d::rect<float> drawRect)override{ virtual inline bool PointWithinParent(MenuComponent*child,geom2d::rect<float> drawRect)override{
return geom2d::overlaps(geom2d::rect<float>{Menu::menus[parentMenu]->pos+rect.pos,rect.size},drawRect); return geom2d::overlaps(geom2d::rect<float>{Menu::menus[parentMenu]->pos+rect.pos,rect.size},drawRect);
} }
virtual inline bool HandleOutsideDisabledButtonSelection(MenuComponent*disabledButton)override{ virtual inline bool HandleOutsideDisabledButtonSelection(std::weak_ptr<MenuComponent>disabledButton)override{
//Set the offset so the center is highlighted by this button. //Set the offset so the center is highlighted by this button.
SetScrollAmount(vf2d{GetScrollAmount().x,-disabledButton->rect.pos.y+disabledButton->rect.size.y}); SetScrollAmount(vf2d{GetScrollAmount().x,-disabledButton.lock()->rect.pos.y+disabledButton.lock()->rect.size.y});
return true; return true;
}; };
virtual void Cleanup()override{} virtual void Cleanup()override{}
inline std::vector<MenuComponent*>&GetComponents(){ inline std::vector<std::weak_ptr<MenuComponent>>&GetComponents(){
return components; return components;
} }
virtual inline void Enable(bool enabled)override final{ virtual inline void Enable(bool enabled)override final{
disabled=!enabled; disabled=!enabled;
for(MenuComponent*component:components){ for(std::weak_ptr<MenuComponent>component:components){
component->Enable(enabled); component.lock()->Enable(enabled);
} }
if(upButton){upButton->Enable(enabled);} if(upButton.lock()){upButton.lock()->Enable(enabled);}
if(downButton){downButton->Enable(enabled);} if(downButton.lock()){downButton.lock()->Enable(enabled);}
}; };
}; };

@ -62,34 +62,34 @@ void Menu::InitializeSellItemWindow(){
Component<MenuComponent>(SELL_ITEM,"Sell Button")->SetGrayedOut(!canSell); Component<MenuComponent>(SELL_ITEM,"Sell Button")->SetGrayedOut(!canSell);
}; };
sellItemWindow->ADD("Item Sell Header",ItemMenuLabel)({{2,2},{188,12}},"Selling {}",Item::BLANK,1,ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND|ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END; sellItemWindow->ADD("Item Sell Header",ItemMenuLabel)(geom2d::rect<float>{{2,2},{188,12}},"Selling {}",Item::BLANK,1,ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND|ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END;
sellItemWindow->ADD("Price Per Item Label",MenuLabel)({{4,18},{188,12}},"Price Per Item",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; sellItemWindow->ADD("Price Per Item Label",MenuLabel)(geom2d::rect<float>{{4,18},{188,12}},"Price Per Item",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
sellItemWindow->ADD("Amount to Sell Label",MenuLabel)({{4,34},{188,12}},"Amount to Sell",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; sellItemWindow->ADD("Amount to Sell Label",MenuLabel)(geom2d::rect<float>{{4,34},{188,12}},"Amount to Sell",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
sellItemWindow->ADD("Price Label",MenuLabel)({{4,50},{188,12}},"Total Price",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; sellItemWindow->ADD("Price Label",MenuLabel)(geom2d::rect<float>{{4,50},{188,12}},"Total Price",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
sellItemWindow->ADD("Price per item Amount Label",MenuLabel)({{sellItemWindow->size.x/2+28,18},{72,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END; sellItemWindow->ADD("Price per item Amount Label",MenuLabel)(geom2d::rect<float>{{sellItemWindow->size.x/2+28,18},{72,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END;
sellItemWindow->ADD("Amount to sell Amount Label",MenuLabel)({{sellItemWindow->size.x/2+48,34},{32,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::FIT_TO_LABEL)END; sellItemWindow->ADD("Amount to sell Amount Label",MenuLabel)(geom2d::rect<float>{{sellItemWindow->size.x/2+48,34},{32,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::FIT_TO_LABEL)END;
sellItemWindow->ADD("Increase sell amount Button",MenuComponent)({{sellItemWindow->size.x/2+80+2,34},{12,12}},"+",[&](MenuFuncData data){ sellItemWindow->ADD("Increase sell amount Button",MenuComponent)(geom2d::rect<float>{{sellItemWindow->size.x/2+80+2,34},{12,12}},"+",[&](MenuFuncData data){
UpdateMenu(GetQuantity()+1); UpdateMenu(GetQuantity()+1);
return true; return true;
})END; })END;
sellItemWindow->ADD("Decrease sell amount Button",MenuComponent)({{sellItemWindow->size.x/2+48-14,34},{12,12}},"-",[](MenuFuncData data){ sellItemWindow->ADD("Decrease sell amount Button",MenuComponent)(geom2d::rect<float>{{sellItemWindow->size.x/2+48-14,34},{12,12}},"-",[](MenuFuncData data){
UpdateMenu(GetQuantity()-1); UpdateMenu(GetQuantity()-1);
return true; return true;
})END; })END;
sellItemWindow->ADD("Total Price Amount Label",MenuLabel)({{sellItemWindow->size.x/2+28,50},{72,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END; sellItemWindow->ADD("Total Price Amount Label",MenuLabel)(geom2d::rect<float>{{sellItemWindow->size.x/2+28,50},{72,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END;
sellItemWindow->ADD("Sell Button",MenuComponent)({{sellItemWindow->size.x/2+18,70},{64,12}},"Sell",[&](MenuFuncData data){ sellItemWindow->ADD("Sell Button",MenuComponent)(geom2d::rect<float>{{sellItemWindow->size.x/2+18,70},{64,12}},"Sell",[&](MenuFuncData data){
Merchant&merchant=Merchant::GetCurrentTravelingMerchant(); Merchant&merchant=Merchant::GetCurrentTravelingMerchant();
merchant.SellItem(Component<ItemMenuLabel>(SELL_ITEM,"Item Sell Header")->GetItem(),GetQuantity()); merchant.SellItem(Component<ItemMenuLabel>(SELL_ITEM,"Item Sell Header")->GetItem(),GetQuantity());
SoundEffect::PlaySFX("Sell Item",SoundEffect::CENTERED); SoundEffect::PlaySFX("Sell Item",SoundEffect::CENTERED);
Menu::CloseMenu(); Menu::CloseMenu();
return true; return true;
})END; })END;
sellItemWindow->ADD("Cancel Button",MenuComponent)({{sellItemWindow->size.x/2-82,70},{64,12}},"Cancel",[](MenuFuncData data){ sellItemWindow->ADD("Cancel Button",MenuComponent)(geom2d::rect<float>{{sellItemWindow->size.x/2-82,70},{64,12}},"Cancel",[](MenuFuncData data){
Menu::CloseMenu(); Menu::CloseMenu();
return true; return true;
})END; })END;

@ -1,5 +1,6 @@
January 1st January 1st
=========== ===========
Fix Listeners so they do not leak! (Add a proper listener class and have all listeners inherit from it.)
The Hub / NPC Interactions The Hub / NPC Interactions
Settings Menu Settings Menu
- Any settings should be saved to the save file! - Any settings should be saved to the save file!

@ -37,15 +37,15 @@ All rights reserved.
#pragma endregion #pragma endregion
#pragma once #pragma once
class IToggleable{ class IToggleable:public std::enable_shared_from_this<IToggleable>{
friend class AiL; friend class AiL;
public: public:
inline std::vector<IToggleable*>GetToggleGroup(){ inline std::vector<std::weak_ptr<IToggleable>>GetToggleGroup(){
return toggleGroup; return toggleGroup;
} }
inline void Select(){ inline void Select(){
for(IToggleable*item:toggleGroup){ for(std::weak_ptr<IToggleable>item:toggleGroup){
item->selected=false; item.lock()->selected=false;
} }
selected=true; selected=true;
} }
@ -57,24 +57,14 @@ public:
ERR("WARNING! Toggle group for this component was set twice for some reason! THIS SHOULD NOT BE HAPPENING!") ERR("WARNING! Toggle group for this component was set twice for some reason! THIS SHOULD NOT BE HAPPENING!")
} }
toggleGroupInitialized=true; toggleGroupInitialized=true;
std::erase_if(uninitializedToggleGroupItems,[&](IToggleable*item){return this==item;});
} }
inline void SetToggleGroup(std::vector<IToggleable*>toggleGroup){ inline void SetToggleGroup(std::vector<std::weak_ptr<IToggleable>>toggleGroup){
this->toggleGroup=toggleGroup; this->toggleGroup=toggleGroup;
SetToggleGroup(); SetToggleGroup();
} }
inline IToggleable(){
uninitializedToggleGroupItems.push_back(this);
#ifdef _DEBUG
if("debug_toggleable_items"_I){
std::cout<<"\tInitialized Toggle Item Ptr: 0x"<<std::hex<<this<<std::endl;
}
#endif
}
protected: protected:
std::vector<IToggleable*>toggleGroup; std::vector<std::weak_ptr<IToggleable>>toggleGroup;
private: private:
bool selected=false; bool selected=false;
bool toggleGroupInitialized=false; bool toggleGroupInitialized=false;
inline static std::vector<IToggleable*>uninitializedToggleGroupItems;
}; };

@ -46,15 +46,15 @@ using A=Attribute;
void Menu::InitializeUserIDWindow(){ void Menu::InitializeUserIDWindow(){
Menu*userIDWindow=CreateMenu(USER_ID,CENTERED,vi2d{168,120}); Menu*userIDWindow=CreateMenu(USER_ID,CENTERED,vi2d{168,120});
userIDWindow->ADD("User ID Creation Explanation",MenuLabel)({{0,-4},{168,92}},"user_id_message"_FS+"\n\n"+"user_id_message2"_FS,1.f,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END; userIDWindow->ADD("User ID Creation Explanation",MenuLabel)(geom2d::rect<float>{{0,-4},{168,92}},"user_id_message"_FS+"\n\n"+"user_id_message2"_FS,1.f,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END;
userIDWindow->ADD("User ID Input",TextEntryLabel)({{36,94},{96,12}},[](std::string_view newLabel){ userIDWindow->ADD("User ID Input",TextEntryLabel)(geom2d::rect<float>{{36,94},{96,12}},[](std::string_view newLabel){
Component<MenuComponent>(USER_ID,"Submit Button")->SetGrayedOut(newLabel.length()==0); Component<MenuComponent>(USER_ID,"Submit Button")->SetGrayedOut(newLabel.length()==0);
},true,24U,1.f,ComponentAttr::BACKGROUND|ComponentAttr::FIT_TO_LABEL|ComponentAttr::OUTLINE|ComponentAttr::SHADOW)END; },true,24U,1.f,ComponentAttr::BACKGROUND|ComponentAttr::FIT_TO_LABEL|ComponentAttr::OUTLINE|ComponentAttr::SHADOW)END;
userIDWindow->ADD("Back Button",MenuComponent)({{18,110},{48,12}},"Back",[](MenuFuncData data){ userIDWindow->ADD("Back Button",MenuComponent)(geom2d::rect<float>{{18,110},{48,12}},"Back",[](MenuFuncData data){
Menu::CloseMenu(); Menu::CloseMenu();
return true; return true;
})END; })END;
userIDWindow->ADD("Submit Button",MenuComponent)({{102,110},{48,12}},"Submit",[](MenuFuncData data){ userIDWindow->ADD("Submit Button",MenuComponent)(geom2d::rect<float>{{102,110},{48,12}},"Submit",[](MenuFuncData data){
SaveFile::SetUserID(Component<TextEntryLabel>(USER_ID,"User ID Input")->GetLabel()); SaveFile::SetUserID(Component<TextEntryLabel>(USER_ID,"User ID Input")->GetLabel());
if(Menu::menus[MAIN_MENU]->S(A::NEXT_MENU)=="New Game"){ if(Menu::menus[MAIN_MENU]->S(A::NEXT_MENU)=="New Game"){
Menu::CloseMenu(); Menu::CloseMenu();

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 0 #define VERSION_MAJOR 0
#define VERSION_MINOR 2 #define VERSION_MINOR 2
#define VERSION_PATCH 1 #define VERSION_PATCH 1
#define VERSION_BUILD 5870 #define VERSION_BUILD 5947
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

Loading…
Cancel
Save