From bd066ee78707552f1d19a49d0e9c729d596e79e0 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Wed, 27 Dec 2023 20:40:01 -0600 Subject: [PATCH] Refactored dynamic cast to do internal error checking. Refactored all inventory and button slot update functions in menu components so they no longer require virtual dispatch when all we require is some passing of lamba functions to update inventories. --- Crawler/BlacksmithCraftingWindow.cpp | 15 ++ Crawler/C++ Header File (OLC-3).zip | Bin 6846 -> 6851 bytes Crawler/CharacterMenuWindow.cpp | 6 +- Crawler/Crawler.vcxproj | 9 +- Crawler/Crawler.vcxproj.filters | 9 +- Crawler/Error.h | 11 +- Crawler/InventoryConsumableWindow.cpp | 4 +- Crawler/InventoryCreator.cpp | 219 ++++++++++++++++++ Crawler/InventoryCreator.h | 65 ++++++ Crawler/InventoryScrollableWindowComponent.h | 73 ++---- Crawler/InventoryWindow.cpp | 6 +- Crawler/Item.cpp | 43 +++- Crawler/Item.h | 7 + Crawler/LevelCompleteWindow.cpp | 4 +- Crawler/Menu.cpp | 8 +- Crawler/Menu.h | 9 +- Crawler/MenuComponent.cpp | 2 - Crawler/MenuComponent.h | 4 +- Crawler/MerchantWindow.cpp | 22 +- .../RowInventoryScrollableWindowComponent.h | 51 +--- ...rchantInventoryScrollableWindowComponent.h | 78 ------- Crawler/Version.h | 2 +- 22 files changed, 430 insertions(+), 217 deletions(-) create mode 100644 Crawler/InventoryCreator.cpp create mode 100644 Crawler/InventoryCreator.h delete mode 100644 Crawler/RowMerchantInventoryScrollableWindowComponent.h diff --git a/Crawler/BlacksmithCraftingWindow.cpp b/Crawler/BlacksmithCraftingWindow.cpp index f0d6f987..ab99cf61 100644 --- a/Crawler/BlacksmithCraftingWindow.cpp +++ b/Crawler/BlacksmithCraftingWindow.cpp @@ -40,6 +40,7 @@ All rights reserved. #include "Crawler.h" #include "MenuItemItemButton.h" #include "PlayerMoneyLabel.h" +#include "RowInventoryScrollableWindowComponent.h" INCLUDE_game INCLUDE_ITEM_CATEGORIES @@ -83,6 +84,20 @@ void Menu::InitializeBlacksmithCraftingWindow(){ })END; armorTab->selectionType=SelectionType::HIGHLIGHT; + auto inventoryDisplay=blacksmithWindow->ADD("Weapon Inventory Display",RowInventoryScrollableWindowComponent)({{2,28},{220,blacksmithWindow->size.y-44}},"Item Name Label","Item Description Label", + [](MenuFuncData data){ + return true; + }, + [](MenuFuncData data){ + return true; + }, + [](MenuFuncData data){ + return true; + }, + InventoryCreator::RowPlayerWeapons_InventoryUpdate, + {.padding=1,.size={207,28}} + )END; + #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; diff --git a/Crawler/C++ Header File (OLC-3).zip b/Crawler/C++ Header File (OLC-3).zip index d1814ddad9c5da00dfeed9764005ed4a27a09935..55ab0c3048f6320cedc738658c2877fd6e71dd58 100644 GIT binary patch delta 982 zcmV;{11bEzHN!O@6aWAK2mn{1n^&}p$jj0L000pO000t`5E>PcFc*Kzv`s*k zRFX>8r$UkFM6D$n5*=?YeV@K%Kgvq%?4m^t*rrI%d_OZHKkmBXu_>t2kF{wju1nqa z+V`$M`QG?ImuVb*zP<9jzutd*&-Ws;d+F-OCrdX!efgO_&=MThSiL>z03dv+qhh%skX>(Y?^dbWI=7p|($Cy5ZmKEj6a9H+5OK z1cC1rou-}cn%Y`j(XKPkb%lX_DlC39vTV2JP`8g%nzpK4R8KCb(e@Wd>C1%Pv+5lh zIM>g`__%pxS3(Ol``*GTTY$AgC?3qSTN*v^z1CQm7GSaNX^YG*!#QJmTYX0j@a49s z8yMv%-TWsdfE`TtXYX7)v+9)>EU5P2`bN$7-nh|`+Jw~qrFLTAZ)OLsKSDHvF%UA8A>{YooO7D-%cpe^$_RQ+oF@H0YiV%wVv6}(fL`#1>hf^MNwH^@XTqO={4z!T6P$(YdaVRLuMV3lNPS}jgC=U6G z%_e{cB(Xb|DBqLIFpl2~y*LVW?9XM=l!0bAjoA?@StF}pk#d5@k?%np+ylcnxR4o( zI0nSNGUy4#IvA5m_FIlkSfp9F3KvY$<-favD!~>MCdvg{A&!4wh0Lc?ag{4Zi!_}L zW=kgSc*Nwd6sOMqIpvZCfKcIpj1+JS%c=0aX)gI-98VMzBF_|0lPgsD9@cONM#WIl;;5+zOo`60Sw+d>_zZqPjTAq=YSEzF5UJO6MmbnYT?M`1CZC*gKW16CW4bFtSQ-fzr)y7s+~@6aWAK2mr4GeOHleh`h}M000A#7chTIv`s*kRFX>8r$Vvl zM6D$n5*=?YeV@K%Kgvq%?4m^t*rrI%d_OZHKkmBnu_>w3kF{wjsn@#gweMYj^1bna zF0&;1e0$}4f4%?sp6^9w_tMpmPnK?e`tmb<#^22iWvxz3t0PnCZn8Fw+bMKa_qMAa z_SPYlZAE(|uUmiW&AwafVdkN3%kG6Xrfd3u4z+zE(+&S-Z>cd=y{XrwOAz>8*=gG8 zuBoln7416nTvr&_r_$m_Bg=Mc4t4uTYtvS>i|WY*HQN5-D1DjGdse+e1Lyj=93MBY z?AFji&Azv=%9dd55XuMh?3P9ke6KYYrUh86d)gwi%W!0l-d5jH1N?eh)(wnuly3f$ z62K0o`?GhhomutD3l>y+aDAiZdvDz6NNqyu|5Cd+Q9A-WAm1BEMWY9mS??R&+VZum z*C1@Lf~eAhc)9IQ#TiOEgq>*|l;2J$)%6hP*4wg^r~yNNwQD`!+jX_;$j&ugdu+eH zf7PHCT$0RY>OK^VFwRAG$72@LbVX{xD9ZAc;PZu|MV7=&ND9*!v#An%TBuCOiw~hh zW^W* zm(6I&M6|$vb2#M*SE~VW##QRTW-!ki_m- zs(eo_!X$Yw^x`Pgu|JndQwEyhG+{@mbcL*fMal^pN4^Jba1RWV;6mms;usM7%Ah9{ zt6)qj*>43lVUgl+8P1uc%YSzTRe~)jOjHQAL>$3?3Rz60;;K-L=2;dGW=kgSc*Nwd zlw{8S85NQRfKcIpj1+JS%bD=KX(9Pw98VP!qR17`(koQ?9@cWP+ zgUjiEfbT<~7K3V6DDXNC&pJ04>PdV?VSL9O@o_UcK=QF$vKi`KM2pe$q5pH!y`7uy zUnKWEy~cmLYvLA1k)~OvwR+j%XvsVChhAH{JRA;_4W=!oC#HM6noz#gCEng%YuFGm zl>GOZn%HmFoqxC&I(Lz{qp%#$lW@DG0jtLux!CIt?>A;YUHjh0@y5f6aJ=;X0Z>Z= z1d}it8nYk-))ft}1ASMKY>2$g0{{R6lLH!20{sJ%RvJD5S(B9-CitemInvRef=Inventory::GetItem(it->ActualName()); auto equip=equipList->ADD("Equip Item "+std::to_string(counter),RowItemDisplay)({{2,2+counter*29.f},{120-15,28}},itemInvRef, [](MenuFuncData data){ - RowItemDisplay*comp=dynamic_cast(data.component); + RowItemDisplay*comp=DYNAMIC_CAST(data.component); if(comp!=nullptr){ Inventory::EquipItem(comp->GetItem(),EquipSlot(comp->I(Attribute::EQUIP_TYPE))); for(MenuComponent*button:((ScrollableWindowComponent*)data.parentComponent)->GetComponents()){ - RowItemDisplay*comp=dynamic_cast(button); + RowItemDisplay*comp=DYNAMIC_CAST(button); if(comp!=nullptr){ comp->SetSelected(false); }else{ @@ -162,7 +162,7 @@ void Menu::InitializeCharacterMenuWindow(){ equip->SetHoverFunc( [&](MenuFuncData data){ - RowItemDisplay*button=dynamic_cast(data.component); + RowItemDisplay*button=DYNAMIC_CAST(data.component); if(button!=nullptr){ const std::weak_ptrbuttonItem=button->GetItem(); std::vectorstatsBeforeEquip; diff --git a/Crawler/Crawler.vcxproj b/Crawler/Crawler.vcxproj index 602ee597..3d30eb51 100644 --- a/Crawler/Crawler.vcxproj +++ b/Crawler/Crawler.vcxproj @@ -343,6 +343,10 @@ + + + + @@ -407,7 +411,6 @@ - @@ -488,6 +491,10 @@ + + + + diff --git a/Crawler/Crawler.vcxproj.filters b/Crawler/Crawler.vcxproj.filters index 81c91edc..77ff2e26 100644 --- a/Crawler/Crawler.vcxproj.filters +++ b/Crawler/Crawler.vcxproj.filters @@ -375,12 +375,12 @@ Header Files\Interface - - Header Files\Interface - Header Files + + Header Files\Interface + @@ -632,6 +632,9 @@ Source Files\Interface + + Source Files\Interface + diff --git a/Crawler/Error.h b/Crawler/Error.h index 917d8009..a5cd24a2 100644 --- a/Crawler/Error.h +++ b/Crawler/Error.h @@ -38,6 +38,8 @@ All rights reserved. #pragma once #include #include +#include +#include #ifndef __EMSCRIPTEN__ #include #endif @@ -73,4 +75,11 @@ All rights reserved. std::cout< +type DYNAMIC_CAST(auto variable){ + type pointer=dynamic_cast(variable); + if(pointer==nullptr)ERR("Could not dynamic cast to type "<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(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. + MenuItemButton*button2=DYNAMIC_CAST(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){ @@ -77,7 +77,7 @@ void Menu::InitializeConsumableInventoryWindow(){ button->selected=data.menu.I(A::LOADOUT_SLOT); data.game->SetLoadoutItem(button->selected,button->GetItem().lock()->ActualName()); return true; - })END; + },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. diff --git a/Crawler/InventoryCreator.cpp b/Crawler/InventoryCreator.cpp new file mode 100644 index 00000000..5dfaea0d --- /dev/null +++ b/Crawler/InventoryCreator.cpp @@ -0,0 +1,219 @@ +#pragma region License +/* +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2018 - 2022 OneLoneCoder.com + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions or derivations of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions or derivative works in binary form must reproduce the above +copyright notice. This list of conditions and the following disclaimer must be +reproduced in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may +be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +Portions of this software are copyright © 2023 The FreeType +Project (www.freetype.org). Please see LICENSE_FT.txt for more information. +All rights reserved. +*/ +#pragma endregion +#include "InventoryCreator.h" +#include "RowInventoryScrollableWindowComponent.h" + +#define DEFINE(SubName) InventoryCreator InventoryCreator::SubName##_InventoryUpdate(&InventoryCreator::SubName##_InventorySlotsUpdate,&InventoryCreator::SubName##_AddButtonOnSlotUpdate); + +DEFINE(Player); +DEFINE(RowPlayer); +DEFINE(RowMerchant); +DEFINE(RowPlayerWeapons); +DEFINE(RowPlayerArmor); + +#pragma region Player Inventory Updates +std::function InventoryCreator::Player_InventorySlotsUpdate= + [](InventoryScrollableWindowComponent&component,ITCategory cat){ + size_t invSize=Inventory::get(cat).size(); + //We only want to refresh the inventory slots if the component count no longer matches what's actually in our inventory. + if(component.components.size()invSize){ //There are empty spots, so let's clean up. + component.RemoveAllComponents(); + for(std::weak_ptr item:Inventory::get(cat)){ + component.AddButtonOnSlotUpdate(cat); + } + } + }; +std::function InventoryCreator::Player_AddButtonOnSlotUpdate= + [](InventoryScrollableWindowComponent&component,ITCategory cat){ + size_t invSize=component.components.size()+1; + int invWidth=int(component.rect.size.x/(float(component.options.size.x)+component.options.padding)); + int x=int((invSize-1)%invWidth); + int y=int((invSize-1)/invWidth); + int itemIndex=y*invWidth+x; + + 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 + ->SetCompactDescriptions(component.compact==COMPACT); + }; +#pragma endregion + +#pragma region Row Inventory Player Updates +std::function InventoryCreator::RowPlayer_InventorySlotsUpdate=Player_InventorySlotsUpdate; +std::function InventoryCreator::RowPlayer_AddButtonOnSlotUpdate= + [](InventoryScrollableWindowComponent&component,ITCategory cat){ + RowInventoryScrollableWindowComponent*c=DYNAMIC_CAST(&component); + size_t invSize=c->components.size()+1; + int invWidth=int(c->rect.size.x/(float(c->options.size.x)+c->options.padding)); + int x=int((invSize-1)%invWidth); + int y=int((invSize-1)/invWidth); + int itemIndex=y*invWidth+x; + + 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; + newItem->SetCompactDescriptions(c->compact==COMPACT); + newItem->SetPriceLabelType(c->priceLabel); + newItem->SetHoverFunc(c->inventoryButtonHoverAction); + newItem->SetMouseOutFunc(c->inventoryButtonMouseOutAction); + }; +#pragma endregion + +#pragma region Row Merchant Updates +std::function InventoryCreator::RowMerchant_InventorySlotsUpdate= + [](InventoryScrollableWindowComponent&component,ITCategory cat){ + const std::vector>&merchantInv=Merchant::GetCurrentTravelingMerchant().GetShopItems(); + //We only want to refresh the inventory slots if the component count no longer matches what's actually in our inventory. + if(component.components.size()merchantInv.size()){ //There are empty spots, so let's clean up. + component.RemoveAllComponents(); + for(std::shared_ptr item:merchantInv){ + component.AddButtonOnSlotUpdate(cat); + } + } + }; +std::function InventoryCreator::RowMerchant_AddButtonOnSlotUpdate= + [](InventoryScrollableWindowComponent&component,ITCategory cat){ + const std::vector>&merchantInv=Merchant::GetCurrentTravelingMerchant().GetShopItems(); + RowInventoryScrollableWindowComponent*c=DYNAMIC_CAST(&component); + + size_t invSize=c->components.size()+1; + int invWidth=int(c->rect.size.x/(float(c->options.size.x)+c->options.padding)); + int x=int((invSize-1)%invWidth); + int y=int((invSize-1)/invWidth); + int itemIndex=y*invWidth+x; + + 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; + newItem->SetCompactDescriptions(c->compact==COMPACT); + newItem->SetPriceLabelType(c->priceLabel); + newItem->SetHoverFunc(c->inventoryButtonHoverAction); + newItem->SetMouseOutFunc(c->inventoryButtonMouseOutAction); + }; +#pragma endregion + +#pragma region Row Player Weapons Updates +std::function InventoryCreator::RowPlayerWeapons_InventorySlotsUpdate= + [](InventoryScrollableWindowComponent&component,ITCategory cat){ + std::vector>weapons; + std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(weapons),[](std::shared_ptr item){return item.get()->IsWeapon();}); + //We only want to refresh the inventory slots if the component count no longer matches what's actually in our inventory. + if(component.components.size()weapons.size()){ //There are empty spots, so let's clean up. + component.RemoveAllComponents(); + for(std::weak_ptr item:Inventory::get(cat)){ + component.AddButtonOnSlotUpdate(cat); + } + } + }; +std::function InventoryCreator::RowPlayerWeapons_AddButtonOnSlotUpdate= + [](InventoryScrollableWindowComponent&component,ITCategory cat){ + std::vector>weapons; + std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(weapons),[](std::shared_ptr item){return item.get()->IsWeapon();}); + + RowInventoryScrollableWindowComponent*c=DYNAMIC_CAST(&component); + size_t invSize=c->components.size()+1; + int invWidth=int(c->rect.size.x/(float(c->options.size.x)+c->options.padding)); + int x=int((invSize-1)%invWidth); + int y=int((invSize-1)/invWidth); + int itemIndex=y*invWidth+x; + + vf2d buttonSize=c->options.size; + vf2d totalSpacing={c->options.padding+buttonSize.x,c->options.padding+buttonSize.y}; + + for(std::shared_ptr weapon:weapons){ + auto newItem=c->ADD("item_"+cat+"_"+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; + newItem->SetCompactDescriptions(c->compact==COMPACT); + newItem->SetPriceLabelType(c->priceLabel); + newItem->SetHoverFunc(c->inventoryButtonHoverAction); + newItem->SetMouseOutFunc(c->inventoryButtonMouseOutAction); + } + }; +#pragma endregion + +#pragma region Row Player Armor Updates +std::function InventoryCreator::RowPlayerArmor_InventorySlotsUpdate= + [](InventoryScrollableWindowComponent&component,ITCategory cat){ + std::vector>armor; + std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(armor),[](std::shared_ptr item){return item.get()->IsArmor();}); + //We only want to refresh the inventory slots if the component count no longer matches what's actually in our inventory. + if(component.components.size()armor.size()){ //There are empty spots, so let's clean up. + component.RemoveAllComponents(); + for(std::weak_ptr item:Inventory::get(cat)){ + component.AddButtonOnSlotUpdate(cat); + } + } + }; +std::function InventoryCreator::RowPlayerArmor_AddButtonOnSlotUpdate= + [](InventoryScrollableWindowComponent&component,ITCategory cat){ + std::vector>armor; + std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(armor),[](std::shared_ptr item){return item.get()->IsArmor();}); + + RowInventoryScrollableWindowComponent*c=DYNAMIC_CAST(&component); + size_t invSize=c->components.size()+1; + int invWidth=int(c->rect.size.x/(float(c->options.size.x)+c->options.padding)); + int x=int((invSize-1)%invWidth); + int y=int((invSize-1)/invWidth); + int itemIndex=y*invWidth+x; + + vf2d buttonSize=c->options.size; + vf2d totalSpacing={c->options.padding+buttonSize.x,c->options.padding+buttonSize.y}; + + for(std::shared_ptr armor:armor){ + auto newItem=c->ADD("item_"+cat+"_"+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; + newItem->SetCompactDescriptions(c->compact==COMPACT); + newItem->SetPriceLabelType(c->priceLabel); + newItem->SetHoverFunc(c->inventoryButtonHoverAction); + newItem->SetMouseOutFunc(c->inventoryButtonMouseOutAction); + } + }; +#pragma endregion \ No newline at end of file diff --git a/Crawler/InventoryCreator.h b/Crawler/InventoryCreator.h new file mode 100644 index 00000000..e144d20e --- /dev/null +++ b/Crawler/InventoryCreator.h @@ -0,0 +1,65 @@ +#pragma region License +/* +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2018 - 2022 OneLoneCoder.com + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions or derivations of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions or derivative works in binary form must reproduce the above +copyright notice. This list of conditions and the following disclaimer must be +reproduced in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may +be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +Portions of this software are copyright © 2023 The FreeType +Project (www.freetype.org). Please see LICENSE_FT.txt for more information. +All rights reserved. +*/ +#pragma endregion +#pragma once +#include "Item.h" + +//WARNING! This macro will leave the member scoping at a PRIVATE level upon completion! +#define _SETUP(SubName) \ +private: \ + static std::function SubName##_InventorySlotsUpdate; \ + static std::function SubName##_AddButtonOnSlotUpdate; \ +public: \ + static InventoryCreator SubName##_InventoryUpdate; \ +private: + +class InventoryScrollableWindowComponent; + +class InventoryCreator{ + _SETUP(Player); + _SETUP(RowPlayer); + _SETUP(RowMerchant); + _SETUP(RowPlayerWeapons); + _SETUP(RowPlayerArmor); +public: + const std::function*const InventorySlotsUpdate; + const std::function*const AddButtonOnSlotUpdate; + inline InventoryCreator( + std::function*const InventorySlotsUpdate, + std::function*const AddButtonOnSlotUpdate) + :InventorySlotsUpdate(InventorySlotsUpdate),AddButtonOnSlotUpdate(AddButtonOnSlotUpdate){}; +}; \ No newline at end of file diff --git a/Crawler/InventoryScrollableWindowComponent.h b/Crawler/InventoryScrollableWindowComponent.h index 7c099389..f909151c 100644 --- a/Crawler/InventoryScrollableWindowComponent.h +++ b/Crawler/InventoryScrollableWindowComponent.h @@ -41,6 +41,7 @@ All rights reserved. #include "MenuItemButton.h" #include "Crawler.h" #include "ScrollableWindowComponent.h" +#include "InventoryCreator.h" using A=Attribute; @@ -56,22 +57,26 @@ struct InventoryWindowOptions{ // // Please ensure these are overwritten, otherwise you will get errors complaining about dynamic casts unable to convert classes successfully. class InventoryScrollableWindowComponent:public ScrollableWindowComponent{ + friend class InventoryCreator; + friend class Menu; protected: std::functioninventoryButtonClickAction; std::functioninventoryButtonHoverAction; std::functioninventoryButtonMouseOutAction; + const std::function*const onInventorySlotsUpdate; + const std::function*const addButtonOnSlotUpdate; CompactText compact=COMPACT; InventoryWindowOptions options; std::string itemNameLabelName; std::string itemDescriptionLabelName; bool inventoryButtonsActive=true; public: - inline InventoryScrollableWindowComponent(geom2d::rectrect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) - :InventoryScrollableWindowComponent(rect,itemNameLabelName,itemDescriptionLabelName,inventoryButtonClickAction,DO_NOTHING,DO_NOTHING,options,inventoryButtonsActive,attributes){ + inline InventoryScrollableWindowComponent(geom2d::rectrect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,const InventoryCreator&creator,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) + :InventoryScrollableWindowComponent(rect,itemNameLabelName,itemDescriptionLabelName,inventoryButtonClickAction,DO_NOTHING,DO_NOTHING,creator,options,inventoryButtonsActive,attributes){ } - inline InventoryScrollableWindowComponent(geom2d::rectrect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,std::functioninventoryButtonHoverAction,std::functioninventoryButtonMouseOutAction,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) + inline InventoryScrollableWindowComponent(geom2d::rectrect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,std::functioninventoryButtonHoverAction,std::functioninventoryButtonMouseOutAction,const InventoryCreator&creator,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) :ScrollableWindowComponent(rect,attributes),inventoryButtonHoverAction(inventoryButtonHoverAction),inventoryButtonMouseOutAction(inventoryButtonMouseOutAction),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName), - options(options),inventoryButtonClickAction(inventoryButtonClickAction),inventoryButtonsActive(inventoryButtonsActive){} + options(options),inventoryButtonClickAction(inventoryButtonClickAction),inventoryButtonsActive(inventoryButtonsActive),addButtonOnSlotUpdate(creator.AddButtonOnSlotUpdate),onInventorySlotsUpdate(creator.InventorySlotsUpdate){} virtual inline void Update(Crawler*game)override{ ScrollableWindowComponent::Update(game); bool noneHovered=true; @@ -90,65 +95,21 @@ public: if(compact)this->compact=COMPACT; else this->compact=NON_COMPACT; for(MenuComponent*component:components){ - MenuItemButton*itemButton=dynamic_cast(component); - if(itemButton){ - itemButton->SetCompactDescriptions(compact); - }else{ - ERR("WARNING! Attempting to cast a button that isn't a MenuItemButton!"); - } + MenuItemButton*itemButton=DYNAMIC_CAST(component); + itemButton->SetCompactDescriptions(compact); } } protected: - inline void RemoveEmptySlots(){ - //Algorithm will iterate through all slots, finding blank slots. Each time a blank slot is found, all items will shift over by one, and then the last item will be removed. Repeat until all slots iterated through. - for(int i=0;iUpdate(game); //We have to call update to update the validation state. - //HACK ALERT!! This only gets called on inventories...And only on inventory items, which would have the valid flag set. We only care about components that are inventory slots. - if(!button->valid){ - for(int j=i;jparentMenu]->components.at(components[j]->name)=components[size_t(j+1)]; - std::swap(components[j]->inventoryIndex,components[size_t(j+1)]->inventoryIndex); - std::swap(components[j],components[size_t(j+1)]); - } - MenuComponent*lastButton=components[components.size()-1]; - //Now we have to fix up the keyboard button list. - RemoveButton(lastButton); - i--; //Subtract one from the index so we don't accidently skip slots. - } - } - CalculateBounds(); //Recalculate the bounds as it's possible the width/height of the component has changed. - } - virtual inline void OnInventorySlotsUpdate(ITCategory cat)override{ - size_t invSize=Inventory::get(cat).size(); - //We only want to refresh the inventory slots if the component count no longer matches what's actually in our inventory. - if(components.size()invSize){ //There are empty spots, so let's clean up. - RemoveAllComponents(); - for(std::weak_ptr item:Inventory::get(cat)){ - AddButtonOnSlotUpdate(cat); - } - } - } virtual inline void Cleanup()override final{ } + //Called whenever an inventory slot gets updated, whether it's adding or removing an item. + inline void OnInventorySlotsUpdate(ITCategory cat){ + (*onInventorySlotsUpdate)(*this,cat); + }; - virtual inline void AddButtonOnSlotUpdate(ITCategory cat){ - size_t invSize=components.size()+1; - int invWidth=int(rect.size.x/(float(options.size.x)+options.padding)); - int x=int((invSize-1)%invWidth); - int y=int((invSize-1)/invWidth); - int itemIndex=y*invWidth+x; - - vf2d buttonSize=options.size; - int totalSpacing=options.padding+buttonSize.x; - - ADD("item_"+cat+"_"+std::to_string(itemIndex),MenuItemButton)({{float(totalSpacing*x),float(totalSpacing*y)},buttonSize},Inventory::get(cat),itemIndex,inventoryButtonClickAction,inventoryButtonHoverAction,inventoryButtonMouseOutAction,parentMenu,itemNameLabelName,itemDescriptionLabelName,inventoryButtonsActive?IconButtonAttr::SELECTABLE:IconButtonAttr::NOT_SELECTABLE)END - ->SetCompactDescriptions(compact==COMPACT); + inline void AddButtonOnSlotUpdate(ITCategory cat){ + (*addButtonOnSlotUpdate)(*this,cat); } }; \ No newline at end of file diff --git a/Crawler/InventoryWindow.cpp b/Crawler/InventoryWindow.cpp index a7fec77e..551bfe8c 100644 --- a/Crawler/InventoryWindow.cpp +++ b/Crawler/InventoryWindow.cpp @@ -90,7 +90,7 @@ void Menu::InitializeInventoryWindow(){ auto inventoryDisplay=inventoryWindow->ADD("Inventory Display - "+category,RowInventoryScrollableWindowComponent)({{72,28},{150,inventoryWindow->size.y-44}},"Item Name Label","Item Description Label",DO_NOTHING, [](MenuFuncData data){ - Component(data.menu.GetType(),"Item Icon")->SetItem(dynamic_cast(data.component)->GetItem()); + Component(data.menu.GetType(),"Item Icon")->SetItem(DYNAMIC_CAST(data.component)->GetItem()); Component(data.menu.GetType(),"Item Icon")->UpdateIcon(); return true; }, @@ -98,7 +98,9 @@ void Menu::InitializeInventoryWindow(){ Component(data.menu.GetType(),"Item Icon")->SetItem(Item::BLANK); Component(data.menu.GetType(),"Item Icon")->UpdateIcon(); return true; - },{.padding=1,.size={137,28}})END; + }, + InventoryCreator::RowPlayer_InventoryUpdate, + {.padding=1,.size={137,28}})END; if(first){ inventoryWindow->S(A::LAST_INVENTORY_TYPE_OPENED)=category; diff --git a/Crawler/Item.cpp b/Crawler/Item.cpp index 99e5c81f..3f0c3575 100644 --- a/Crawler/Item.cpp +++ b/Crawler/Item.cpp @@ -203,6 +203,21 @@ void ItemInfo::InitializeItems(){ props.customProps=&data[key]; } it.useFunc=scriptName; + + #pragma region Equipment Category Verification Tests + int equipmentCategories=0; + equipmentCategories+=it.IsWeapon(); + equipmentCategories+=it.IsArmor(); + equipmentCategories+=it.IsAccessory(); + if(it.IsEquippable()&&equipmentCategories==0)ERR(std::format("WARNING! {} is not considered a weapon, armor, or accessory but is considered equippable!",it.Name())) + if(it.IsEquippable()&&equipmentCategories!=1)ERR(std::format("WARNING! {} is considered in {} categories among the [weapon, armor, or accessory] set but is considered equippable!",it.Name(),equipmentCategories)) + + if(it.IsEquippable()&&it.Category()!="Equipment"&&it.Category()!="Accessories")ERR(std::format("WARNING! {} is considered equippable, but is not in either the Equipment category or the Accessories category!",it.Name())) + if(it.Category()=="Equipment"&&!(it.IsWeapon()||it.IsArmor()))ERR(std::format("WARNING! {} is in the Equipment Category, but is not considered a Weapon or Armor!",it.Name())) + if(it.Category()=="Accessories"&&it.IsWeapon())ERR(std::format("WARNING! {} is in the Accessories Category, but is considered a Weapon!",it.Name())) + if(it.Category()=="Accessories"&&it.IsArmor())ERR(std::format("WARNING! {} is in the Accessories Category, but is considered Armor!",it.Name())) + if(it.Category()=="Accessories"&&!it.IsAccessory())ERR(std::format("WARNING! {} is in the Accessories Category, but not considered an Accessory!",it.Name())) + #pragma endregion } }; @@ -471,8 +486,11 @@ const std::string Item::DisplayName()const{ } return name; } +const bool ItemInfo::IsEquippable()const{ + return slot!=EquipSlot::NONE&&(category=="Equipment"||category=="Accessories"); +} const bool Item::IsEquippable()const{ - return Category()=="Equipment"||Category()=="Accessories"; + return it->IsEquippable(); } const std::string Item::Description(CompactText compact)const{ std::string description=it->Description(); @@ -882,4 +900,25 @@ const EnhancementInfo&Item::GetEnhancementInfo()const{ const bool EnhancementInfo::CanBeEnhanced()const{ return enhancementStats.size()=="Item.Item Max Enhancement Level"_I+1&&craftingRequirements.size()=="Item.Item Max Enhancement Level"_I+1; -}; \ No newline at end of file +}; + + +const bool ItemInfo::IsWeapon()const{ + return slot==EquipSlot::WEAPON; +} +const bool ItemInfo::IsArmor()const{ + return IsEquippable()&&!IsWeapon()&&!IsAccessory(); +} +const bool ItemInfo::IsAccessory()const{ + return slot==EquipSlot::RING1||slot==EquipSlot::RING2; +} + +const bool Item::IsWeapon()const{ + return it->IsWeapon(); +} +const bool Item::IsArmor()const{ + return it->IsArmor(); +} +const bool Item::IsAccessory()const{ + return it->IsAccessory(); +} \ No newline at end of file diff --git a/Crawler/Item.h b/Crawler/Item.h index 809e9a91..0c263660 100644 --- a/Crawler/Item.h +++ b/Crawler/Item.h @@ -185,6 +185,9 @@ public: static std::shared_ptrBLANK; const std::optionalItemSet()const; const bool IsEquippable()const; + const bool IsWeapon()const; + const bool IsArmor()const; + const bool IsAccessory()const; const uint32_t BuyValue()const; const uint32_t SellValue()const; const bool CanBeSold()const; @@ -299,6 +302,10 @@ public: const bool CanBePurchased()const; const bool UseDuringCast()const; const EnhancementInfo&GetEnhancementInfo()const; + const bool IsEquippable()const; + const bool IsWeapon()const; + const bool IsArmor()const; + const bool IsAccessory()const; }; class ItemOverlay{ diff --git a/Crawler/LevelCompleteWindow.cpp b/Crawler/LevelCompleteWindow.cpp index 4ff3adb6..859322ed 100644 --- a/Crawler/LevelCompleteWindow.cpp +++ b/Crawler/LevelCompleteWindow.cpp @@ -55,12 +55,12 @@ void Menu::InitializeLevelCompleteWindow(){ 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)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; 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)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; Menu::AddInventoryListener(stageLootWindow,"Stage Loot"); auto nextButtonAction=[](MenuFuncData data){ diff --git a/Crawler/Menu.cpp b/Crawler/Menu.cpp index c780f465..470d9f63 100644 --- a/Crawler/Menu.cpp +++ b/Crawler/Menu.cpp @@ -318,7 +318,7 @@ void Menu::Draw(Crawler*game){ MenuComponent*selectedComponent=buttons[selection.y][selection.x]; vf2d drawOffset{}; if(selectedComponent->parentComponent!=nullptr){ - ScrollableWindowComponent*scrollableComponent=dynamic_cast(selectedComponent->parentComponent); + ScrollableWindowComponent*scrollableComponent=DYNAMIC_CAST(selectedComponent->parentComponent); if(scrollableComponent!=nullptr){ drawOffset+=scrollableComponent->GetScrollAmount(); } @@ -582,14 +582,16 @@ 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)){ - component->OnInventorySlotsUpdate(cat); + InventoryScrollableWindowComponent*comp=DYNAMIC_CAST(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); } } 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)){ - component->OnInventorySlotsUpdate(cat); + InventoryScrollableWindowComponent*comp=DYNAMIC_CAST(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); } } diff --git a/Crawler/Menu.h b/Crawler/Menu.h index 547d94a4..2ae18d8e 100644 --- a/Crawler/Menu.h +++ b/Crawler/Menu.h @@ -244,13 +244,8 @@ private: template T*Component(MenuType menu,std::string componentName){ - T*tmp=dynamic_cast(Menu::menus[menu]->components[componentName]); - if(tmp!=nullptr){ - return tmp; - }else{ - ERR("WARNING! Attempting to cast a button that isn't a "<(Menu::menus[menu]->components[componentName]); + return tmp; } struct MenuFuncData{ diff --git a/Crawler/MenuComponent.cpp b/Crawler/MenuComponent.cpp index e3ef4365..e817fb43 100644 --- a/Crawler/MenuComponent.cpp +++ b/Crawler/MenuComponent.cpp @@ -191,8 +191,6 @@ vf2d MenuComponent::GetPos(){ return rect.pos; } -void MenuComponent::OnInventorySlotsUpdate(ITCategory cat){} - std::string MenuComponent::GetLabel(){ return label; } diff --git a/Crawler/MenuComponent.h b/Crawler/MenuComponent.h index 2db261a8..a6504b68 100644 --- a/Crawler/MenuComponent.h +++ b/Crawler/MenuComponent.h @@ -66,6 +66,8 @@ using SelectionType::CROSSHAIR; using SelectionType::HIGHLIGHT; using SelectionType::INNER_BOX; +class InventoryScrollableWindowComponent; + class MenuComponent:public IAttributable{ friend class Menu; friend class MenuItemButton; @@ -136,8 +138,6 @@ public: virtual bool DropDraggableItem(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); - //Called whenever an inventory slot gets updated, whether it's adding or removing an item. - virtual void OnInventorySlotsUpdate(ITCategory cat); //Called whenever equipment and base stats are updated, notifying a component that numbers that may be displayed have changed. virtual void OnEquipStatsUpdate(); std::string GetLabel(); diff --git a/Crawler/MerchantWindow.cpp b/Crawler/MerchantWindow.cpp index d3dc4969..3ab79db8 100644 --- a/Crawler/MerchantWindow.cpp +++ b/Crawler/MerchantWindow.cpp @@ -38,7 +38,7 @@ All rights reserved. #include "Menu.h" #include "Crawler.h" -#include "RowMerchantInventoryScrollableWindowComponent.h" +#include "RowInventoryScrollableWindowComponent.h" #include "MenuItemItemButton.h" #include "MenuComponent.h" #include "PlayerMoneyLabel.h" @@ -61,7 +61,7 @@ void Menu::InitializeMerchantWindow(){ std::sort(categories.begin(),categories.end(),[](std::pair&cat1,std::pair&cat2){return cat1.secondADD("Buy Tab",MenuComponent)({{2,0},{merchantWindow->size.x/2-4,24}},"Buy",[](MenuFuncData data){ - Component(MERCHANT,"Merchant Inventory Display")->Enable(true); + Component(MERCHANT,"Merchant Inventory Display")->Enable(true); Component(MERCHANT,"Sell Tab")->selected=false; Component(MERCHANT,"Inventory Tabs Outline")->Enable(false); for(auto&[category,items]:ITEM_CATEGORIES){ @@ -91,9 +91,9 @@ void Menu::InitializeMerchantWindow(){ })END; sellTab->selectionType=SelectionType::HIGHLIGHT; - auto inventoryDisplay=merchantWindow->ADD("Merchant Inventory Display",RowMerchantInventoryScrollableWindowComponent)({{2,28},{220,merchantWindow->size.y-44}},"Item Name Label","Item Description Label", + auto inventoryDisplay=merchantWindow->ADD("Merchant Inventory Display",RowInventoryScrollableWindowComponent)({{2,28},{220,merchantWindow->size.y-44}},"Item Name Label","Item Description Label", [](MenuFuncData data){ - RowItemDisplay*item=dynamic_cast(data.component); + RowItemDisplay*item=DYNAMIC_CAST(data.component); Component(BUY_ITEM,"Item Purchase Header")->S(A::ITEM_NAME)=item->GetItem().lock()->ActualName(); Component(BUY_ITEM,"Price per item Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->BuyValue())); Component(BUY_ITEM,"Amount to buy Amount Label")->SetLabel("1"); @@ -110,7 +110,7 @@ void Menu::InitializeMerchantWindow(){ return true; }, [](MenuFuncData data){ - Component(data.menu.GetType(),"Item Icon")->SetItem(dynamic_cast(data.component)->GetItem()); + Component(data.menu.GetType(),"Item Icon")->SetItem(DYNAMIC_CAST(data.component)->GetItem()); Component(data.menu.GetType(),"Item Icon")->UpdateIcon(); return true; }, @@ -118,7 +118,9 @@ void Menu::InitializeMerchantWindow(){ Component(data.menu.GetType(),"Item Icon")->SetItem(Item::BLANK); Component(data.menu.GetType(),"Item Icon")->UpdateIcon(); return true; - },{.padding=1,.size={220-13,28}})END; + }, + InventoryCreator::RowMerchant_InventoryUpdate, + {.padding=1,.size={220-13,28}})END; inventoryDisplay->SetPriceLabelType(PriceLabel::BUY_LABEL); for(auto&[category,items]:ITEM_CATEGORIES){ @@ -151,7 +153,7 @@ void Menu::InitializeMerchantWindow(){ auto inventoryDisplay=merchantWindow->ADD("Inventory Display - "+category,RowInventoryScrollableWindowComponent)({{72,28},{150,merchantWindow->size.y-44}},"Item Name Label","Item Description Label", [](MenuFuncData data){ - RowItemDisplay*item=dynamic_cast(data.component); + RowItemDisplay*item=DYNAMIC_CAST(data.component); if(item->GetItem().lock()->CanBeSold()){ Component(SELL_ITEM,"Item Sell Header")->S(A::ITEM_NAME)=item->GetItem().lock()->ActualName(); Component(SELL_ITEM,"Price per item Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->SellValue())); @@ -170,7 +172,7 @@ void Menu::InitializeMerchantWindow(){ return true; }, [](MenuFuncData data){ - Component(data.menu.GetType(),"Item Icon")->SetItem(dynamic_cast(data.component)->GetItem()); + Component(data.menu.GetType(),"Item Icon")->SetItem(DYNAMIC_CAST(data.component)->GetItem()); Component(data.menu.GetType(),"Item Icon")->UpdateIcon(); return true; }, @@ -178,7 +180,9 @@ void Menu::InitializeMerchantWindow(){ Component(data.menu.GetType(),"Item Icon")->SetItem(Item::BLANK); Component(data.menu.GetType(),"Item Icon")->UpdateIcon(); return true; - },{.padding=1,.size={137,28}})END; + }, + InventoryCreator::RowPlayer_InventoryUpdate, + {.padding=1,.size={137,28}})END; inventoryDisplay->SetPriceLabelType(PriceLabel::SELL_LABEL); if(first){ diff --git a/Crawler/RowInventoryScrollableWindowComponent.h b/Crawler/RowInventoryScrollableWindowComponent.h index c09a06ab..dd8108f4 100644 --- a/Crawler/RowInventoryScrollableWindowComponent.h +++ b/Crawler/RowInventoryScrollableWindowComponent.h @@ -38,65 +38,30 @@ All rights reserved. #include "InventoryScrollableWindowComponent.h" #include "RowItemDisplay.h" +#include "InventoryCreator.h" class RowInventoryScrollableWindowComponent:public InventoryScrollableWindowComponent{ + friend class InventoryCreator; protected: PriceLabel::PriceLabel priceLabel=PriceLabel::NONE; public: - inline RowInventoryScrollableWindowComponent(geom2d::rectrect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,std::functioninventoryButtonHoverAction,std::functioninventoryButtonMouseOutAction,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) - :InventoryScrollableWindowComponent(rect,itemNameLabelName,itemDescriptionLabelName,inventoryButtonClickAction,inventoryButtonHoverAction,inventoryButtonMouseOutAction,options,inventoryButtonsActive,attributes){} + inline RowInventoryScrollableWindowComponent(geom2d::rectrect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,std::functioninventoryButtonHoverAction,std::functioninventoryButtonMouseOutAction,const InventoryCreator&creator,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) + :InventoryScrollableWindowComponent(rect,itemNameLabelName,itemDescriptionLabelName,inventoryButtonClickAction,inventoryButtonHoverAction,inventoryButtonMouseOutAction,creator,options,inventoryButtonsActive,attributes){} virtual inline void SetCompactDescriptions(bool compact)override final{ if(compact)this->compact=COMPACT; else this->compact=NON_COMPACT; for(MenuComponent*component:components){ - RowItemDisplay*itemButton=dynamic_cast(component); - if(itemButton){ - itemButton->SetCompactDescriptions(compact); - }else{ - ERR("WARNING! Attempting to cast a button that isn't a MenuItemButton!"); - } + RowItemDisplay*itemButton=DYNAMIC_CAST(component); + itemButton->SetCompactDescriptions(compact); } } virtual inline void SetPriceLabelType(PriceLabel::PriceLabel labelType)final{ this->priceLabel=labelType; for(MenuComponent*component:components){ - RowItemDisplay*itemButton=dynamic_cast(component); - if(itemButton){ - itemButton->SetPriceLabelType(labelType); - }else{ - ERR("WARNING! Attempting to cast a button that isn't a MenuItemButton!"); - } - } - } - - virtual inline void AddButtonOnSlotUpdate(ITCategory cat)override{ - size_t invSize=components.size()+1; - int invWidth=int(rect.size.x/(float(options.size.x)+options.padding)); - int x=int((invSize-1)%invWidth); - int y=int((invSize-1)/invWidth); - int itemIndex=y*invWidth+x; - - vf2d buttonSize=options.size; - vf2d totalSpacing={options.padding+buttonSize.x,options.padding+buttonSize.y}; - - auto newItem=ADD("item_"+cat+"_"+std::to_string(itemIndex),RowItemDisplay)({totalSpacing*vf2d{float(x),float(y)},buttonSize},Inventory::GetInventorySlot(cat,itemIndex),inventoryButtonClickAction,itemNameLabelName,itemDescriptionLabelName,inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END; - newItem->SetCompactDescriptions(compact==COMPACT); - newItem->SetPriceLabelType(priceLabel); - newItem->SetHoverFunc(inventoryButtonHoverAction); - newItem->SetMouseOutFunc(inventoryButtonMouseOutAction); - - //Since we are indexing into a vector reference which is inevitably going to get erased as we add more items, - //we must update all previous menu component references in case that memory has been overwritten! - for(int counter=0;MenuComponent*component:components){ - RowItemDisplay*item=dynamic_cast(component); - if(item!=nullptr){ - item->SetItem(Inventory::GetInventorySlot(cat,counter)); - }else{ - ERR("WARNING! Could not properly cast item to RowItemDisplay* type!"); - } - counter++; + RowItemDisplay*itemButton=DYNAMIC_CAST(component); + itemButton->SetPriceLabelType(labelType); } } }; \ No newline at end of file diff --git a/Crawler/RowMerchantInventoryScrollableWindowComponent.h b/Crawler/RowMerchantInventoryScrollableWindowComponent.h deleted file mode 100644 index 9ae0429a..00000000 --- a/Crawler/RowMerchantInventoryScrollableWindowComponent.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma region License -/* -License (OLC-3) -~~~~~~~~~~~~~~~ - -Copyright 2018 - 2022 OneLoneCoder.com - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions or derivations of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -2. Redistributions or derivative works in binary form must reproduce the above -copyright notice. This list of conditions and the following disclaimer must be -reproduced in the documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors may -be used to endorse or promote products derived from this software without specific -prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT -SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. - -Portions of this software are copyright © 2023 The FreeType -Project (www.freetype.org). Please see LICENSE_FT.txt for more information. -All rights reserved. -*/ -#pragma endregion - -#include "RowInventoryScrollableWindowComponent.h" - -class RowMerchantInventoryScrollableWindowComponent:public RowInventoryScrollableWindowComponent{ -public: - inline RowMerchantInventoryScrollableWindowComponent(geom2d::rectrect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,std::functioninventoryButtonHoverAction,std::functioninventoryButtonMouseOutAction,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) - :RowInventoryScrollableWindowComponent(rect,itemNameLabelName,itemDescriptionLabelName,inventoryButtonClickAction,inventoryButtonHoverAction,inventoryButtonMouseOutAction,options,inventoryButtonsActive,attributes){} - - - virtual inline void OnInventorySlotsUpdate(ITCategory cat)override{ - const std::vector>&merchantInv=Merchant::GetCurrentTravelingMerchant().GetShopItems(); - //We only want to refresh the inventory slots if the component count no longer matches what's actually in our inventory. - if(components.size()merchantInv.size()){ //There are empty spots, so let's clean up. - RemoveAllComponents(); - for(std::shared_ptr item:merchantInv){ - AddButtonOnSlotUpdate(cat); - } - } - } - - virtual inline void AddButtonOnSlotUpdate(ITCategory cat)override{ - const std::vector>&merchantInv=Merchant::GetCurrentTravelingMerchant().GetShopItems(); - size_t invSize=components.size()+1; - int invWidth=int(rect.size.x/(float(options.size.x)+options.padding)); - int x=int((invSize-1)%invWidth); - int y=int((invSize-1)/invWidth); - int itemIndex=y*invWidth+x; - - vf2d buttonSize=options.size; - vf2d totalSpacing={options.padding+buttonSize.x,options.padding+buttonSize.y}; - - auto newItem=ADD("merchant_item_"+cat+"_"+std::to_string(itemIndex),RowItemDisplay)({totalSpacing*vf2d{float(x),float(y)},buttonSize},Merchant::GetCurrentTravelingMerchant().GetShopItems()[itemIndex],inventoryButtonClickAction,itemNameLabelName,itemDescriptionLabelName,inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END; - newItem->SetCompactDescriptions(compact==COMPACT); - newItem->SetPriceLabelType(priceLabel); - newItem->SetHoverFunc(inventoryButtonHoverAction); - newItem->SetMouseOutFunc(inventoryButtonMouseOutAction); - } -}; \ No newline at end of file diff --git a/Crawler/Version.h b/Crawler/Version.h index 1200d431..749290fa 100644 --- a/Crawler/Version.h +++ b/Crawler/Version.h @@ -39,7 +39,7 @@ All rights reserved. #define VERSION_MAJOR 0 #define VERSION_MINOR 2 #define VERSION_PATCH 1 -#define VERSION_BUILD 4900 +#define VERSION_BUILD 4919 #define stringify(a) stringify_(a) #define stringify_(a) #a