MenuComponentRefactor #30

Merged
sigonasr2 merged 7 commits from MenuComponentRefactor into master 11 months ago
  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. 323
      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/saves
/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",
"valarray": "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))){
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));
}
#endif
@ -2077,7 +2076,7 @@ void AiL::ChangePlayerClass(Class cl){
uint8_t levelCap=player->levelCap;
uint32_t totalXPEarned=player->totalXPEarned;
uint32_t currentLevelXP=player->currentLevelXP;
std::set<MenuComponent*>moneyListeners=Player::moneyListeners;
std::vector<std::weak_ptr<MenuComponent>>moneyListeners=Player::moneyListeners;
EntityStats previousStats=player->stats;
size_t cooldownSoundInstance=player->cooldownSoundInstance;
switch(cl){
@ -2549,12 +2548,6 @@ const MapName&AiL::GetCurrentMapName()const{
}
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){
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){
this->chapter=chapter;
for(MenuComponent*component:Menu::chapterListeners){
component->OnChapterUpdate(chapter);
for(std::weak_ptr<MenuComponent>component:Menu::chapterListeners){
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
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.
All rights reserved.
*/
@ -72,29 +72,29 @@ void Menu::InitializeBlacksmithCraftingWindow(){
});
#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<RowInventoryScrollableWindowComponent>(BLACKSMITH,"Weapon Inventory Display")->Enable(true);
Component<RowInventoryScrollableWindowComponent>(BLACKSMITH,"Armor Inventory Display")->Enable(false);
data.component->selected=true;
data.component.lock()->selected=true;
return true;
})END;
weaponTab->selected=true;
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<RowInventoryScrollableWindowComponent>(BLACKSMITH,"Weapon Inventory Display")->Enable(false);
Component<RowInventoryScrollableWindowComponent>(BLACKSMITH,"Armor Inventory Display")->Enable(true);
data.component->selected=true;
data.component.lock()->selected=true;
return true;
})END;
armorTab->selectionType=SelectionType::HIGHLIGHT;
#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){
RowItemDisplay*comp=DYNAMIC_CAST<RowItemDisplay*>(data.component);
const std::weak_ptr<Item>item=comp->GetItem();
std::weak_ptr<RowItemDisplay>comp=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
const std::weak_ptr<Item>item=comp.lock()->GetItem();
std::string label="";
if(item.lock()->EnhancementIsPossible()&&item.lock()->GetEnhancementInfo().size()>item.lock()->EnhancementLevel()+1){
@ -109,8 +109,8 @@ void Menu::InitializeBlacksmithCraftingWindow(){
return true;
},
[](MenuFuncData data){
RowItemDisplay*rowItem=DYNAMIC_CAST<RowItemDisplay*>(data.component);
Component<MenuItemItemButton>(BLACKSMITH,"Item Icon")->SetItem(rowItem->GetItem());
std::weak_ptr<RowItemDisplay>rowItem=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
Component<MenuItemItemButton>(BLACKSMITH,"Item Icon")->SetItem(rowItem.lock()->GetItem());
return true;
},
[](MenuFuncData data){
@ -118,18 +118,18 @@ void Menu::InitializeBlacksmithCraftingWindow(){
return true;
},
InventoryCreator::RowPlayerWeapons_InventoryUpdate,
{.padding=1,.size={207,28}}
InventoryWindowOptions{.padding=1,.size={207,28}}
)END;
AddInventoryListener(weaponsDisplay,"Equipment");
weaponsDisplay->SetCompactDescriptions(CRAFTING_INFO);
#pragma endregion
#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){
Menu::OpenMenu(CRAFT_ITEM);
RowItemDisplay*comp=DYNAMIC_CAST<RowItemDisplay*>(data.component);
const std::weak_ptr<Item>item=comp->GetItem();
std::weak_ptr<RowItemDisplay>comp=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
const std::weak_ptr<Item>item=comp.lock()->GetItem();
std::string label="";
if(item.lock()->EnhancementIsPossible()&&item.lock()->GetEnhancementInfo().size()>item.lock()->EnhancementLevel()+1){
@ -143,8 +143,8 @@ void Menu::InitializeBlacksmithCraftingWindow(){
return true;
},
[](MenuFuncData data){
RowItemDisplay*rowItem=DYNAMIC_CAST<RowItemDisplay*>(data.component);
Component<MenuItemItemButton>(BLACKSMITH,"Item Icon")->SetItem(rowItem->GetItem());
std::weak_ptr<RowItemDisplay>rowItem=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
Component<MenuItemItemButton>(BLACKSMITH,"Item Icon")->SetItem(rowItem.lock()->GetItem());
return true;
},
[](MenuFuncData data){
@ -152,7 +152,7 @@ void Menu::InitializeBlacksmithCraftingWindow(){
return true;
},
InventoryCreator::RowPlayerArmor_InventoryUpdate,
{.padding=1,.size={207,28}}
InventoryWindowOptions{.padding=1,.size={207,28}}
)END;
AddInventoryListener(armorDisplay,"Equipment");
armorDisplay->Enable(false);
@ -161,25 +161,25 @@ void Menu::InitializeBlacksmithCraftingWindow(){
#pragma region Inventory Description
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 Icon",MenuItemItemButton)({{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 Description Label",MenuLabel)({{226,94},{inventoryDescriptionWidth-6,blacksmithWindow->size.y-44-66}},"",0.5f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)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)(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)(geom2d::rect<float>{{226,84},{inventoryDescriptionWidth-6,12}},"",0.75f,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 region Money Display
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());
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);
Player::AddMoneyListener(moneyDisplay);
#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){
Menu::CloseMenu();
return true;
},{2,2})END;
},vf2d{2,2})END;
}

@ -62,27 +62,27 @@ void Menu::InitializeBuyItemWindow(){
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("Amount to Buy Label",MenuLabel)({{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 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)(geom2d::rect<float>{{4,34},{188,12}},"Amount to Buy",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("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("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)(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);
return true;
})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);
return true;
})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();
const std::string&item=Component<MenuLabel>(BUY_ITEM,"Item Purchase Header")->GetString(A::ITEM_NAME);
merchant.PurchaseItem(item,GetQuantity());
@ -90,7 +90,7 @@ void Menu::InitializeBuyItemWindow(){
Menu::CloseMenu();
return true;
})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();
return true;
})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
fi
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
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
cp buildtemplate.html ${PROJECT_NAME}.html
sed -i "s/_REPLACEME_/$PROJECT_NAME.js/" ${PROJECT_NAME}.html
cp buildtemplate.html "${PROJECT_NAME}.html"
sed -i "s/_REPLACEME_/$PROJECT_NAME.js/" "${PROJECT_NAME}.html"
if [[ "$1" == "headless" || "$2" == "headless" ]]; then
echo "Running as headless web server"
emrun --no_browser ${PROJECT_NAME}.html
emrun --no_browser "${PROJECT_NAME}.html"
else
emrun --serve_after_close ${PROJECT_NAME}.html
emrun --serve_after_close "${PROJECT_NAME}.html"
fi
if [ $? -eq 127 ]

@ -53,23 +53,23 @@ void Menu::InitializeClassInfoWindow(){
Menu*classSelectionWindow=Menu::menus[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 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("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("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("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)(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)(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};
classInfoWindow->ADD("Ability 1 Display",CharacterAbilityPreviewComponent)({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 3 Display",CharacterAbilityPreviewComponent)({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("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)(geom2d::rect<float>{healthDisplayLabelPos+vf2d{0,32*1}+abilityIconOffsets,labelSize*vf2d{1,2}},data.ability2)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)(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
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.
All rights reserved.
*/
@ -58,9 +58,9 @@ void Menu::InitializeCharacterMenuWindow(){
vf2d windowSize=game->GetScreenSize()-vf2d{52,52};
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("Equip Slot Outline",MenuComponent)({{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 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)(geom2d::rect<float>{{0,28},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)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{
"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);
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);
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);
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){
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Outline")->Enable(false);
Component<ScrollableWindowComponent>(data.component->parentMenu,"Equip List")->Enable(false);
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Bottom Outline")->Enable(false);
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Select Button")->Enable(false);
Component<CharacterRotatingDisplay>(data.component->parentMenu,"Character Rotating Display")->Enable(true);
Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Outline")->Enable(false);
Component<ScrollableWindowComponent>(data.component.lock()->parentMenu,"Equip List")->Enable(false);
Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Bottom Outline")->Enable(false);
Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Select Button")->Enable(false);
Component<CharacterRotatingDisplay>(data.component.lock()->parentMenu,"Character Rotating Display")->Enable(true);
for(int counter=0;const std::string&attribute:displayAttrs){
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label");
statDisplayLabel->SetStatChangeAmt(0);
std::weak_ptr<StatLabel>statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label");
statDisplayLabel.lock()->SetStatChangeAmt(0);
}
equipmentWindowOpened=false;
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"};
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){
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>>&accessories=Inventory::get("Accessories");
@ -131,40 +131,36 @@ void Menu::InitializeCharacterMenuWindow(){
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();
for(int counter=0;const std::weak_ptr<Item>it:availableEquipment){
const static auto OppositeRingSlotDoesNotMatchCurrentEquip=[](RowItemDisplay*comp){
EquipSlot slot=EquipSlot(comp->I(Attribute::EQUIP_TYPE));
const static auto OppositeRingSlotDoesNotMatchCurrentEquip=[](std::weak_ptr<RowItemDisplay>comp){
EquipSlot slot=EquipSlot(comp.lock()->I(Attribute::EQUIP_TYPE));
std::weak_ptr<Item>otherItem;
if(slot&EquipSlot::RING1)otherItem=Inventory::GetEquip(EquipSlot::RING2);
else
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){
RowItemDisplay*comp=DYNAMIC_CAST<RowItemDisplay*>(data.component);
if(comp!=nullptr){
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.
Inventory::EquipItem(comp->GetItem(),EquipSlot(comp->I(Attribute::EQUIP_TYPE)));
SoundEffect::PlaySFX(comp->GetItem().lock()->UseSound(),SoundEffect::CENTERED);
for(MenuComponent*button:((ScrollableWindowComponent*)data.parentComponent)->GetComponents()){
RowItemDisplay*comp=DYNAMIC_CAST<RowItemDisplay*>(button);
if(comp!=nullptr){
comp->SetSelected(false);
}else{
ERR("WARNING! Attempting to cast a button that isn't a RowItemDisplay!");
std::weak_ptr<RowItemDisplay>comp=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
if(!comp.expired()){
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.lock()->GetItem(),EquipSlot(comp.lock()->I(Attribute::EQUIP_TYPE)));
SoundEffect::PlaySFX(comp.lock()->GetItem().lock()->UseSound(),SoundEffect::CENTERED);
for(std::weak_ptr<MenuComponent>button:DYNAMIC_POINTER_CAST<ScrollableWindowComponent>(data.parentComponent.lock())->GetComponents()){
std::weak_ptr<RowItemDisplay>comp=DYNAMIC_POINTER_CAST<RowItemDisplay>(button.lock());
comp.lock()->SetSelected(false);
}
}
comp->SetSelected(true);
comp.lock()->SetSelected(true);
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);
}
MenuItemItemButton*equipButton=Component<MenuItemItemButton>(CHARACTER_MENU,"Equip Slot "+slotNames[data.parentComponent->I(A::INDEXED_THEME)]);
equipButton->SetItem(comp->GetItem(),false);
std::shared_ptr<MenuItemItemButton>equipButton=Component<MenuItemItemButton>(CHARACTER_MENU,"Equip Slot "+slotNames[data.parentComponent.lock()->I(A::INDEXED_THEME)]);
equipButton->SetItem(comp.lock()->GetItem(),false);
}
}else{
ERR("WARNING! Attempting to cast a button that isn't a RowItemDisplay!");
@ -174,11 +170,11 @@ void Menu::InitializeCharacterMenuWindow(){
equip->SetHoverFunc(
[&](MenuFuncData data){
RowItemDisplay*button=DYNAMIC_CAST<RowItemDisplay*>(data.component);
if(button!=nullptr){
const std::weak_ptr<Item>buttonItem=button->GetItem();
std::weak_ptr<RowItemDisplay>button=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
if(!button.expired()){
const std::weak_ptr<Item>buttonItem=button.lock()->GetItem();
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){
statsBeforeEquip.push_back(game->GetPlayer()->GetStat(attribute));
}
@ -188,12 +184,12 @@ void Menu::InitializeCharacterMenuWindow(){
if(slot==EquipSlot::RING1)otherItem=Inventory::GetEquip(EquipSlot::RING2);
else
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);
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];
statDisplayLabel->SetStatChangeAmt(statChangeAmt);
statDisplayLabel.lock()->SetStatChangeAmt(statChangeAmt);
counter++;
}
Inventory::UnequipItem(slot);
@ -214,8 +210,8 @@ void Menu::InitializeCharacterMenuWindow(){
equip->SetMouseOutFunc(
[](MenuFuncData data){
for(int counter=0;const std::string&attribute:displayAttrs){
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label");
statDisplayLabel->SetStatChangeAmt(0);
std::weak_ptr<StatLabel>statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label");
statDisplayLabel.lock()->SetStatChangeAmt(0);
counter++;
}
return true;
@ -232,27 +228,27 @@ void Menu::InitializeCharacterMenuWindow(){
counter++;
}
equipList->I(Attribute::INDEXED_THEME)=data.component->I(Attribute::INDEXED_THEME);
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Outline")->Enable(true);
equipList->I(Attribute::INDEXED_THEME)=data.component.lock()->I(Attribute::INDEXED_THEME);
Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Outline")->Enable(true);
equipList->Enable(true);
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Bottom Outline")->Enable(true);
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Select Button")->Enable(true);
Component<CharacterRotatingDisplay>(data.component->parentMenu,"Character Rotating Display")->Enable(false);
Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Bottom Outline")->Enable(true);
Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Select Button")->Enable(true);
Component<CharacterRotatingDisplay>(data.component.lock()->parentMenu,"Character Rotating Display")->Enable(false);
equipmentWindowOpened=true;
return true;
},[](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);
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;
},[](MenuFuncData data){//On Mouse Out
if(!equipmentWindowOpened){
Component<MenuLabel>(data.component->parentMenu,"Item Equip Description")->SetLabel("");
Component<MenuLabel>(data.component->parentMenu,"Item Equip Name")->Enable(false);
Component<MenuLabel>(data.component->parentMenu,"Item Equip Description")->Enable(false);
Component<CharacterRotatingDisplay>(data.component->parentMenu,"Character Rotating Display")->Enable(true);
Component<MenuLabel>(data.component.lock()->parentMenu,"Item Equip Description")->SetLabel("");
Component<MenuLabel>(data.component.lock()->parentMenu,"Item Equip Name")->Enable(false);
Component<MenuLabel>(data.component.lock()->parentMenu,"Item Equip Description")->Enable(false);
Component<CharacterRotatingDisplay>(data.component.lock()->parentMenu,"Character Rotating Display")->Enable(true);
}
return true;
},"Item Equip Name","Item Equip Description")END;
@ -262,26 +258,26 @@ void Menu::InitializeCharacterMenuWindow(){
equipmentSlot->SetShowQuantity(false);
equipmentSlot->SetCompactDescriptions(false);
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);
}
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;
for(const std::string&attribute:displayAttrs){
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);
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 itemDescriptionDisplay=characterMenuWindow->ADD("Item Description",MenuLabel)({{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 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 itemNameDisplay=characterMenuWindow->ADD("Item Name",MenuLabel)(geom2d::rect<float>{{0,28},{120,12}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END;
auto itemDescriptionDisplay=characterMenuWindow->ADD("Item Description",MenuLabel)(geom2d::rect<float>{{0,40},{120,windowSize.y-49}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW)END;
auto 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)(geom2d::rect<float>{{123,40},{120,windowSize.y-49}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW)END;
itemNameDisplay->Enable(false);
itemDescriptionDisplay->Enable(false);

@ -53,16 +53,16 @@ void Menu::InitializeClassSelectionWindow(){
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};
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){
std::string selectedClass=data.component->S(A::CLASS_SELECTION);
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.lock()->S(A::CLASS_SELECTION);
data.game->ChangePlayerClass(classutils::StringToClass(selectedClass));
GameState::ChangeState(States::OVERWORLD_MAP);
return true;
@ -93,7 +93,7 @@ void Menu::InitializeClassSelectionWindow(){
Witch::walk_s,
};
std::vector<IToggleable*>toggleGroup;
std::vector<std::weak_ptr<IToggleable>>toggleGroup;
for(int i=0;i<6;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.
classSelectionWindow->ADD(className+" Background",MenuLabel)({backgroundOffsetPos,backgroundSize},"",1,ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
classSelectionWindow->ADD(className+" Button",MenuComponent)({offsetPos,buttonSize},"Info",CLASS_INFO,
classSelectionWindow->ADD(className+" Background",MenuLabel)(geom2d::rect<float>{backgroundOffsetPos,backgroundSize},"",1,ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
classSelectionWindow->ADD(className+" Button",MenuComponent)(geom2d::rect<float>{offsetPos,buttonSize},"Info",CLASS_INFO,
[](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];
Menu::InitializeClassInfoWindow();
return true;
})END
->S(A::CLASS_SELECTION)=className;
classSelectionWindow->ADD(className+" Label",MenuLabel)({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){
classSelectionWindow->ADD(className+" Label",MenuLabel)(geom2d::rect<float>{backgroundOffsetPos,buttonSize},className,1,ComponentAttr::SHADOW)END;
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"]->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;
})END;
@ -130,7 +130,7 @@ void Menu::InitializeClassSelectionWindow(){
toggleGroup.push_back(classSprite);
}
for(IToggleable*item:toggleGroup){
item->SetToggleGroup(toggleGroup);
for(std::weak_ptr<IToggleable>item:toggleGroup){
item.lock()->SetToggleGroup(toggleGroup);
}
}

@ -47,7 +47,7 @@ using A=Attribute;
void Menu::InitializeConsumableCraftItemWindow(){
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=[&](){
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);
};
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 Amount Label",MenuLabel)({{consumableCraftItemWindow->size.x/2+48,34},{32,12}},"1"
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)(geom2d::rect<float>{{consumableCraftItemWindow->size.x/2+48,34},{32,12}},"1"
,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);
Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Amount to Craft Amount Label")->SetLabel(std::to_string(qty));
return true;
})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);
Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Amount to Craft Amount Label")->SetLabel(std::to_string(qty));
return true;
})END;
consumableCraftItemWindow->ADD("Materials Requirement Outline",MenuComponent)({{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("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)(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("Craft Button",MenuComponent)({{consumableCraftItemWindow->size.x-84,116-18},{48,12}},"Craft",[](MenuFuncData data){
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)(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();
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);
}
}
data.component->SetGrayedOut(!item.lock()->CanEnhanceItem(qty));
data.component.lock()->SetGrayedOut(!item.lock()->CanEnhanceItem(qty));
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
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.
All rights reserved.
*/
@ -53,10 +53,10 @@ void Menu::InitializeConsumableCraftingWindow(){
Menu*consumableCraftingWindow=CreateMenu(CRAFT_CONSUMABLE,CENTERED,game->GetScreenSize()-vi2d{52,52});
#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){
RowItemDisplay*comp=DYNAMIC_CAST<RowItemDisplay*>(data.component);
const std::weak_ptr<Item>item=comp->GetItem();
std::weak_ptr<RowItemDisplay>comp=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
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,"Amount to Craft Amount Label")->SetLabel("1");
Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Item Name Header")->SetLabel(std::format("Crafting {}",item.lock()->DisplayName()));
@ -68,13 +68,13 @@ void Menu::InitializeConsumableCraftingWindow(){
return true;
},
[](MenuFuncData data){
RowItemDisplay*rowItem=DYNAMIC_CAST<RowItemDisplay*>(data.component);
if(rowItem->GetItem().lock()->GetEnhancementInfo().AvailableChapter()<=game->GetCurrentChapter()){
std::weak_ptr<RowItemDisplay>rowItem=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
if(rowItem.lock()->GetItem().lock()->GetEnhancementInfo().AvailableChapter()<=game->GetCurrentChapter()){
Component<MenuItemItemButton>(CRAFT_CONSUMABLE,"Item Icon")->SetHideDetails(false);
}else{
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;
},
[](MenuFuncData data){
@ -82,7 +82,7 @@ void Menu::InitializeConsumableCraftingWindow(){
return true;
},
InventoryCreator::RowPlayerWeapons_InventoryUpdate,
{.padding=1,.size={207,28}}
InventoryWindowOptions{.padding=1,.size={207,28}}
)END;
craftingItemsDisplay->SetCompactDescriptions(CRAFTING_INFO);
@ -96,7 +96,7 @@ void Menu::InitializeConsumableCraftingWindow(){
int x=int((invSize-1)%invWidth);
int y=int((invSize-1)/invWidth);
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->SetCompactDescriptions(craftingItemsDisplay->compact);
newItem->SetPriceLabelType(craftingItemsDisplay->priceLabel);
@ -111,26 +111,26 @@ void Menu::InitializeConsumableCraftingWindow(){
#pragma region Inventory Description
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 Icon",MenuItemItemButton)({{226+inventoryDescriptionWidth/2-24,30},{48,48}},Item::BLANK,MenuType::ENUM_END,DO_NOTHING,"","",IconButtonAttr::NOT_SELECTABLE)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)(geom2d::rect<float>{{226+inventoryDescriptionWidth/2-24,30},{48,48}},Item::BLANK,MenuType::ENUM_END,DO_NOTHING,"","",IconButtonAttr::NOT_SELECTABLE)END
->SetShowQuantity(false);
consumableCraftingWindow->ADD("Item Name Label",MenuLabel)({{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 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)(geom2d::rect<float>{{226,94},{inventoryDescriptionWidth-6,consumableCraftingWindow->size.y-44-66}},"",0.5f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
#pragma endregion
#pragma region Money Display
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());
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);
Player::AddMoneyListener(moneyDisplay);
#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){
Menu::CloseMenu();
return true;
},{2,2})END;
},vf2d{2.f,2.f})END;
}

@ -43,19 +43,19 @@ All rights reserved.
void Menu::InitializeCraftItemWindow(){
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("Required Materials Label",MenuLabel)({{4,80},{craftItemWindow->size.x-8,0}},"Required Materials",1.0f,ComponentAttr::SHADOW)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)(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("Craft Button",MenuComponent)({{craftItemWindow->size.x-84,116},{48,12}},"Craft",[](MenuFuncData data){
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)(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();
if(item.lock()->CanEnhanceItem()){
item.lock()->EnhanceItem();
@ -65,7 +65,7 @@ void Menu::InitializeCraftItemWindow(){
label=std::format("Level {} ->#00AA00 {}",item.lock()->EnhancementLevel(),item.lock()->EnhancementLevel()+1);
}
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;
})END;
}

@ -54,15 +54,14 @@ public:
virtual inline void UpdateSpawns(std::vector<std::string>&spawns){
Menu::menus.at(parentMenu)->components.erase_if([&](auto key){
if(key.first.starts_with("Spawn ")){
std::erase_if(components,[&](MenuComponent*component){return key.second==component;});
delete key.second;
std::erase_if(components,[&](std::weak_ptr<MenuComponent>component){return &*key.second==&*component.lock();});
return true;
}
return false;});
int offsetY=0;
vf2d parentSize=Menu::menus.at(OVERWORLD_LEVEL_SELECT)->size;
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;
}
}

@ -40,6 +40,7 @@ All rights reserved.
#include <sstream>
#include <format>
#include <any>
#include <memory>
#ifndef __EMSCRIPTEN__
#include <source_location>
#endif
@ -83,3 +84,17 @@ type DYNAMIC_CAST(auto variable){
if(pointer==nullptr)ERR("Could not dynamic cast to type "<<typeid(variable).name()<<"!");
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;
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){
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));
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.
if(component->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.
if(button2==nullptr)ERR("Could not cast item to a MenuItemButton*!");
if(button2==button){
if(button2->selected!=-1){
data.game->ClearLoadoutItem(button2->selected);
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.lock()->GetName().starts_with("item")){
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.expired())ERR("Could not cast item to a MenuItemButton*!");
if(&*button2.lock()==&*button.lock()){
if(button2.lock()->selected!=-1){
data.game->ClearLoadoutItem(button2.lock()->selected);
}
button2->selected=-1;
button2.lock()->selected=-1;
}
if(button2->selected==data.menu.I(A::LOADOUT_SLOT)){
button2->selected=-1;
if(button2.lock()->selected==data.menu.I(A::LOADOUT_SLOT)){
button2.lock()->selected=-1;
}
}
}
button->selected=data.menu.I(A::LOADOUT_SLOT);
data.game->SetLoadoutItem(button->selected,button->GetItem().lock()->ActualName());
button.lock()->selected=data.menu.I(A::LOADOUT_SLOT);
data.game->SetLoadoutItem(button.lock()->selected,button.lock()->GetItem().lock()->ActualName());
return true;
},InventoryCreator::Player_InventoryUpdate)END;
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.
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("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;
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);
};
#pragma endregion
@ -91,7 +91,7 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
vf2d buttonSize=c->options.size;
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->SetPriceLabelType(c->priceLabel);
newItem->SetHoverFunc(c->inventoryButtonHoverAction);
@ -128,7 +128,7 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
vf2d buttonSize=c->options.size;
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->SetPriceLabelType(c->priceLabel);
newItem->SetHoverFunc(c->inventoryButtonHoverAction);
@ -139,15 +139,13 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
#pragma region Row Player Weapons Updates
std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::RowPlayerWeapons_InventorySlotsUpdate=
[](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.AddButtonOnSlotUpdate(cat);
};
std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::RowPlayerWeapons_AddButtonOnSlotUpdate=
[](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();});
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);
@ -160,7 +158,7 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
int x=int((invSize-1)%invWidth);
int y=int((invSize-1)/invWidth);
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->SetPriceLabelType(c->priceLabel);
newItem->SetHoverFunc(c->inventoryButtonHoverAction);
@ -173,15 +171,13 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
#pragma region Row Player Armor Updates
std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::RowPlayerArmor_InventorySlotsUpdate=
[](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.AddButtonOnSlotUpdate(cat);
};
std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)> InventoryCreator::RowPlayerArmor_AddButtonOnSlotUpdate=
[](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();});
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);
@ -194,7 +190,7 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
int x=int((invSize-1)%invWidth);
int y=int((invSize-1)/invWidth);
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->SetPriceLabelType(c->priceLabel);
newItem->SetHoverFunc(c->inventoryButtonHoverAction);

@ -80,8 +80,8 @@ public:
virtual inline void Update(AiL*game)override{
ScrollableWindowComponent::Update(game);
bool noneHovered=true;
for(MenuComponent*component:components){
if(component->hovered){
for(std::weak_ptr<MenuComponent>component:components){
if(component.lock()->hovered){
noneHovered=false;
break;
}
@ -93,9 +93,9 @@ public:
}
virtual inline void SetCompactDescriptions(CompactText compact){
this->compact=compact;
for(MenuComponent*component:components){
MenuItemButton*itemButton=DYNAMIC_CAST<MenuItemButton*>(component);
itemButton->SetCompactDescriptions(compact);
for(std::weak_ptr<MenuComponent>component:components){
std::weak_ptr<MenuItemButton>itemButton=DYNAMIC_POINTER_CAST<MenuItemButton>(component.lock());
itemButton.lock()->SetCompactDescriptions(compact);
}
}
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
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.
All rights reserved.
*/
@ -58,8 +58,8 @@ using ButtonAttr::UNSELECTABLE_VIA_KEYBOARD;
void Menu::InitializeInventoryWindow(){
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 Tabs Outline",MenuComponent)({{0,28},{72,inventoryWindow->size.y-44}},"",DO_NOTHING,UNSELECTABLE)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)(geom2d::rect<float>{{0,28},{72,inventoryWindow->size.y-44}},"",DO_NOTHING,UNSELECTABLE)END;
std::vector<std::pair<std::string,int>>categories;
for(auto&[category,items]:ITEM_CATEGORIES){
@ -75,22 +75,22 @@ void Menu::InitializeInventoryWindow(){
float buttonWidth=64;
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){
//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<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<MenuComponent>(data.menu.GetType(),data.component->S(A::CATEGORY_NAME)+" Inventory Tab")->SetSelected(true);
data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)=data.component->S(A::CATEGORY_NAME);
Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.component.lock()->S(A::CATEGORY_NAME))->Enable(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.lock()->S(A::CATEGORY_NAME);
return true;
},{textScaling,1.f})END;
},vf2d{textScaling,1.f})END;
button->SetSelectionType(HIGHLIGHT);
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){
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;
},
[](MenuFuncData data){
@ -98,7 +98,7 @@ void Menu::InitializeInventoryWindow(){
return true;
},
InventoryCreator::RowPlayer_InventoryUpdate,
{.padding=1,.size={137,28}})END;
InventoryWindowOptions{.padding=1,.size={137,28}})END;
if(first){
inventoryWindow->S(A::LAST_INVENTORY_TYPE_OPENED)=category;
@ -116,25 +116,25 @@ void Menu::InitializeInventoryWindow(){
#pragma region Inventory Description
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 Icon",MenuItemItemButton)({{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 Description Label",MenuLabel)({{226,94},{inventoryDescriptionWidth-6,inventoryWindow->size.y-44-66}},"",0.5f,LEFT_ALIGN|SHADOW)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)(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)(geom2d::rect<float>{{226,84},{inventoryDescriptionWidth-6,12}},"",0.75f,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 region Money Display
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());
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);
Player::AddMoneyListener(moneyDisplay);
#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){
Menu::CloseMenu();
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);
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;
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 Description")->SetLabel("");
return true;
},"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 Description")->SetLabel("");
return true;
},"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 Description")->SetLabel("");
return true;
},"Item Name Label","Item Description")END;
itemLoadoutWindow->ADD("Item Name Label",MenuLabel)({{0,158},{itemLoadoutWindowWidth,12}},"",1,ComponentAttr::SHADOW)END;
itemLoadoutWindow->ADD("Item Description",MenuLabel)({{0,170},{itemLoadoutWindowWidth,24}},"",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)(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);
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 Label",MenuLabel)({{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;
levelCompleteWindow->ADD("Monster Loot Outline",MenuComponent)(geom2d::rect<float>{{0,32},{windowSize.size.x-80.f,72}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
levelCompleteWindow->ADD("Monster Loot Label",MenuLabel)(geom2d::rect<float>{{0,32},{windowSize.size.x-80.f,12}},"Monster Loot",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END;
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");
levelCompleteWindow->ADD("Stage Loot Outline",MenuComponent)({{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;
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;
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)(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)(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");
auto nextButtonAction=[](MenuFuncData data){
@ -69,13 +69,13 @@ void Menu::InitializeLevelCompleteWindow(){
return true;
};
levelCompleteWindow->ADD("Level Details Outline",MenuComponent)({{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("Next Button",MenuComponent)({{windowSize.size.x-72.f,144},{71,32}},"Next",nextButtonAction)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)(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)(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 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 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)(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 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 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)(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(){
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 List",ScrollableWindowComponent)({{-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("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)(geom2d::rect<float>{{-8,4},{112,116}})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(){
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);
#ifdef __EMSCRIPTEN__
data.menu.S(A::NEXT_MENU)="New Game";
@ -61,7 +61,7 @@ void Menu::InitializeMainMenuWindow(){
#endif
return true;
})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__
data.menu.S(A::NEXT_MENU)="Load Game";
if(SaveFile::GetUserID().length()==0){
@ -77,7 +77,7 @@ void Menu::InitializeMainMenuWindow(){
#endif
return true;
})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();
return true;
})END;

@ -53,12 +53,11 @@ std::vector<Menu*>Menu::stack;
std::map<MenuType,Menu*>Menu::menus;
std::string Menu::themeSelection="BlueDefault";
safeunorderedmap<std::string,Theme>Menu::themes;
safemap<ITCategory,std::vector<MenuComponent*>>Menu::inventoryListeners;
safemap<ITCategory,std::vector<MenuComponent*>>Menu::merchantInventoryListeners;
std::vector<MenuComponent*>Menu::equipStatListeners;
std::vector<MenuComponent*>Menu::chapterListeners;
safemap<ITCategory,std::vector<std::weak_ptr<MenuComponent>>>Menu::inventoryListeners;
safemap<ITCategory,std::vector<std::weak_ptr<MenuComponent>>>Menu::merchantInventoryListeners;
std::vector<std::weak_ptr<MenuComponent>>Menu::equipStatListeners;
std::vector<std::weak_ptr<MenuComponent>>Menu::chapterListeners;
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);
}
Menu::~Menu(){
for(auto&[key,value]:components){
delete value;
}
}
void Menu::InitializeMenus(){
#define MAX_MENUS 32
stack.reserve(MAX_MENUS);
@ -119,28 +112,11 @@ void Menu::InitializeMenus(){
if(menus.count(type)==0){
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){
MenuComponent*component=value;
component->AfterCreate();
value->AfterCreate();
}
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){
@ -157,12 +133,13 @@ void Menu::CheckClickAndPerformMenuSelect(AiL*game){
}
void Menu::HoverMenuSelect(AiL*game){
if(!game->IsFocused()||selection==vi2d{-1,-1}||buttons[selection.y][selection.x]->disabled)return;
if(buttons[selection.y][selection.x]->draggable){
if(!game->IsFocused()||selection.expired()||selection.lock()->disabled)return;
if(selection.lock()->draggable){
if(buttonHoldTime<"ThemeGlobal.MenuHoldTime"_F){
CheckClickAndPerformMenuSelect(game);
}else{
draggingComponent=buttons[selection.y][selection.x]->PickUpDraggableItem();
draggingComponent=std::move(selection.lock()->PickUpDraggableItem());
buttonHoldTime=0;
}
}else{
@ -171,12 +148,12 @@ void Menu::HoverMenuSelect(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;
bool buttonStillValid=buttons[selection.y][selection.x]->onClick(MenuFuncData{*this,game,buttons[selection.y][selection.x],(ScrollableWindowComponent*)buttons[selection.y][selection.x]->parentComponent});
if(!game->IsFocused()||selection.expired()||selection.lock()->disabled||selection.lock()->grayedOut)return;
bool buttonStillValid=selection.lock()->onClick(MenuFuncData{*this,game,selection,dynamic_pointer_cast<ScrollableWindowComponent>(selection.lock()->parentComponent.lock())});
if(buttonStillValid){
if(buttons[selection.y][selection.x]->menuDest!=MenuType::ENUM_END){
if(selection.lock()->menuDest!=MenuType::ENUM_END){
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{
ERR("WARNING! Exceeded menu stack size limit!")
}
@ -185,10 +162,7 @@ void Menu::MenuSelect(AiL*game){
}
void Menu::Update(AiL*game){
if(buttons.count(selection.y)==0)selection={-1,-1};
if(selection.x<0||selection.x>=buttons[selection.y].size()){selection={-1,-1};}
if(draggingComponent==nullptr){
if(!draggingComponent){
HoverMenuSelect(game);
}
@ -196,76 +170,64 @@ void Menu::Update(AiL*game){
SetMouseNavigation(true);
}
for(auto&[key,value]:buttons){
for(auto&button:value){
for(auto&[key,button]:components){
if(!button->disabled){
button->hovered=false;
}
}
}
bool itemHovered=false;
if(!UsingMouseNavigation()){
if(!game->IsTextEntryEnabled()){
if(selection!=vi2d{-1,-1}){
buttons[selection.y][selection.x]->hovered=true;
if(!selection.expired()){
selection.lock()->hovered=true;
itemHovered=true;
}
}
}else{
selection={-1,-1};
for(auto&[key,value]:buttons){
int index=0;
for(auto&button:value){
if(!button->disabled&&!button->grayedOut){
if(button->GetHoverState(game)){
button->hovered=true;
selection={};
for(auto&[key,component]:components){
if(component->selectable){
if(!component->disabled&&!component->grayedOut){
if(component->GetHoverState(game)){
component->hovered=true;
itemHovered=true;
selection.y=key;
selection.x=index;
selection=component;
}
}
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();
}else{
buttonHoldTime=0;
}
if(draggingComponent!=nullptr){
MenuComponent*selectedComponent=nullptr;
if(selection!=vi2d{-1,-1}){
selectedComponent=buttons[selection.y][selection.x];
}
if(draggingComponent){
auto ClearDraggingComponent=[&](){
delete draggingComponent; //We know we allocated a new instance of this, so we will now free it.
draggingComponent=nullptr;
draggingComponent={};
};
if(!UsingMouseNavigation()){
if(!game->IsTextEntryEnabled()){
if(game->KEY_CONFIRM.Released()){
if(selectedComponent==nullptr){//Dropping over an empty area.
if(selection.expired()){//Dropping over an empty area.
ClearDraggingComponent();
}else
if(selectedComponent->DropDraggableItem(draggingComponent)){
if(selection.lock()->DropDraggableItem(std::move(draggingComponent))){
ClearDraggingComponent();
}
}
}
}else{
if(game->KEY_CONFIRM.Released()){
if(selectedComponent==nullptr){//Dropping over an empty area.
if(selection.expired()){//Dropping over an empty area.
ClearDraggingComponent();
}else
if(selectedComponent->DropDraggableItem(draggingComponent)){
if(selection.lock()->DropDraggableItem(std::move(draggingComponent))){
ClearDraggingComponent();
}
}
@ -276,30 +238,12 @@ void Menu::Update(AiL*game){
KeyboardButtonNavigation(game,pos);
}
for(auto&[key,value]:buttons){
for(auto&button:value){
if(button->renderInMain){
button->_BeforeUpdate(game);
}
}
}
for(auto&component:displayComponents){
if(component->renderInMain){
for(auto&[key,component]:components){
component->_BeforeUpdate(game);
}
}
for(auto&[key,value]:buttons){
for(auto&button:value){
if(button->renderInMain){
button->_Update(game);
}
}
}
for(auto&component:displayComponents){
if(component->renderInMain){
for(auto&[key,component]:components){
component->_Update(game);
}
}
};
void Menu::Draw(AiL*game){
@ -309,10 +253,9 @@ void Menu::Draw(AiL*game){
DrawTiledWindowBackground(game,pos,size,GetRenderColor());
}
std::vector<MenuComponent*>allComponents;
std::copy(displayComponents.begin(),displayComponents.end(),std::back_inserter(allComponents));
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(),[](MenuComponent*c1,MenuComponent*c2){return c1->depth>c2->depth;});
std::vector<std::weak_ptr<MenuComponent>>allComponents;
std::for_each(components.begin(),components.end(),[&](auto&pair){allComponents.push_back(pair.second);});
std::sort(allComponents.begin(),allComponents.end(),[](std::weak_ptr<MenuComponent>c1,std::weak_ptr<MenuComponent>c2){return c1.lock()->depth>c2.lock()->depth;});
if(GetCurrentTheme().IsScaled()){
DrawScaledWindowBorder(game,pos,size,GetRenderColor());
@ -321,23 +264,22 @@ void Menu::Draw(AiL*game){
}
for(const auto&component:allComponents){
if(component->renderInMain){
component->_DrawDecal(window,this==Menu::stack.back());
if(!component.expired()&&component.lock()->renderInMain){
component.lock()->_DrawDecal(window,this==Menu::stack.back());
}
}
if(draggingComponent!=nullptr){
if(draggingComponent){
vf2d offsetPos=draggingComponent->rect.pos;
if(!UsingMouseNavigation()){
MenuComponent*selectedComponent=buttons[selection.y][selection.x];
vf2d drawOffset{};
if(selectedComponent->parentComponent!=nullptr){
ScrollableWindowComponent*scrollableComponent=DYNAMIC_CAST<ScrollableWindowComponent*>(selectedComponent->parentComponent);
if(scrollableComponent!=nullptr){
drawOffset+=scrollableComponent->GetScrollAmount();
if(!selection.expired()){
if(!selection.lock()->parentComponent.expired()){
std::weak_ptr<ScrollableWindowComponent>scrollableComponent=DYNAMIC_POINTER_CAST<ScrollableWindowComponent>(selection.lock()->parentComponent.lock());
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());
}else{
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){
vi2d 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;
}
}
std::weak_ptr<MenuComponent>prevSelection=selection;
if(game->KEY_CONFIRM.Pressed()){
SetMouseNavigation(game->GetMouse(0).bPressed); //If a click occurs we use mouse controls.
if(!UsingMouseNavigation()){
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(selection!=vi2d{-1,-1}&&(buttons[selection.y][selection.x]->disabled||buttons[selection.y][selection.x]->grayedOut)){
if(&*prevSelection.lock()!=&*selection.lock()){
if(!selection.expired()&&(selection.lock()->disabled||selection.lock()->grayedOut)){
bool handled=false;
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.
//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;
}
}
@ -572,9 +401,9 @@ Pixel Menu::GetRenderColor(){
return col;
}
bool Menu::HandleOutsideDisabledButtonSelection(MenuComponent*disabledButton){
if(disabledButton->parentComponent!=nullptr){
return disabledButton->parentComponent->HandleOutsideDisabledButtonSelection(disabledButton);
bool Menu::HandleOutsideDisabledButtonSelection(std::weak_ptr<MenuComponent>disabledButton){
if(!disabledButton.expired()){
return disabledButton.lock()->parentComponent.lock()->HandleOutsideDisabledButtonSelection(disabledButton);
}else{
return false;
}
@ -595,25 +424,25 @@ void Menu::SetMouseNavigation(bool mouseNavigation){
void Menu::InventorySlotsUpdated(ITCategory cat){
//Update the inventory with a new inventory slot, since there's one additional item to interact with now.
for(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!!!
comp->OnInventorySlotsUpdate(cat);
for(std::weak_ptr<MenuComponent>component:inventoryListeners.at(cat)){
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.lock()->OnInventorySlotsUpdate(cat);
}
}
void Menu::MerchantInventorySlotsUpdated(ITCategory cat){
//Update the inventory with a new inventory slot, since there's one additional item to interact with now.
for(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!!!
comp->OnInventorySlotsUpdate(cat);
for(std::weak_ptr<MenuComponent>component:merchantInventoryListeners.at(cat)){
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.lock()->OnInventorySlotsUpdate(cat);
}
}
void Menu::AddInventoryListener(MenuComponent*component,ITCategory category){
void Menu::AddInventoryListener(std::weak_ptr<MenuComponent>component,ITCategory category){
if(inventoryListeners.count(category)){
std::vector<MenuComponent*>&listenerList=inventoryListeners.at(category);
if(std::find(listenerList.begin(),listenerList.end(),component)!=listenerList.end()){
ERR("WARNING! Component "<<component->name<<" has already been added to the "<<category<<" listener list! There should not be any duplicates!!")
std::vector<std::weak_ptr<MenuComponent>>&listenerList=inventoryListeners.at(category);
if(std::find_if(listenerList.begin(),listenerList.end(),[&](auto&ptr){return &*ptr.lock()==&*component.lock();})!=listenerList.end()){
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);
}else{
@ -626,11 +455,11 @@ void Menu::InitializeMenuListenerCategory(const std::string&category){
merchantInventoryListeners[category];
}
void Menu::AddMerchantInventoryListener(MenuComponent*component,ITCategory category){
void Menu::AddMerchantInventoryListener(std::weak_ptr<MenuComponent>component,ITCategory category){
if(merchantInventoryListeners.count(category)){
std::vector<MenuComponent*>&listenerList=merchantInventoryListeners.at(category);
if(std::find(listenerList.begin(),listenerList.end(),component)!=listenerList.end()){
ERR("WARNING! Component "<<component->name<<" has already been added to the "<<category<<" merchant listener list! There should not be any duplicates!!")
std::vector<std::weak_ptr<MenuComponent>>&listenerList=merchantInventoryListeners.at(category);
if(std::find_if(listenerList.begin(),listenerList.end(),[&](auto&ptr){return &*ptr.lock()==&*component.lock();})!=listenerList.end()){
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);
}else{
@ -638,9 +467,9 @@ void Menu::AddMerchantInventoryListener(MenuComponent*component,ITCategory categ
}
}
void Menu::AddEquipStatListener(MenuComponent*component){
if(std::find(equipStatListeners.begin(),equipStatListeners.end(),component)!=equipStatListeners.end()){
ERR("WARNING! Component "<<component->name<<" has already been added to the Equip Stat listener list! There should not be any duplicates!!")
void Menu::AddEquipStatListener(std::weak_ptr<MenuComponent>component){
if(std::find_if(equipStatListeners.begin(),equipStatListeners.end(),[&](auto&ptr){return &*ptr.lock()==&*component.lock();})!=equipStatListeners.end()){
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);
}
@ -676,10 +505,8 @@ bool Menu::IsMenuOpen(){
void Menu::CleanupAllMenus(){
for(auto&[key,value]:Menu::menus){
Menu*menu=value;
for(auto&componentKey:menu->components){
MenuComponent*component=componentKey.second;
for(auto&[name,component]:menu->components){
component->Cleanup();
delete component;
}
menu->components.Reset();
menu->Cleanup();
@ -702,7 +529,7 @@ void Menu::DrawThemedWindow(vf2d menuPos,vf2d size,Pixel renderColor){
void Menu::RecalculateComponentCount(){
componentCount=displayComponents.size()+buttons.size();
componentCount=components.size();
}
const MenuType Menu::GetType()const{
@ -714,13 +541,13 @@ void Menu::LockInListeners(){
merchantInventoryListeners.SetInitialized();
}
void Menu::AddChapterListener(MenuComponent*component){
if(std::find(chapterListeners.begin(),chapterListeners.end(),component)!=chapterListeners.end()){
ERR("WARNING! Component "<<component->name<<" has already been added to the Chapter listener list! There should not be any duplicates!!")
void Menu::AddChapterListener(std::weak_ptr<MenuComponent>component){
if(std::find_if(chapterListeners.begin(),chapterListeners.end(),[&](auto&ptr){return &*ptr.lock()==&*component.lock();})!=chapterListeners.end()){
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);
}
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){}

@ -49,7 +49,7 @@ class MenuComponent;
class ScrollableWindowComponent;
//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 DEPTH ,
@ -112,62 +112,28 @@ class Menu:public IAttributable{
friend class EntityStats;
float buttonHoldTime=0;
vi2d selection={-1,-1};
std::weak_ptr<MenuComponent>selection;
vi2d lastActiveMousePos={};
int componentCount=0;
MenuComponent*draggingComponent=nullptr;
std::unique_ptr<MenuComponent>draggingComponent;
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<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<MenuComponent*>chapterListeners; //All menu components that care about story chapter updates subscribe to this list indirectly (See Menu::AddChapterListener()).
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<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<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<std::weak_ptr<MenuComponent>>chapterListeners; //All menu components that care about story chapter updates subscribe to this list indirectly (See Menu::AddChapterListener()).
public:
//The constructor is private. Use CreateMenu() instead!
Menu()=default;
~Menu();
//DO NOT USE DIRECTLY! You should be utilizing the ADD macro for adding components.
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;
if(depth==DEFAULT_DEPTH){
component->depth=STARTING_DEPTH-componentCount;
}else{
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();
if(components.count(componentKey)){
@ -178,7 +144,6 @@ public:
components[componentKey]=component;
components.SetInitialized();
lastRegisteredComponent=componentKey;
std::erase_if(Menu::unhandledComponents,[&](auto b1){return b1==component;});
return component;
}
@ -194,27 +159,23 @@ public:
static std::vector<Menu*>stack;
static std::string themeSelection;
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 bool IsMenuOpen();
const MenuType GetType()const;
safemap<std::string,MenuComponent*>components; //A friendly way to interrogate any component we are interested in.
std::vector<MenuComponent*>displayComponents; //Components that are only for displaying purposes.
safemap<std::string,std::shared_ptr<MenuComponent>>components; //A friendly way to interrogate any component we are interested in.
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 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 bool UsingMouseNavigation();
void SetMouseNavigation(bool mouseNavigation);
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 AddInventoryListener(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 AddEquipStatListener(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 AddInventoryListener(std::weak_ptr<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(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(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();
//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();
@ -242,7 +203,7 @@ private:
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.
bool HandleOutsideDisabledButtonSelection(MenuComponent*disabledButton);
bool HandleOutsideDisabledButtonSelection(std::weak_ptr<MenuComponent>disabledButton);
Pixel GetRenderColor();
MenuType type;
@ -252,17 +213,16 @@ private:
};
template<typename T>
T*Component(MenuType menu,std::string componentName){
T*tmp=DYNAMIC_CAST<T*>(Menu::menus[menu]->components[componentName]);
return tmp;
std::shared_ptr<T>Component(MenuType menu,std::string componentName){
return DYNAMIC_POINTER_CAST<T>(Menu::menus[menu]->components[componentName]);
}
struct MenuFuncData{
Menu&menu;
AiL*const game;
MenuComponent*const component;
ScrollableWindowComponent*const parentComponent=nullptr;
MenuFuncData(Menu&menu,AiL*const game,MenuComponent*const component,ScrollableWindowComponent*const parentComponent=nullptr);
const std::weak_ptr<MenuComponent> component;
const std::weak_ptr<ScrollableWindowComponent> parentComponent={};
MenuFuncData(Menu&menu,AiL*const game,std::weak_ptr<MenuComponent> component,std::weak_ptr<ScrollableWindowComponent>parentComponent={});
};
using MenuFunc=std::function<bool(MenuFuncData)>;

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

@ -39,15 +39,14 @@ All rights reserved.
#include "MenuComponent.h"
#include "drawutil.h"
#include "util.h"
#include "ScrollableWindowComponent.h"
INCLUDE_game
using A=Attribute;
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){
Menu::unhandledComponents.push_back(this);
}
: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){}
MenuComponent::MenuComponent(geom2d::rect<float>rect,std::string label,MenuType menuDest,MenuFunc onClick,ButtonAttr attributes)
:MenuComponent(rect,label,onClick,attributes){
@ -61,19 +60,7 @@ MenuComponent::MenuComponent(geom2d::rect<float>rect,std::string label,MenuType
this->labelScaling=labelScaling;
}
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.
}
MenuComponent::~MenuComponent(){}
void MenuComponent::AfterCreate(){}
@ -146,17 +133,17 @@ void MenuComponent::_DrawDecal(ViewPort&window,bool focused){
}
}
MenuComponent*MenuComponent::PickUpDraggableItem(){
return nullptr;
std::unique_ptr<MenuComponent>MenuComponent::PickUpDraggableItem(){
return {};
}
bool MenuComponent::DropDraggableItem(MenuComponent*draggable){
bool MenuComponent::DropDraggableItem(std::unique_ptr<MenuComponent>draggable){
return false;
}
bool MenuComponent::GetHoverState(AiL*game){
if(parentComponent!=nullptr){
return parentComponent->GetHoverState(game,this);
if(!parentComponent.expired()){
return parentComponent.lock()->GetHoverState(game,this);
}else{
vf2d parentWindowPos=Menu::menus[parentMenu]->pos;
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){
if(parentComponent!=nullptr){
return parentComponent->PointWithinParent(child,drawPos);
if(!parentComponent.expired()){
return parentComponent.lock()->PointWithinParent(child,drawPos);
}else{
return true;
}
}
bool MenuComponent::PointWithinParent(MenuComponent*child,geom2d::rect<float> drawRect){
if(parentComponent!=nullptr){
return parentComponent->PointWithinParent(child,drawRect);
if(!parentComponent.expired()){
return parentComponent.lock()->PointWithinParent(child,drawRect);
}else{
return true;
}
}
bool MenuComponent::HandleOutsideDisabledButtonSelection(MenuComponent*disabledButton){
bool MenuComponent::HandleOutsideDisabledButtonSelection(std::weak_ptr<MenuComponent>disabledButton){
return false;
};
@ -225,7 +212,7 @@ void MenuComponent::_OnMouseOut(){
if(runHoverFunctions){
if(hoverState){
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();
}
}
@ -235,7 +222,7 @@ void MenuComponent::_OnHover(){
if(hovered){
if(runHoverFunctions&&!hoverState){
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();
}
}
@ -259,5 +246,5 @@ void MenuComponent::OnPlayerMoneyUpdate(uint32_t newMoney){}
void MenuComponent::OnChapterUpdate(uint8_t newChapter){}
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();
public:
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,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);
@ -135,11 +135,11 @@ public:
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.
//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.
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.
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.
virtual void OnEquipStatsUpdate();
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){
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;
return pickUp;
}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....
MenuItemButton*draggedItem=(MenuItemButton*)draggable;
MenuItemButton*draggedItem=DYNAMIC_CAST<MenuItemButton*>(draggable.get());
ITCategory cat=draggedItem->invRef.at(draggedItem->inventoryIndex)->Category();
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
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.
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;});
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<MenuComponent>(MERCHANT,"Sell Tab")->selected=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<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;
})END;
buyTab->selected=true;
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<MenuComponent>(MERCHANT,"Buy Tab")->selected=false;
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<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;
})END;
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){
RowItemDisplay*item=DYNAMIC_CAST<RowItemDisplay*>(data.component);
Component<MenuLabel>(BUY_ITEM,"Item Purchase Header")->S(A::ITEM_NAME)=item->GetItem().lock()->ActualName();
Component<MenuLabel>(BUY_ITEM,"Price per item Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->BuyValue()));
std::weak_ptr<RowItemDisplay>item=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
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.lock()->GetItem().lock()->BuyValue()));
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();
bool canPurchase=merchant.CanPurchaseItem(item->GetItem().lock()->ActualName(),1);
bool canPurchase=merchant.CanPurchaseItem(item.lock()->GetItem().lock()->ActualName(),1);
std::string colorCode="";
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,"Item Purchase Header")->SetLabel("Buying "+item->GetItem().lock()->DisplayName());
Component<MenuComponent>(BUY_ITEM,"Purchase Button")->SetGrayedOut(!merchant.CanPurchaseItem(item->GetItem().lock()->ActualName(),1));
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.lock()->GetItem().lock()->DisplayName());
Component<MenuComponent>(BUY_ITEM,"Purchase Button")->SetGrayedOut(!merchant.CanPurchaseItem(item.lock()->GetItem().lock()->ActualName(),1));
Menu::OpenMenu(BUY_ITEM);
return true;
},
[](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;
},
[](MenuFuncData data){
@ -119,14 +119,14 @@ void Menu::InitializeMerchantWindow(){
return true;
},
InventoryCreator::RowMerchant_InventoryUpdate,
{.padding=1,.size={220-13,28}})END;
InventoryWindowOptions{.padding=1,.size={220-13,28}})END;
inventoryDisplay->SetPriceLabelType(PriceLabel::BUY_LABEL);
for(auto&[category,items]:ITEM_CATEGORIES){
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;});
@ -137,41 +137,41 @@ void Menu::InitializeMerchantWindow(){
float buttonWidth=64;
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){
//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<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<MenuComponent>(data.menu.GetType(),data.component->S(A::CATEGORY_NAME)+" Inventory Tab")->SetSelected(true);
data.menu.S(A::LAST_INVENTORY_TYPE_OPENED)=data.component->S(A::CATEGORY_NAME);
Component<RowInventoryScrollableWindowComponent>(data.menu.GetType(),"Inventory Display - "+data.component.lock()->S(A::CATEGORY_NAME))->Enable(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.lock()->S(A::CATEGORY_NAME);
return true;
},{textScaling,1.f})END;
},vf2d{textScaling,1.f})END;
button->SetSelectionType(HIGHLIGHT);
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){
RowItemDisplay*item=DYNAMIC_CAST<RowItemDisplay*>(data.component);
if(item->GetItem().lock()->CanBeSold()){
Component<ItemMenuLabel>(SELL_ITEM,"Item Sell Header")->SetItem(item->GetItem());
Component<MenuLabel>(SELL_ITEM,"Price per item Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->SellValue()));
std::weak_ptr<RowItemDisplay>item=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
if(item.lock()->GetItem().lock()->CanBeSold()){
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.lock()->GetItem().lock()->SellValue()));
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();
bool canPurchase=merchant.CanSellItem(item->GetItem(),1);
bool canPurchase=merchant.CanSellItem(item.lock()->GetItem(),1);
std::string colorCode="";
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,"Item Sell Header")->SetLabel("Selling "+item->GetItem().lock()->DisplayName());
Component<MenuComponent>(SELL_ITEM,"Sell Button")->SetGrayedOut(!merchant.CanSellItem(item->GetItem(),1));
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.lock()->GetItem().lock()->DisplayName());
Component<MenuComponent>(SELL_ITEM,"Sell Button")->SetGrayedOut(!merchant.CanSellItem(item.lock()->GetItem(),1));
Menu::OpenMenu(SELL_ITEM);
}
return true;
},
[](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;
},
[](MenuFuncData data){
@ -179,7 +179,7 @@ void Menu::InitializeMerchantWindow(){
return true;
},
InventoryCreator::RowPlayer_InventoryUpdate,
{.padding=1,.size={137,28}})END;
InventoryWindowOptions{.padding=1,.size={137,28}})END;
inventoryDisplay->SetPriceLabelType(PriceLabel::SELL_LABEL);
if(first){
@ -198,27 +198,27 @@ void Menu::InitializeMerchantWindow(){
#pragma region Inventory Description
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 Icon",MenuItemItemButton)({{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 Description Label",MenuLabel)({{226,94},{inventoryDescriptionWidth-6,merchantWindow->size.y-44-66}},"",0.5f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)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)(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)(geom2d::rect<float>{{226,84},{inventoryDescriptionWidth-6,12}},"",0.75f,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 region Money Display
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());
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);
Player::AddMoneyListener(moneyDisplay);
#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){
Menu::CloseMenu();
return true;
},{2,2})END;
},vf2d{2,2})END;
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};
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("Chapter Label",MenuLabel)({{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("Panel 1 Back",MenuLabel)(geom2d::rect<float>{{0,0},{windowSize.x-1,44}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)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)(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("Encounters Label",MenuLabel)({{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("Panel 2 Back",MenuLabel)(geom2d::rect<float>{{0,52},{windowSize.x-1,96}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)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)(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("Enter Button",MenuComponent)({{0,166},{windowSize.x-1,16}},"Enter",[](MenuFuncData data){State_OverworldMap::StartLevel();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)(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(){
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("Character Button",MenuComponent)({{4,12+28*1},{88,24}},"Character",
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)(geom2d::rect<float>{{4,12+28*1},{88,24}},"Character",
[](MenuFuncData data){
Component<CharacterRotatingDisplay>(CHARACTER_MENU,"Character Rotating Display")->SetIcon(GFX[classutils::GetClassInfo(game->GetPlayer()->GetClassName()).classFullImgName].Decal());
Menu::OpenMenu(CHARACTER_MENU);
return true;
})END;
overworldMenuWindow->ADD("Inventory Button",MenuComponent)({{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("Quit Button",MenuComponent)({{4,12+28*4},{88,24}},"Quit Game",[](MenuFuncData data){
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)(geom2d::rect<float>{{4,12+28*3},{88,24}},"Settings",[](MenuFuncData data){/*Menu::OpenMenu(SETTINGS_MENU);*/return true;})END;
overworldMenuWindow->ADD("Quit Button",MenuComponent)(geom2d::rect<float>{{4,12+28*4},{88,24}},"Quit Game",[](MenuFuncData data){
Menu::CloseAllMenus();
SaveFile::SaveGame();
game->ResetGame();

@ -73,7 +73,7 @@ InputGroup Player::KEY_ITEM1;
InputGroup Player::KEY_ITEM2;
InputGroup Player::KEY_ITEM3;
std::set<MenuComponent*>Player::moneyListeners;
std::vector<std::weak_ptr<MenuComponent>>Player::moneyListeners;
Player::Player()
:lastReleasedMovementKey(DOWN),facingDirection(DOWN),state(State::NORMAL){
@ -1020,8 +1020,8 @@ void EntityStats::RecalculateEquipStats(){
equipStats.A(key)+=setStats.A_Read(key);
}
}
for(MenuComponent*component:Menu::equipStatListeners){
component->OnEquipStatsUpdate();
for(std::weak_ptr<MenuComponent>component:Menu::equipStatListeners){
component.lock()->OnEquipStatsUpdate();
}
}
@ -1065,12 +1065,14 @@ uint32_t Player::GetMoney()const{
void Player::SetMoney(uint32_t newMoney){
money=newMoney;
for(auto&component:moneyListeners){
component->OnPlayerMoneyUpdate(newMoney);
component.lock()->OnPlayerMoneyUpdate(newMoney);
}
}
void Player::AddMoneyListener(MenuComponent*component){
if(moneyListeners.count(component))ERR("WARNING! Trying to add a second copy of component "<<std::quoted(component->GetName()));
moneyListeners.insert(component);
void Player::AddMoneyListener(std::weak_ptr<MenuComponent>component){
if(std::find_if(moneyListeners.begin(),moneyListeners.end(),[&](auto&ptr){return &*ptr.lock()==&*component.lock();})!=moneyListeners.end()){
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();
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;
void SetMoney(uint32_t newMoney);
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
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.
All rights reserved.
*/
@ -51,17 +51,17 @@ public:
virtual inline void SetCompactDescriptions(CompactText compact)override final{
this->compact=compact;
for(MenuComponent*component:components){
RowItemDisplay*itemButton=DYNAMIC_CAST<RowItemDisplay*>(component);
itemButton->SetCompactDescriptions(compact);
for(std::weak_ptr<MenuComponent>component:components){
std::weak_ptr<RowItemDisplay>itemButton=DYNAMIC_POINTER_CAST<RowItemDisplay>(component.lock());
itemButton.lock()->SetCompactDescriptions(compact);
}
}
virtual inline void SetPriceLabelType(PriceLabel::PriceLabel labelType)final{
this->priceLabel=labelType;
for(MenuComponent*component:components){
RowItemDisplay*itemButton=DYNAMIC_CAST<RowItemDisplay*>(component);
itemButton->SetPriceLabelType(labelType);
for(std::weak_ptr<MenuComponent>component:components){
std::weak_ptr<RowItemDisplay>itemButton=DYNAMIC_POINTER_CAST<RowItemDisplay>(component.lock());
itemButton.lock()->SetPriceLabelType(labelType);
}
}
};

@ -223,9 +223,9 @@ const void SaveFile::UpdateSaveGameData(){
float offsetY=0;
for(size_t i=0;i<saveFileCount;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){
LoadFileButton*comp=DYNAMIC_CAST<LoadFileButton*>(data.component);
saveFileID=comp->getSaveFileID();
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){
std::weak_ptr<LoadFileButton>comp=DYNAMIC_POINTER_CAST<LoadFileButton>(data.component.lock());
saveFileID=comp.lock()->getSaveFileID();
SaveFile::LoadGame();
return true;
},ButtonAttr::NONE)END;

@ -42,10 +42,10 @@ All rights reserved.
void Menu::InitializeSaveFileWindow(){
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 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("Back Button",MenuComponent)({{-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("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)(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)(geom2d::rect<float>{{-8,68},{48,12}},"Back",[](MenuFuncData data){Menu::CloseMenu();game->TextEntryEnable(false);return true;})END;
saveFileWindow->ADD("Continue Button",MenuComponent)(geom2d::rect<float>{{56,68},{48,12}},"Begin",MenuType::CLASS_SELECTION,[](MenuFuncData data){
SaveFile::SetSaveFileName(game->TextEntryGetString());
SaveFile::SetSaveFileID(SaveFile::GetSaveFileCount());
game->TextEntryEnable(false);

@ -46,9 +46,9 @@ using A=Attribute;
class ScrollableWindowComponent:public MenuComponent{
protected:
ViewPort subWindow;
std::vector<MenuComponent*>components;
MenuComponent*upButton=nullptr;
MenuComponent*downButton=nullptr;
std::vector<std::weak_ptr<MenuComponent>>components;
std::weak_ptr<MenuComponent>upButton;
std::weak_ptr<MenuComponent>downButton;
geom2d::rect<float>bounds; //It's for the scrollbar.
float scrollBarHeight=0;
float scrollBarTop=0;
@ -56,8 +56,8 @@ protected:
float scrollBarHoverTime=0;
vf2d scrollOffset;
protected:
inline bool OnScreen(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}});
inline bool OnScreen(std::weak_ptr<MenuComponent>component){
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:
inline ScrollableWindowComponent(geom2d::rect<float>rect,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)
@ -70,37 +70,25 @@ public:
RemoveButton(components.back());
}
}
virtual inline void RemoveButton(MenuComponent*button){
std::vector<MenuComponent*>&buttonList=Menu::menus[button->parentMenu]->buttons.at(int(button->originalPos.y));
std::vector<MenuComponent*>&keyboardButtonList=Menu::menus[button->parentMenu]->keyboardButtons.at(int(button->originalPos.y));
virtual inline void RemoveButton(std::weak_ptr<MenuComponent>button){
auto componentSearchResults=std::find_if(components.begin(),components.end(),[&](std::weak_ptr<MenuComponent>ptr){return &*ptr.lock()==&*button.lock();});
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;
removedCount+=std::erase(buttonList,button);
removedCount+=std::erase(keyboardButtonList,button);
removedCount+=Menu::menus[button->parentMenu]->components.erase(button->GetName());
if(removedCount!=3){
MenuType parentMenu=button.lock()->parentMenu;
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!";
}
if(buttonList.size()==0){
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;
Menu::menus[parentMenu]->RecalculateComponentCount();
CalculateBounds();
}
virtual inline void SetScrollAmount(vf2d scrollOffset){
this->scrollOffset=scrollOffset;
for(MenuComponent*component:components){
component->rect.pos=component->originalPos+scrollOffset;
for(std::weak_ptr<MenuComponent>component:components){
component.lock()->rect.pos=component.lock()->originalPos+scrollOffset;
}
}
virtual inline vf2d GetScrollAmount(){
@ -109,16 +97,16 @@ public:
protected:
virtual inline void AfterCreate()override{
//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;
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;
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)(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);
if(upButton){upButton->Enable(!disabled);}
if(downButton){downButton->Enable(!disabled);}
if(!upButton.expired()){upButton.lock()->Enable(!disabled);}
if(!downButton.expired()){downButton.lock()->Enable(!disabled);}
}
virtual inline void BeforeUpdate(AiL*game)override{
MenuComponent::BeforeUpdate(game);
for(MenuComponent*component:components){
component->_BeforeUpdate(game);
for(std::weak_ptr<MenuComponent>component:components){
component.lock()->_BeforeUpdate(game);
}
}
virtual inline void Update(AiL*game)override{
@ -165,17 +153,17 @@ protected:
SetScrollAmount({GetScrollAmount().x,0});
}
std::sort(components.begin(),components.end(),[](MenuComponent*c1,MenuComponent*c2){return c1->depth>c2->depth;});
for(MenuComponent*component:components){
component->disabled=!OnScreen(component);
component->_Update(game);
std::sort(components.begin(),components.end(),[](std::weak_ptr<MenuComponent>c1,std::weak_ptr<MenuComponent>c2){return c1.lock()->depth>c2.lock()->depth;});
for(std::weak_ptr<MenuComponent>component:components){
component.lock()->disabled=!OnScreen(component.lock());
component.lock()->_Update(game);
}
upButton->disabled=false;
downButton->disabled=false;
upButton.lock()->disabled=false;
downButton.lock()->disabled=false;
if(geom2d::contains(rect,bounds)){//This means we have no reason to show a scrollbar.
upButton->disabled=true;
downButton->disabled=true;
upButton.lock()->disabled=true;
downButton.lock()->disabled=true;
}
}
inline void DrawScrollbar(ViewPort&window,vf2d parentPos,bool focused){
@ -198,8 +186,8 @@ protected:
if(border){
window.DrawRectDecal(rect.pos,rect.size);
}
for(MenuComponent*component:components){
component->_DrawDecal(subWindow,focused);
for(std::weak_ptr<MenuComponent>component:components){
component.lock()->_DrawDecal(subWindow,focused);
}
if(!geom2d::contains(rect,bounds)){
DrawScrollbar(window,{},focused);
@ -212,33 +200,33 @@ protected:
//Calculates the bounds of all components.
inline void CalculateBounds(){
bounds={};
for(MenuComponent*component:components){
if(component->rect.pos.x<bounds.pos.x){
float sizeIncrease=bounds.pos.x-component->rect.pos.x;
for(std::weak_ptr<MenuComponent>component:components){
if(component.lock()->rect.pos.x<bounds.pos.x){
float sizeIncrease=bounds.pos.x-component.lock()->rect.pos.x;
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){
float sizeIncrease=component->rect.right().start.x-bounds.right().start.x;
if(component.lock()->rect.right().start.x>bounds.right().start.x){
float sizeIncrease=component.lock()->rect.right().start.x-bounds.right().start.x;
bounds.size.x+=sizeIncrease;
}
if(component->rect.pos.y<bounds.pos.y){
float sizeIncrease=bounds.pos.y-component->rect.pos.y;
if(component.lock()->rect.pos.y<bounds.pos.y){
float sizeIncrease=bounds.pos.y-component.lock()->rect.pos.y;
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){
float sizeIncrease=component->rect.bottom().start.y-bounds.bottom().start.y;
if(component.lock()->rect.bottom().start.y>bounds.bottom().start.y){
float sizeIncrease=component.lock()->rect.bottom().start.y-bounds.bottom().start.y;
bounds.size.y+=sizeIncrease;
}
}
}
public:
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);
button->renderInMain=false; //Now we are in control!
button->parentComponent=this;
button->parentComponent=Menu::menus[parentMenu]->components[this->GetName()];
button->disabled=disabled;
CalculateBounds();
@ -252,21 +240,21 @@ public:
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);
}
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.
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;
};
virtual void Cleanup()override{}
inline std::vector<MenuComponent*>&GetComponents(){
inline std::vector<std::weak_ptr<MenuComponent>>&GetComponents(){
return components;
}
virtual inline void Enable(bool enabled)override final{
disabled=!enabled;
for(MenuComponent*component:components){
component->Enable(enabled);
for(std::weak_ptr<MenuComponent>component:components){
component.lock()->Enable(enabled);
}
if(upButton){upButton->Enable(enabled);}
if(downButton){downButton->Enable(enabled);}
if(upButton.lock()){upButton.lock()->Enable(enabled);}
if(downButton.lock()){downButton.lock()->Enable(enabled);}
};
};

@ -62,34 +62,34 @@ void Menu::InitializeSellItemWindow(){
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("Amount to Sell Label",MenuLabel)({{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 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)(geom2d::rect<float>{{4,34},{188,12}},"Amount to Sell",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("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("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)(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);
return true;
})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);
return true;
})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.SellItem(Component<ItemMenuLabel>(SELL_ITEM,"Item Sell Header")->GetItem(),GetQuantity());
SoundEffect::PlaySFX("Sell Item",SoundEffect::CENTERED);
Menu::CloseMenu();
return true;
})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();
return true;
})END;

@ -1,5 +1,6 @@
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
Settings Menu
- Any settings should be saved to the save file!

@ -37,15 +37,15 @@ All rights reserved.
#pragma endregion
#pragma once
class IToggleable{
class IToggleable:public std::enable_shared_from_this<IToggleable>{
friend class AiL;
public:
inline std::vector<IToggleable*>GetToggleGroup(){
inline std::vector<std::weak_ptr<IToggleable>>GetToggleGroup(){
return toggleGroup;
}
inline void Select(){
for(IToggleable*item:toggleGroup){
item->selected=false;
for(std::weak_ptr<IToggleable>item:toggleGroup){
item.lock()->selected=false;
}
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!")
}
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;
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:
std::vector<IToggleable*>toggleGroup;
std::vector<std::weak_ptr<IToggleable>>toggleGroup;
private:
bool selected=false;
bool toggleGroupInitialized=false;
inline static std::vector<IToggleable*>uninitializedToggleGroupItems;
};

@ -46,15 +46,15 @@ using A=Attribute;
void Menu::InitializeUserIDWindow(){
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 Input",TextEntryLabel)({{36,94},{96,12}},[](std::string_view newLabel){
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)(geom2d::rect<float>{{36,94},{96,12}},[](std::string_view newLabel){
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;
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();
return true;
})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());
if(Menu::menus[MAIN_MENU]->S(A::NEXT_MENU)=="New Game"){
Menu::CloseMenu();

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

Loading…
Cancel
Save