From 70423be9d45ec8574169cfdb8410be04d2c6c61a Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Fri, 22 Dec 2023 06:14:37 -0600 Subject: [PATCH] Item reference system deprecated in favor of smart pointer system. Converted all items to use pointers instead, removed all reference wrappers for item system. Fixed buggy last item sorting when removing items from InventoryScrollableWindowComponents. Implemented proper inventory removal checks when items are completely removed from an inventory. Sorted inventories now copy the pointer as a shared pointer from the main inventory code instead of needlessly creating extra copies of the same item. --- Crawler/CharacterMenuWindow.cpp | 28 ++-- Crawler/Crawler.cpp | 56 ++++---- Crawler/Crawler.h | 4 +- Crawler/EquipSlotButton.h | 6 +- Crawler/InventoryConsumableWindow.cpp | 2 +- Crawler/InventoryScrollableWindowComponent.h | 5 +- Crawler/Item.cpp | 133 +++++++++--------- Crawler/Item.h | 41 ++++-- Crawler/ItemLoadoutWindow.cpp | 1 - Crawler/MenuComponent.h | 1 + Crawler/MenuItemButton.h | 52 +++---- Crawler/MenuItemItemButton.h | 45 +++--- Crawler/Merchant.cpp | 38 ++--- Crawler/Merchant.h | 8 +- Crawler/MerchantWindow.cpp | 14 +- Crawler/Player.cpp | 6 +- Crawler/RowItemDisplay.h | 29 ++-- ...rchantInventoryScrollableWindowComponent.h | 4 +- Crawler/ScrollableWindowComponent.h | 8 +- Crawler/Version.h | 2 +- 20 files changed, 259 insertions(+), 224 deletions(-) diff --git a/Crawler/CharacterMenuWindow.cpp b/Crawler/CharacterMenuWindow.cpp index 9daac4e4..63c40239 100644 --- a/Crawler/CharacterMenuWindow.cpp +++ b/Crawler/CharacterMenuWindow.cpp @@ -121,20 +121,20 @@ void Menu::InitializeCharacterMenuWindow(){ [&](MenuFuncData data){ EquipSlot slot=EquipSlot(data.component->I(Attribute::EQUIP_TYPE)); - std::vector&equips=Inventory::get("Equipment"); - std::vector&accessories=Inventory::get("Accessories"); - std::vectoravailableEquipment; - std::copy_if(equips.begin(),equips.end(),std::back_inserter(availableEquipment),[&](Item&it){ - return it.GetEquipSlot()&slot; + std::vector>&equips=Inventory::get("Equipment"); + std::vector>&accessories=Inventory::get("Accessories"); + std::vector>availableEquipment; + std::copy_if(equips.begin(),equips.end(),std::back_inserter(availableEquipment),[&](std::shared_ptrit){ + return it->GetEquipSlot()&slot; }); - std::copy_if(accessories.begin(),accessories.end(),std::back_inserter(availableEquipment),[&](Item&it){ - return it.GetEquipSlot()&slot; + std::copy_if(accessories.begin(),accessories.end(),std::back_inserter(availableEquipment),[&](std::shared_ptrit){ + return it->GetEquipSlot()&slot; }); ScrollableWindowComponent*equipList=Component(data.component->parentMenu,"Equip List"); equipList->RemoveAllComponents(); - for(int counter=0;Item&it:availableEquipment){ - Item&itemInvRef=Inventory::GetItem(it.ActualName()); + for(int counter=0;const std::shared_ptrit:availableEquipment){ + std::weak_ptritemInvRef=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); @@ -165,13 +165,13 @@ void Menu::InitializeCharacterMenuWindow(){ [&](MenuFuncData data){ RowItemDisplay*button=dynamic_cast(data.component); if(button!=nullptr){ - const Item&buttonItem=button->GetItem(); + const std::weak_ptrbuttonItem=button->GetItem(); std::vectorstatsBeforeEquip; EquipSlot slot=EquipSlot(button->I(Attribute::EQUIP_TYPE)); for(ItemAttribute attribute:displayAttrs){ statsBeforeEquip.push_back(game->GetPlayer()->GetStat(attribute)); } - Item*equippedItem=Inventory::GetEquip(slot); + std::weak_ptrequippedItem=Inventory::GetEquip(slot); Inventory::EquipItem(buttonItem,slot); for(int counter=0;ItemAttribute attribute:displayAttrs){ StatLabel*statDisplayLabel=Component(CHARACTER_MENU,"Attribute "+ItemAttributable::GetDisplayInfo(attribute).name+" Label"); @@ -180,8 +180,8 @@ void Menu::InitializeCharacterMenuWindow(){ counter++; } Inventory::UnequipItem(slot); - if(*equippedItem!=Item::BLANK){ - Inventory::EquipItem(*equippedItem,slot); + if(!ISBLANK(equippedItem)){ + Inventory::EquipItem(equippedItem,slot); } }else{ ERR("WARNING! Attempting to cast a button that isn't a RowItemDisplay!"); @@ -202,7 +202,7 @@ void Menu::InitializeCharacterMenuWindow(){ equip->SetSelectionType(SelectionType::NONE); equip->I(Attribute::EQUIP_TYPE)=int(slot); - if(Inventory::GetEquip(slot)==&itemInvRef){ + if(Inventory::GetEquip(slot)==itemInvRef){ equip->SetSelected(true); } equip->SetCompactDescriptions(false); diff --git a/Crawler/Crawler.cpp b/Crawler/Crawler.cpp index 1e621a3b..27bbcb0a 100644 --- a/Crawler/Crawler.cpp +++ b/Crawler/Crawler.cpp @@ -1242,7 +1242,7 @@ void Crawler::RenderHud(){ if("debug_player_info"_I){ DrawShadowStringDecal({0,128},player->GetPos().str()); DrawShadowStringDecal({0,136},"Spd: "+std::to_string(player->GetMoveSpdMult())); - DrawShadowStringDecal({0,92},"Loadout Slot 1 Qty: "+std::to_string(GetLoadoutItem(0).Amt())); + DrawShadowStringDecal({0,92},"Loadout Slot 1 Qty: "+std::to_string(GetLoadoutItem(0).lock()->Amt())); DrawShadowStringDecal({0,1},"Selection: "+Menu::menus[INVENTORY_CONSUMABLES]->selection.str()); DrawShadowStringDecal({0,12},"Button Hold Time: "+std::to_string(Menu::menus[INVENTORY_CONSUMABLES]->buttonHoldTime)); }} @@ -1303,16 +1303,19 @@ void Crawler::RenderCooldowns(){ } if(loadoutSlot!=-1){ - uint32_t itemAmt=GetLoadoutItem(loadoutSlot).Amt(); - if(itemAmt>0){ - std::string amtString="x"+std::to_string(itemAmt); - vf2d qtySize=vf2d{GetTextSize(amtString)}*vf2d{0.5f,0.75f}; - DrawShadowStringDecal(pos+vf2d{20,20}-qtySize/2,amtString,WHITE,BLACK,{0.5f,0.75f}); - }else{ - DrawDecal(pos,GFX["square_skill_overlay_icon_empty.png"].Decal(),{1,1},DARK_RED); - shortNameCol=RED; - manaCostShadowCol=DARK_RED; - keyDisplayCol=RED; + const std::weak_ptrloadoutItem=GetLoadoutItem(loadoutSlot); + if(!loadoutItem.expired()&&loadoutItem.lock()->it!=nullptr){ + uint32_t itemAmt=loadoutItem.lock()->Amt(); + if(itemAmt>0){ + std::string amtString="x"+std::to_string(itemAmt); + vf2d qtySize=vf2d{GetTextSize(amtString)}*vf2d{0.5f,0.75f}; + DrawShadowStringDecal(pos+vf2d{20,20}-qtySize/2,amtString,WHITE,BLACK,{0.5f,0.75f}); + }else{ + DrawDecal(pos,GFX["square_skill_overlay_icon_empty.png"].Decal(),{1,1},DARK_RED); + shortNameCol=RED; + manaCostShadowCol=DARK_RED; + keyDisplayCol=RED; + } } } @@ -2210,7 +2213,7 @@ void Crawler::SetChapter(int chapter){ this->chapter=chapter; } -Item&Crawler::GetLoadoutItem(int slot){ +const std::weak_ptrCrawler::GetLoadoutItem(int slot){ if(slot<0||slot>loadout.size()-1)ERR("Invalid inventory slot "+std::to_string(slot)+", please choose a slot in range (0-"+std::to_string(loadout.size()-1)+")."); return loadout[slot]; } @@ -2218,8 +2221,8 @@ Item&Crawler::GetLoadoutItem(int slot){ void Crawler::SetLoadoutItem(int slot,std::string itemName){ if(slot<0||slot>loadout.size()-1)ERR("Invalid inventory slot "+std::to_string(slot)+", please choose a slot in range (0-"+std::to_string(loadout.size()-1)+")."); if(Inventory::GetItemCount(itemName)>0){ - GetLoadoutItem(slot)=Inventory::CopyItem(itemName); - GetLoadoutItem(slot).amt=std::min((uint32_t)"Player.Item Loadout Limit"_I,GetLoadoutItem(slot).Amt()); //We are only allowed to have up to 10 maximum of an item on a journey. + loadout[slot]=Inventory::CopyItem(itemName); + GetLoadoutItem(slot).lock()->amt=std::min((uint32_t)"Player.Item Loadout Limit"_I,GetLoadoutItem(slot).lock()->Amt()); //We are only allowed to have up to 10 maximum of an item on a journey. InputGroup*inputGroup=nullptr; switch(slot){ case 0:{ @@ -2232,16 +2235,15 @@ void Crawler::SetLoadoutItem(int slot,std::string itemName){ inputGroup=&Player::KEY_ITEM3; }break; } - Ability itemAbility{itemName,"","","Item.Item Cooldown Time"_F,0,inputGroup,"items/"+itemName+".png",VERY_DARK_RED,DARK_RED,PrecastData{GetLoadoutItem(slot).CastTime(),0,0},true}; + Ability itemAbility{itemName,"","","Item.Item Cooldown Time"_F,0,inputGroup,"items/"+itemName+".png",VERY_DARK_RED,DARK_RED,PrecastData{GetLoadoutItem(slot).lock()->CastTime(),0,0},true}; switch(slot){ case 0:{ itemAbility.action=[&](Player*p,vf2d pos={}){ - bool itemUsed=game->UseLoadoutItem(0); - - return itemUsed; + return game->UseLoadoutItem(0); }; game->GetPlayer()->SetItem1UseFunc(itemAbility); + Component(MenuType::ITEM_LOADOUT,"Loadout Item 1")->SetItem(loadout[slot]); Component(MenuType::ITEM_LOADOUT,"Loadout Item 1")->UpdateIcon(); }break; case 1:{ @@ -2249,6 +2251,7 @@ void Crawler::SetLoadoutItem(int slot,std::string itemName){ return game->UseLoadoutItem(1); }; game->GetPlayer()->SetItem2UseFunc(itemAbility); + Component(MenuType::ITEM_LOADOUT,"Loadout Item 2")->SetItem(loadout[slot]); Component(MenuType::ITEM_LOADOUT,"Loadout Item 2")->UpdateIcon(); }break; case 2:{ @@ -2256,6 +2259,7 @@ void Crawler::SetLoadoutItem(int slot,std::string itemName){ return game->UseLoadoutItem(2); }; game->GetPlayer()->SetItem3UseFunc(itemAbility); + Component(MenuType::ITEM_LOADOUT,"Loadout Item 3")->SetItem(loadout[slot]); Component(MenuType::ITEM_LOADOUT,"Loadout Item 3")->UpdateIcon(); }break; } @@ -2267,9 +2271,9 @@ void Crawler::SetLoadoutItem(int slot,std::string itemName){ bool Crawler::UseLoadoutItem(int slot){ if(slot<0||slot>loadout.size()-1)ERR("Invalid inventory slot "+std::to_string(slot)+", please choose a slot in range (0-"+std::to_string(loadout.size()-1)+")."); - if(GetLoadoutItem(slot).Amt()>0){ - Inventory::UseItem(GetLoadoutItem(slot).ActualName()); - GetLoadoutItem(slot).amt--; + if(GetLoadoutItem(slot).lock()->Amt()>0){ + Inventory::UseItem(GetLoadoutItem(slot).lock()->ActualName()); + GetLoadoutItem(slot).lock()->amt--; return true; } return false; @@ -2277,7 +2281,7 @@ bool Crawler::UseLoadoutItem(int slot){ void Crawler::ClearLoadoutItem(int slot){ if(slot<0||slot>loadout.size()-1)ERR("Invalid inventory slot "+std::to_string(slot)+", please choose a slot in range (0-"+std::to_string(loadout.size()-1)+")."); - loadout[slot].it=nullptr; + loadout[slot].reset(); InputGroup*inputGroup=nullptr; switch(slot){ case 0:{ @@ -2294,18 +2298,17 @@ void Crawler::ClearLoadoutItem(int slot){ switch(slot){ case 0:{ itemAbility.action=[&](Player*p,vf2d pos={}){ - bool itemUsed=game->UseLoadoutItem(0); - - return itemUsed; + return game->UseLoadoutItem(0); }; game->GetPlayer()->SetItem1UseFunc(itemAbility); + Component(MenuType::ITEM_LOADOUT,"Loadout Item 1")->SetItem(Item::BLANK); Component(MenuType::ITEM_LOADOUT,"Loadout Item 1")->UpdateIcon(); }break; case 1:{ itemAbility.action=[&](Player*p,vf2d pos={}){ return game->UseLoadoutItem(1); }; - game->GetPlayer()->SetItem2UseFunc(itemAbility); + Component(MenuType::ITEM_LOADOUT,"Loadout Item 2")->SetItem(Item::BLANK); Component(MenuType::ITEM_LOADOUT,"Loadout Item 2")->UpdateIcon(); }break; case 2:{ @@ -2313,6 +2316,7 @@ void Crawler::ClearLoadoutItem(int slot){ return game->UseLoadoutItem(2); }; game->GetPlayer()->SetItem3UseFunc(itemAbility); + Component(MenuType::ITEM_LOADOUT,"Loadout Item 3")->SetItem(Item::BLANK); Component(MenuType::ITEM_LOADOUT,"Loadout Item 3")->UpdateIcon(); }break; } diff --git a/Crawler/Crawler.h b/Crawler/Crawler.h index 2e10b659..9f259f96 100644 --- a/Crawler/Crawler.h +++ b/Crawler/Crawler.h @@ -106,7 +106,7 @@ private: bool encounterStarted=false; int totalBossEncounterMobs=0; int chapter=1; //We start at chapter 1. - std::arrayloadout; + std::array,3>loadout; float fadeOutDuration=0; float fadeOutTotalTime=0; float fadeInDuration=0; @@ -195,7 +195,7 @@ public: MapTag GetCurrentMap(); int GetCurrentChapter(); void SetChapter(int chapter); - Item&GetLoadoutItem(int slot); + const std::weak_ptrGetLoadoutItem(int slot); void SetLoadoutItem(int slot,std::string itemName); //Returns true if the item can be used (we have >0 of it) bool UseLoadoutItem(int slot); diff --git a/Crawler/EquipSlotButton.h b/Crawler/EquipSlotButton.h index 52a66bcd..c65d8563 100644 --- a/Crawler/EquipSlotButton.h +++ b/Crawler/EquipSlotButton.h @@ -50,9 +50,9 @@ public: inline EquipSlotButton(geom2d::rectrect,EquipSlot slot,MenuType menuDest,MenuFunc onClick,MenuFunc onHover,MenuFunc onMouseOut,std::string itemNameLabelName="",std::string itemDescriptionLabelName="") :MenuItemItemButton(rect,Item::BLANK,menuDest,onClick,onHover,onMouseOut,itemNameLabelName,itemDescriptionLabelName),slot(slot){} inline void OnEquipStatsUpdate()override{ - Item&equip=*Inventory::GetEquip(slot); - if(!equip.IsBlank()){ - icon=const_cast(equip.Decal()); + const std::weak_ptrequip=Inventory::GetEquip(slot); + if(!ISBLANK(equip)){ + icon=const_cast(equip.lock()->Decal()); SetItem(equip); }else{ icon=nullptr; diff --git a/Crawler/InventoryConsumableWindow.cpp b/Crawler/InventoryConsumableWindow.cpp index 7f56edd5..2432d10b 100644 --- a/Crawler/InventoryConsumableWindow.cpp +++ b/Crawler/InventoryConsumableWindow.cpp @@ -75,7 +75,7 @@ void Menu::InitializeConsumableInventoryWindow(){ } } button->selected=data.menu.I(A::LOADOUT_SLOT); - data.game->SetLoadoutItem(button->selected,button->GetItem().ActualName()); + data.game->SetLoadoutItem(button->selected,button->GetItem().lock()->ActualName()); return true; })END; Menu::AddInventoryListener(consumableWindow,"Consumables"); diff --git a/Crawler/InventoryScrollableWindowComponent.h b/Crawler/InventoryScrollableWindowComponent.h index 356765e8..93288265 100644 --- a/Crawler/InventoryScrollableWindowComponent.h +++ b/Crawler/InventoryScrollableWindowComponent.h @@ -110,9 +110,10 @@ protected: for(int j=i;jparentMenu]->components.at(components[j]->name)=components[size_t(j+1)]; - components[j]=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=Menu::menus[components[components.size()-1]->parentMenu]->components.at(components[components.size()-1]->name); + 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. diff --git a/Crawler/Item.cpp b/Crawler/Item.cpp index a549edc8..4b9f5fdb 100644 --- a/Crawler/Item.cpp +++ b/Crawler/Item.cpp @@ -49,20 +49,21 @@ INCLUDE_GFX safemapITEM_DATA; safemapITEM_SCRIPTS; safemap>ITEM_CATEGORIES; -Item Item::BLANK; -std::mapInventory::_inventory; -std::map>Inventory::sortedInv; +std::shared_ptrItem::BLANK=std::make_shared(); +std::map>Inventory::_inventory; +std::map>>Inventory::sortedInv; std::vectorItemOverlay::items; std::mapItemSet::sets; -std::mapInventory::equipment; +std::map>Inventory::equipment; std::mapItemInfo::nameToEquipSlot; +int Item::IsBlankStaticCallCounter=0; ItemInfo::ItemInfo() :customProps({nullptr,nullptr}),img(nullptr){} void ItemInfo::InitializeItems(){ for(int i=int(EquipSlot::HELMET);i<=int(EquipSlot::RING2);i<<=1){ - Inventory::equipment[EquipSlot(i)]=&Item::BLANK; + Inventory::equipment[EquipSlot(i)]=Item::BLANK; } nameToEquipSlot["Helmet"]=EquipSlot::HELMET; @@ -251,20 +252,20 @@ void Inventory::AddItem(IT it,uint32_t amt,bool monsterDrop){ if(!ITEM_DATA.count(it))ERR("Item "<(amt,it); InsertIntoSortedInv(it); }else{ - _inventory.at(it).amt+=amt; + _inventory.at(it)->amt+=amt; } InsertIntoStageInventoryCategory(it,amt,monsterDrop); } -Item Inventory::CopyItem(IT it){ - if(!_inventory.count(it))return Item::BLANK; - return _inventory.at(it); +std::shared_ptrInventory::CopyItem(IT it){ + if(!_inventory.count(it))return std::make_shared(*Item::BLANK); + return std::make_shared(*_inventory.at(it)); } -Item&Inventory::GetItem(IT it){ +std::weak_ptrInventory::GetItem(IT it){ if(!_inventory.count(it))return Item::BLANK; return _inventory.at(it); } @@ -273,7 +274,7 @@ uint32_t Inventory::GetItemCount(IT it){ if(!_inventory.count(it)){ return 0; }else{ - return _inventory.at(it).Amt(); + return _inventory.at(it)->Amt(); } } @@ -292,7 +293,7 @@ bool Inventory::UseItem(IT it,uint32_t amt){ //Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory. bool Inventory::RemoveItem(IT it,ITCategory inventory,uint32_t amt){ #pragma region Calculate Inventory to Manipulate - std::vector&inv=sortedInv.at(inventory); + std::vector>&inv=sortedInv.at(inventory); bool eraseFromLootWindow=false; if(inventory=="Monster Loot") { inv=sortedInv.at("Monster Loot"); @@ -303,7 +304,7 @@ bool Inventory::RemoveItem(IT it,ITCategory inventory,uint32_t amt){ eraseFromLootWindow=true; } int count=0; - for(Item&item:inv){ + for(std::shared_ptritem:inv){ if(item==it)break; count++; } @@ -311,27 +312,26 @@ bool Inventory::RemoveItem(IT it,ITCategory inventory,uint32_t amt){ uint32_t itemAmt=GetItemCount(it); if(inventory=="Monster Loot"||inventory=="Stage Loot"){ - itemAmt=inv.at(count).Amt(); + itemAmt=inv.at(count)->Amt(); } //There are two places to manipulate items in (Both the sorted inventory and the actual inventory) if (!itemAmt)return false; if (amt>=itemAmt){ - - if(!eraseFromLootWindow){ + inv.erase(inv.begin()+count); //Clears it from the detected sorted inventory as well! + if(!eraseFromLootWindow){ //We must clear out the item AFTER we've updated context-sensitive inventories because they may be borrowing a ref from this structure!!! _inventory.erase(it); - }else{ - inv.erase(inv.begin()+count); } + //Callback for GUI inventories. Menu::InventorySlotsUpdated(inventory); return true; }else{ if(!eraseFromLootWindow){ - _inventory.at(it).amt-=amt; + _inventory.at(it)->amt-=amt; }else{ - inv.at(count).amt-=amt; + inv.at(count)->amt-=amt; //Don't touch the sorted inventory unless this is monster loot or stage loot because there's only "1" of this item in the entry list. } return false; } @@ -343,12 +343,12 @@ bool Inventory::RemoveItem(IT it,uint32_t amt){ return RemoveItem(it, cat, amt); } -std::vector&Inventory::get(ITCategory itemCategory){ +std::vector>&Inventory::get(ITCategory itemCategory){ return sortedInv.at(itemCategory); } void Inventory::InsertIntoSortedInv(IT item){ - sortedInv.at(ITEM_DATA[item].category).push_back(Item{1,item}); + sortedInv.at(ITEM_DATA[item].category).push_back(_inventory[item]); //This should be a callback to menus that we need to update the interface with another item slot since a new one has appeared. Menu::InventorySlotsUpdated(ITEM_DATA[item].category); } @@ -358,12 +358,12 @@ void Inventory::InsertIntoStageInventoryCategory(IT item,uint32_t amt,bool monst if(monsterDrop){ stageInventoryCategory="Monster Loot"; } - std::vector&inv=sortedInv.at(stageInventoryCategory); - std::vector::iterator it=std::find(inv.begin(),inv.end(),Item{amt,item}); + std::vector>&inv=sortedInv.at(stageInventoryCategory); + std::vector>::iterator it=std::find(inv.begin(),inv.end(),std::make_shared(amt,item)); if(it!=inv.end()){ - (*it).amt+=amt; + (*it)->amt+=amt; }else{ - inv.push_back(Item{amt,item}); + inv.push_back(std::make_shared(amt,item)); } Menu::InventorySlotsUpdated(stageInventoryCategory); } @@ -377,14 +377,14 @@ bool Inventory::ExecuteAction(IT item){ } bool Inventory::SwapItems(ITCategory itemCategory,uint32_t slot1,uint32_t slot2){ - std::vector&inv=sortedInv.at(itemCategory); + std::vector>&inv=sortedInv.at(itemCategory); int largestSlot=std::max(slot1,slot2); if(inv.size()<=largestSlot){ //The inventory is too small, so expand out blank slots to accomodate. inv.resize(largestSlot+size_t(1)); } - Item&item1=inv.at(slot1); - Item&item2=inv.at(slot2); + std::shared_ptritem1=inv.at(slot1); + std::shared_ptritem2=inv.at(slot2); std::swap(item1,item2); return true; } @@ -393,11 +393,11 @@ uint32_t Item::Amt()const{ return amt; }; const std::string&Item::ActualName()const{ - if(IsBlank())return ""; + if(_IsBlank())return ""; return it->Name(); }; const std::string Item::DisplayName()const{ - if(IsBlank())return ""; + if(_IsBlank())return ""; std::string name=ActualName(); if(IsEquippable()&&EnhancementLevel()>0){ name+=" [+"+std::to_string(EnhancementLevel())+"]"; @@ -465,28 +465,22 @@ const ItemScript&ItemInfo::OnUseAction()const{ }; const bool Item::IsBlank()const{ + if(Item::IsBlankStaticCallCounter!=1)ERR("WARNING! You should not call the IsBlank() function directly! Use ISBLANK() macro instead!") + Item::IsBlankStaticCallCounter--; return amt==0||it==nullptr; } void Inventory::Clear(ITCategory itemCategory){ - std::vectoritemList=get(itemCategory); //We have to make a copy here because RemoveItem() will modify the list provided by get() inline. - for(Item&item:itemList){ - size_t itemQuantity=GetItemCount(item.ActualName());//Normally we want to clear all the items that are actually in our inventory...But... + std::vector>itemList=get(itemCategory); //We have to make a copy here because RemoveItem() will modify the list provided by get() inline. + for(std::shared_ptr&item:itemList){ + size_t itemQuantity=GetItemCount(item->ActualName());//Normally we want to clear all the items that are actually in our inventory...But... if(itemCategory=="Monster Loot"||itemCategory=="Stage Loot"){//These do not affect the actual inventory, we just clear the lists. - itemQuantity=item.Amt(); + itemQuantity=item->Amt(); } - RemoveItem(item.ActualName(),itemCategory,uint32_t(itemQuantity)); + RemoveItem(item->ActualName(),itemCategory,uint32_t(itemQuantity)); } } -const bool Item::operator==(const Item&rhs)const{ - return it==rhs.it; -} - -const bool Item::operator==(const IT&rhs)const{ - return it->Name()==rhs; -} - ItemOverlay::ItemOverlay(ItemInfo item) :it(item),width("ItemDrop.Item Drop Scale"_F*24+4+game->GetTextSizeProp(item.Name()).x*0.5f){ xOffset=-width; @@ -564,28 +558,28 @@ void ItemSet::AddSetBonus(std::string setName,int pieceCount,Stats&bonuses){ sets[setName].setBonusBreakpoints.push_back({pieceCount,bonuses}); } -void Inventory::EquipItem(const Item&it,EquipSlot slot){ - if(!(it.it->slot&slot))return; +void Inventory::EquipItem(const std::weak_ptrit,EquipSlot slot){ + if(!(it.lock()->it->slot&slot))return; EquipSlot equippedSlot=GetSlotEquippedIn(it); if(equippedSlot!=EquipSlot::NONE)UnequipItem(equippedSlot); - if(GetEquip(slot)!=nullptr)UnequipItem(slot); - Inventory::equipment[slot]=const_cast(&it); + if(!GetEquip(slot).expired())UnequipItem(slot); + Inventory::equipment[slot]=it; PlayerStats::RecalculateEquipStats(); }; void Inventory::UnequipItem(EquipSlot slot){ - Inventory::equipment[slot]=&Item::BLANK; + Inventory::equipment[slot]=Item::BLANK; PlayerStats::RecalculateEquipStats(); }; -EquipSlot Inventory::GetSlotEquippedIn(const Item&it){ +EquipSlot Inventory::GetSlotEquippedIn(const std::weak_ptrit){ for(int i=int(EquipSlot::HELMET);i<=int(EquipSlot::RING2);i<<=1){ EquipSlot slot=EquipSlot(i); - Item*equip=GetEquip(slot); - if(equip==nullptr)continue; - if(equip->ActualName()==it.ActualName())return slot; + std::weak_ptrequip=GetEquip(slot); + if(equip.expired())continue; + if(equip.lock()->ActualName()==it.lock()->ActualName())return slot; } return EquipSlot::NONE; }; -Item*Inventory::GetEquip(EquipSlot slot){ +std::weak_ptrInventory::GetEquip(EquipSlot slot){ return Inventory::equipment[slot]; } const EquipSlot Item::GetEquipSlot()const{ @@ -657,10 +651,10 @@ const std::mapInventory::GetEquippedItemSets(){ std::mapsetCounts; for(int i=int(EquipSlot::HELMET);i<=int(EquipSlot::RING2);i<<=1){ EquipSlot slot=EquipSlot(i); - Item*equip=Inventory::GetEquip(slot); - if(equip->IsBlank())continue; - if(equip->ItemSet()){ - setCounts[*equip->ItemSet().value()]++; + std::weak_ptrequip=Inventory::GetEquip(slot); + if(ISBLANK(equip))continue; + if(equip.lock()->ItemSet()){ + setCounts[*equip.lock()->ItemSet().value()]++; } } return setCounts; @@ -713,11 +707,24 @@ const bool Item::CanBePurchased()const{ return it->CanBePurchased(); } - void Item::SetAmt(uint32_t newAmt){ amt=newAmt; } -const Item&Inventory::GetInventorySlot(ITCategory itemCategory,size_t slot){ - return GetItem(get(itemCategory)[slot].ActualName()); -} \ No newline at end of file +const std::weak_ptrInventory::GetInventorySlot(ITCategory itemCategory,size_t slot){ + return GetItem(get(itemCategory).at(slot)->ActualName()); +} + +bool Item::IsBlank(std::shared_ptritem){ + Item::IsBlankStaticCallCounter++; + return item->IsBlank(); +}; +bool Item::IsBlank(std::weak_ptritem){ + if(!item.expired())Item::IsBlankStaticCallCounter++;else return true; + return item.lock()->IsBlank(); +}; + +const bool Item::_IsBlank()const{ + Item::IsBlankStaticCallCounter++; + return IsBlank(); +}; \ No newline at end of file diff --git a/Crawler/Item.h b/Crawler/Item.h index ae7a833c..4b2ecbd2 100644 --- a/Crawler/Item.h +++ b/Crawler/Item.h @@ -138,6 +138,8 @@ private: uint8_t enhancementLevel; ItemInfo*it; void SetAmt(uint32_t newAmt); + static int IsBlankStaticCallCounter; + const bool _IsBlank()const; public: Item(); Item(uint32_t amt,IT item,uint8_t enhancementLevel=0); @@ -154,18 +156,27 @@ public: const ItemScript&OnUseAction()const; const float CastTime()const; const float CooldownTime()const; + //Use ISBLANK macro instead!! This should not be called directly!! const bool IsBlank()const; const uint8_t EnhancementLevel()const; void EnhanceItem(); - static Item BLANK; - const bool operator==(const Item&rhs)const; - const bool operator==(const IT&rhs)const; + static std::shared_ptrBLANK; const std::optionalItemSet()const; const bool IsEquippable()const; const uint32_t BuyValue()const; const uint32_t SellValue()const; const bool CanBeSold()const; const bool CanBePurchased()const; + //Use ISBLANK macro instead!! This should not be called directly!! + static bool IsBlank(std::shared_ptritem); + //Use ISBLANK macro instead!! This should not be called directly!! + static bool IsBlank(std::weak_ptritem); + friend const bool operator==(std::shared_ptrlhs,std::shared_ptrrhs){return lhs->it==rhs->it;}; + friend const bool operator==(std::shared_ptrlhs,const IT&rhs){return lhs->ActualName()==rhs;}; + friend const bool operator==(std::weak_ptrlhs,std::weak_ptrrhs){return !lhs.expired()&&!rhs.expired()&&lhs.lock()->it==rhs.lock()->it;}; + friend const bool operator==(std::weak_ptrlhs,const IT&rhs){return !lhs.expired()&&lhs.lock()->ActualName()==rhs;}; + friend const bool operator==(const IT&lhs,std::weak_ptrrhs){return operator==(rhs,lhs);}; + friend const bool operator==(const IT&lhs,std::shared_ptrrhs){return operator==(rhs,lhs);}; }; class Inventory{ @@ -175,19 +186,19 @@ public: static void AddItem(IT it,uint32_t amt=1,bool monsterDrop=false); //Returns the actual amount available in your main inventory. static uint32_t GetItemCount(IT it); - static Item CopyItem(IT it); - static Item&GetItem(IT it); + static std::shared_ptrCopyItem(IT it); + static std::weak_ptrGetItem(IT it); //Auto-executes its use function and removes the amt specified from the inventory. Multiple amounts will cause the item to execute its useFunc multiple times. static bool UseItem(IT it,uint32_t amt=1); static bool RemoveItem(IT it,ITCategory inventory,uint32_t amt = 1); static bool RemoveItem(IT it,uint32_t amt=1); - static std::vector&get(ITCategory itemCategory); - static const Item&GetInventorySlot(ITCategory itemCategory,size_t slot); + static std::vector>&get(ITCategory itemCategory); + static const std::weak_ptrGetInventorySlot(ITCategory itemCategory,size_t slot); static void Clear(ITCategory itemCategory); - static void EquipItem(const Item&it,EquipSlot slot); + static void EquipItem(const std::weak_ptrit,EquipSlot slot); static void UnequipItem(EquipSlot slot); - static EquipSlot GetSlotEquippedIn(const Item&it); - static Item*GetEquip(EquipSlot slot); + static EquipSlot GetSlotEquippedIn(const std::weak_ptrit); + static std::weak_ptrGetEquip(EquipSlot slot); static const std::mapGetEquippedItemSets(); static bool SwapItems(ITCategory itemCategory,uint32_t slot1,uint32_t slot2); @@ -200,10 +211,10 @@ private: static void InsertIntoSortedInv(IT item); static void InsertIntoStageInventoryCategory(IT item,uint32_t amt,bool monsterDrop); static bool ExecuteAction(IT item); - static std::map_inventory; - static std::mapequipment; + static std::map>_inventory; + static std::map>equipment; //Only contains "1" of every item, as this is a map to index items and not the actual storage of items! - static std::map>sortedInv; + static std::map>>sortedInv; }; class ItemProps{ @@ -274,4 +285,6 @@ public: static void Update(); static void Draw(); void ResetTimer(); -}; \ No newline at end of file +}; + +#define ISBLANK(itemRef) Item::IsBlank(itemRef) \ No newline at end of file diff --git a/Crawler/ItemLoadoutWindow.cpp b/Crawler/ItemLoadoutWindow.cpp index 50e47f3c..b02680bf 100644 --- a/Crawler/ItemLoadoutWindow.cpp +++ b/Crawler/ItemLoadoutWindow.cpp @@ -50,7 +50,6 @@ 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; float buttonBorderPadding=64; diff --git a/Crawler/MenuComponent.h b/Crawler/MenuComponent.h index 59d9f602..2db261a8 100644 --- a/Crawler/MenuComponent.h +++ b/Crawler/MenuComponent.h @@ -109,6 +109,7 @@ protected: bool valid=true; //If set to false, this would cause the component to be removed. bool fitToLabel=false; //Will shrink text horizontally to fit the size of the label if the display text is too large. bool grayedOut=false; //If turned on, a button will appear grayed out and unresponsive. + int inventoryIndex=0; vf2d labelScaling={1,1}; virtual void Update(Crawler*game); virtual void DrawDecal(ViewPort&window,bool focused); diff --git a/Crawler/MenuItemButton.h b/Crawler/MenuItemButton.h index cefbfe40..b29f8954 100644 --- a/Crawler/MenuItemButton.h +++ b/Crawler/MenuItemButton.h @@ -50,40 +50,52 @@ INCLUDE_game INCLUDE_ITEM_DATA class MenuItemButton:public MenuIconButton{ + friend class InventoryScrollableWindowComponent; private: - std::vector&invRef; - int inventoryIndex=0; + std::vector>&invRef; MenuType itemDescriptionMenu; std::string itemNameLabelName; std::string itemDescriptionLabelName; CompactText compact=COMPACT; public: int selected=-1; //0-2 representing which loadout slot this item consumes. -1 means not selected. - inline MenuItemButton(geom2d::rectrect,std::vector&invRef,int invIndex,MenuFunc onClick,MenuType itemDescriptionMenu,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE) - :MenuIconButton(rect,invRef.size()>invIndex?const_cast(invRef[invIndex].Decal()):nullptr,onClick,attributes),invRef(invRef),inventoryIndex(invIndex),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ + inline MenuItemButton(geom2d::rectrect,std::vector>&invRef,int invIndex,MenuFunc onClick,MenuType itemDescriptionMenu,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE) + :MenuIconButton(rect,invRef.size()>invIndex?const_cast(invRef[invIndex]->Decal()):nullptr,onClick,attributes),invRef(invRef),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ draggable=false; + inventoryIndex=invIndex; valid=invRef.size()>invIndex; } - inline MenuItemButton(geom2d::rectrect,std::vector&invRef,int invIndex,MenuFunc onClick,MenuFunc onHover,MenuFunc onMouseOut,MenuType itemDescriptionMenu,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE) - :MenuIconButton(rect,invRef.size()>invIndex?const_cast(invRef[invIndex].Decal()):nullptr,onClick,attributes),invRef(invRef),inventoryIndex(invIndex),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ + inline MenuItemButton(geom2d::rectrect,std::vector>&invRef,int invIndex,MenuFunc onClick,MenuFunc onHover,MenuFunc onMouseOut,MenuType itemDescriptionMenu,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE) + :MenuIconButton(rect,invRef.size()>invIndex?const_cast(invRef[invIndex]->Decal()):nullptr,onClick,attributes),invRef(invRef),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ draggable=false; + inventoryIndex=invIndex; runHoverFunctions=true; valid=invRef.size()>invIndex; SetHoverFunc(onHover); SetMouseOutFunc(onMouseOut); } - inline Item&GetItem(){ - return Inventory::GetItem(invRef.at(inventoryIndex).ActualName()); + inline std::weak_ptrGetItem(){ + return Inventory::GetItem(invRef.at(inventoryIndex)->ActualName()); } //Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory. inline bool UseItem(uint32_t amt=1){ if(invRef.size()<=inventoryIndex)return false; - return Inventory::UseItem(invRef.at(inventoryIndex).ActualName(),amt); + return Inventory::UseItem(invRef.at(inventoryIndex)->ActualName(),amt); } inline void SetCompactDescriptions(bool compact){ if(compact)this->compact=COMPACT; else this->compact=NON_COMPACT; } + inline virtual void Update(Crawler*game)override{ + MenuIconButton::Update(game); + valid=invRef.size()>inventoryIndex&&ITEM_DATA.count(invRef[inventoryIndex]->ActualName())&&invRef[inventoryIndex]->Amt()>0; + if(!valid){ + icon=nullptr; + } + if(hovered){ + UpdateLabel(); + } + } protected: virtual inline void OnMouseOut()override{ if(itemNameLabelName!=""){ @@ -94,13 +106,13 @@ protected: } } void UpdateLabel(){ - if(invRef[inventoryIndex].IsBlank())return; + if(ISBLANK(invRef[inventoryIndex]))return; std::string labelNameText; std::string labelDescriptionText; if(valid){ - icon=const_cast(invRef[inventoryIndex].Decal()); - labelNameText=invRef[inventoryIndex].DisplayName(); - labelDescriptionText=invRef[inventoryIndex].Description(compact); + icon=const_cast(invRef[inventoryIndex]->Decal()); + labelNameText=invRef[inventoryIndex]->DisplayName(); + labelDescriptionText=invRef[inventoryIndex]->Description(compact); }else{ icon=nullptr; labelNameText=""; @@ -118,23 +130,15 @@ protected: virtual inline void OnHover()override{ UpdateLabel(); } - virtual inline void Update(Crawler*game)override{ - MenuIconButton::Update(game); - valid=invRef.size()>inventoryIndex&&ITEM_DATA.count(invRef[inventoryIndex].ActualName()); - - if(hovered){ - UpdateLabel(); - } - } virtual inline void DrawDecal(ViewPort&window,bool focused)override{ MenuIconButton::DrawDecal(window,focused); if(selected!=-1){ drawutil::DrawCrosshairDecalViewPort(window,{rect.pos,rect.size},0); } if(valid){ - int itemQuantity=Inventory::GetItemCount(invRef.at(inventoryIndex).ActualName()); //Normally we'd retrieve how many of this item we have from our inventory...However Monster Loot and Stage Loot inventories are special and hold their own inventory counts... + int itemQuantity=Inventory::GetItemCount(invRef.at(inventoryIndex)->ActualName()); //Normally we'd retrieve how many of this item we have from our inventory...However Monster Loot and Stage Loot inventories are special and hold their own inventory counts... if(&invRef==&Inventory::get("Monster Loot")||&invRef==&Inventory::get("Stage Loot")){ - itemQuantity=invRef.at(inventoryIndex).Amt(); //So the item quantity comes from the stack itself and not our main inventory. + itemQuantity=invRef.at(inventoryIndex)->Amt(); //So the item quantity comes from the stack itself and not our main inventory. } std::string quantityText="x"+std::to_string(itemQuantity); vf2d quantityTextScale=rect.size/48.f; @@ -158,7 +162,7 @@ protected: inline bool DropDraggableItem(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; - ITCategory cat=draggedItem->invRef.at(draggedItem->inventoryIndex).Category(); + ITCategory cat=draggedItem->invRef.at(draggedItem->inventoryIndex)->Category(); return Inventory::SwapItems(cat,draggedItem->inventoryIndex,inventoryIndex); } }; diff --git a/Crawler/MenuItemItemButton.h b/Crawler/MenuItemItemButton.h index 68eb8c77..1bb8f327 100644 --- a/Crawler/MenuItemItemButton.h +++ b/Crawler/MenuItemItemButton.h @@ -50,30 +50,30 @@ INCLUDE_ITEM_DATA class MenuItemItemButton:public MenuIconButton{ private: - std::reference_wrapperitemRef; + std::weak_ptritemRef; std::string itemNameLabelName; std::string itemDescriptionLabelName; bool hideQty=false; CompactText compact=COMPACT; public: - inline MenuItemItemButton(geom2d::rectrect,const Item&itemRef,MenuType menuDest,MenuFunc onClick,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE) - :MenuIconButton(rect,(!itemRef.IsBlank())?const_cast(itemRef.Decal()):nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ + inline MenuItemItemButton(geom2d::rectrect,const std::weak_ptritemRef,MenuType menuDest,MenuFunc onClick,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE) + :MenuIconButton(rect,!ISBLANK(itemRef)?const_cast(itemRef.lock()->Decal()):nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ draggable=false; - valid=!itemRef.IsBlank(); + valid=!ISBLANK(itemRef); } - inline MenuItemItemButton(geom2d::rectrect,const Item&itemRef,MenuType menuDest,MenuFunc onClick,MenuFunc onHover,MenuFunc onMouseOut,std::string itemNameLabelName="",std::string itemDescriptionLabelName="",IconButtonAttr attributes=IconButtonAttr::SELECTABLE) - :MenuIconButton(rect,(!itemRef.IsBlank())?const_cast(itemRef.Decal()):nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ + inline MenuItemItemButton(geom2d::rectrect,const std::weak_ptritemRef,MenuType menuDest,MenuFunc onClick,MenuFunc onHover,MenuFunc onMouseOut,std::string itemNameLabelName="",std::string itemDescriptionLabelName="",IconButtonAttr attributes=IconButtonAttr::SELECTABLE) + :MenuIconButton(rect,!ISBLANK(itemRef)?const_cast(itemRef.lock()->Decal()):nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ runHoverFunctions=true; draggable=false; - valid=!itemRef.IsBlank(); + valid=!ISBLANK(itemRef); SetHoverFunc(onHover); SetMouseOutFunc(onMouseOut); } - inline const Item&GetItem(){ - return itemRef.get(); + inline const std::weak_ptrGetItem(){ + return itemRef; } - inline const Item&SetItem(const Item&newItem){ - return itemRef=std::reference_wrapper(newItem); + inline const std::weak_ptrSetItem(const std::weak_ptrnewItem){ + return itemRef=newItem; } inline void SetShowQuantity(bool show){ this->hideQty=!show; @@ -83,11 +83,11 @@ public: else this->compact=NON_COMPACT; } inline void UpdateIcon(){ - if(itemRef.get().IsBlank()){ + if(ISBLANK(itemRef)){ icon=nullptr; return; } - icon=const_cast(itemRef.get().Decal()); + icon=const_cast(itemRef.lock()->Decal()); } protected: virtual inline void OnMouseOut()override{ @@ -105,16 +105,16 @@ protected: std::string labelNameText; std::string labelDescriptionText; - if(itemRef.get().IsBlank()){ + if(ISBLANK(itemRef)){ icon=nullptr; labelNameText=""; labelDescriptionText=""; return; } - icon=const_cast(itemRef.get().Decal()); - labelNameText=itemRef.get().DisplayName(); - labelDescriptionText=itemRef.get().Description(compact); + icon=const_cast(itemRef.lock()->Decal()); + labelNameText=itemRef.lock()->DisplayName(); + labelDescriptionText=itemRef.lock()->Description(compact); if(itemNameLabelName!=""){ Component(parentMenu,itemNameLabelName)->label=labelNameText; Component(parentMenu,itemNameLabelName)->Enable(true); @@ -126,8 +126,11 @@ protected: } virtual inline void Update(Crawler*game)override{ MenuIconButton::Update(game); - valid=!itemRef.get().IsBlank(); - + valid=!ISBLANK(itemRef); + + if(!valid){ + icon=nullptr; + } if(hovered){ UpdateLabel(); } @@ -135,11 +138,11 @@ protected: virtual inline void DrawDecal(ViewPort&window,bool focused)override{ MenuIconButton::DrawDecal(window,focused); if(valid&&!hideQty){ - std::string quantityText="x"+std::to_string(itemRef.get().Amt()); + std::string quantityText="x"+std::to_string(itemRef.lock()->Amt()); vf2d quantityTextScale=rect.size/48.f; vf2d textSize=vf2d(game->GetTextSizeProp(quantityText))*quantityTextScale; vf2d drawPos=rect.pos+rect.size-textSize; - if(itemRef.get().Amt()!=INFINITE){ + if(itemRef.lock()->Amt()!=INFINITE){ window.DrawShadowStringDecal(drawPos,quantityText,WHITE,BLACK,quantityTextScale); } } diff --git a/Crawler/Merchant.cpp b/Crawler/Merchant.cpp index a2adb62b..5ad7ec93 100644 --- a/Crawler/Merchant.cpp +++ b/Crawler/Merchant.cpp @@ -44,7 +44,7 @@ INCLUDE_game INCLUDE_ITEM_CATEGORIES std::map>Merchant::merchants; -std::map>Merchant::sortedItems; +std::map>>Merchant::sortedItems; MerchantFunctionPrimingData Merchant::purchaseFunctionPrimed("CanPurchaseItem()"); MerchantFunctionPrimingData Merchant::sellFunctionPrimed("CanSellItem()"); Merchant Merchant::travelingMerchant; @@ -58,11 +58,11 @@ const std::string&Merchant::GetDisplayName()const{ return displayName; } -const std::vector&Merchant::GetShopItems()const{ +const std::vector>&Merchant::GetShopItems()const{ return shopItems; } -const std::vector&Merchant::GetShopItems(ITCategory category){ +const std::vector>&Merchant::GetShopItems(ITCategory category){ return sortedItems[category]; } @@ -72,7 +72,7 @@ Merchant&Merchant::AddMerchant(Chapter chapter){ } void Merchant::AddItem(IT item,uint32_t amt,uint8_t enhancementLevel){ - shopItems.push_back(Item{amt,item,enhancementLevel}); + shopItems.push_back(std::make_shared(amt,item,enhancementLevel)); if(&GetCurrentTravelingMerchant()==this){ UpdateSortedItemsList(); Menu::MerchantInventorySlotsUpdated(ITEM_DATA[item].Category()); @@ -84,8 +84,8 @@ void Merchant::UpdateSortedItemsList(){ for(auto&[key,items]:ITEM_CATEGORIES){ sortedItems[key].clear(); } - std::for_each(merchant.shopItems.begin(),merchant.shopItems.end(),[](const Item&item){ - sortedItems[item.Category()].push_back(const_cast(&item)); + std::for_each(merchant.shopItems.begin(),merchant.shopItems.end(),[](const std::shared_ptritem){ + sortedItems[item->Category()].push_back(item); }); } @@ -132,11 +132,11 @@ void Merchant::Initialize(){ } bool Merchant::CanPurchaseItem(IT item,uint32_t amt)const{ bool itemAvailable=false; - const Item*foundItem=nullptr; - for(const Item&it:shopItems){ - if(it==item&&it.Amt()>=amt&&it.CanBePurchased()){ + std::weak_ptrfoundItem; + for(const std::shared_ptrit:shopItems){ + if(it==item&&it->Amt()>=amt&&it->CanBePurchased()){ itemAvailable=true; - foundItem=⁢ + foundItem=it; break; } } @@ -145,8 +145,8 @@ bool Merchant::CanPurchaseItem(IT item,uint32_t amt)const{ purchaseFunctionPrimed.item=item; return purchaseFunctionPrimed= itemAvailable&& - foundItem!=nullptr&& - game->GetPlayer()->GetMoney()>=foundItem->BuyValue()*amt; + !foundItem.expired()&& + game->GetPlayer()->GetMoney()>=foundItem.lock()->BuyValue()*amt; }; bool Merchant::CanSellItem(IT item,uint32_t amt)const{ const ItemInfo&it=ITEM_DATA[item]; @@ -161,12 +161,12 @@ void Merchant::PurchaseItem(IT item,uint32_t amt){ purchaseFunctionPrimed.Validate(item,amt); uint32_t totalCost=0U; - for(Item&it:shopItems){ + for(std::shared_ptrit:shopItems){ if(it==item){ - if(it.Amt()!=INFINITE){ - it.SetAmt(it.Amt()-amt); + if(it->Amt()!=INFINITE){ + it->SetAmt(it->Amt()-amt); } - totalCost=it.BuyValue()*amt; + totalCost=it->BuyValue()*amt; break; } } @@ -181,10 +181,10 @@ void Merchant::SellItem(IT item,uint32_t amt){ uint32_t totalCost=0U; bool itemFound=false; - for(Item&it:shopItems){ + for(std::shared_ptrit:shopItems){ if(it==item){ - if(it.Amt()!=INFINITE){ - it.SetAmt(it.Amt()+amt); + if(it->Amt()!=INFINITE){ + it->SetAmt(it->Amt()+amt); } itemFound=true; break; diff --git a/Crawler/Merchant.h b/Crawler/Merchant.h index f110b035..a2b244f7 100644 --- a/Crawler/Merchant.h +++ b/Crawler/Merchant.h @@ -49,8 +49,8 @@ public: static void RandomizeTravelingMerchant(); static Merchant&GetCurrentTravelingMerchant(); const std::string&GetDisplayName()const; - const std::vector&GetShopItems()const; - const static std::vector&GetShopItems(ITCategory category); + const std::vector>&GetShopItems()const; + const static std::vector>&GetShopItems(ITCategory category); void AddItem(IT item,uint32_t amt=1,uint8_t enhancementLevel=0U); bool CanPurchaseItem(IT item,uint32_t amt=1U)const; bool CanSellItem(IT item,uint32_t amt=1U)const; @@ -66,7 +66,7 @@ private: static const Merchant&GetRandomMerchant(Chapter chapter); static std::map>merchants; std::string displayName; - std::vectorshopItems; - static std::map>sortedItems; + std::vector>shopItems; + static std::map>>sortedItems; static void UpdateSortedItemsList(); }; \ No newline at end of file diff --git a/Crawler/MerchantWindow.cpp b/Crawler/MerchantWindow.cpp index 8e644b41..749d0bf5 100644 --- a/Crawler/MerchantWindow.cpp +++ b/Crawler/MerchantWindow.cpp @@ -64,18 +64,18 @@ void Menu::InitializeMerchantWindow(){ auto inventoryDisplay=merchantWindow->ADD("Merchant Inventory Display",RowMerchantInventoryScrollableWindowComponent)({{2,28},{220,merchantWindow->size.y-44}},"Item Name Label","Item Description Label", [](MenuFuncData data){ RowItemDisplay*item=dynamic_cast(data.component); - Component(BUY_ITEM,"Item Purchase Header")->S(A::ITEM_NAME)=item->GetItem().ActualName(); - Component(BUY_ITEM,"Price per item Amount Label")->SetLabel(std::to_string(item->GetItem().BuyValue())); + 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"); - Component(BUY_ITEM,"Total Price Amount Label")->SetLabel(std::to_string(item->GetItem().BuyValue())); + Component(BUY_ITEM,"Total Price Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->BuyValue())); Merchant&merchant=Merchant::GetCurrentTravelingMerchant(); - bool canPurchase=merchant.CanPurchaseItem(item->GetItem().ActualName(),1); + bool canPurchase=merchant.CanPurchaseItem(item->GetItem().lock()->ActualName(),1); std::string colorCode=""; if(!canPurchase)colorCode="#FF0000"; - Component(BUY_ITEM,"Total Price Amount Label")->SetLabel(colorCode+std::to_string(item->GetItem().BuyValue())); - Component(BUY_ITEM,"Item Purchase Header")->SetLabel("Buying "+item->GetItem().DisplayName()); - Component(BUY_ITEM,"Purchase Button")->SetGrayedOut(!merchant.CanPurchaseItem(item->GetItem().ActualName(),1)); + Component(BUY_ITEM,"Total Price Amount Label")->SetLabel(colorCode+std::to_string(item->GetItem().lock()->BuyValue())); + Component(BUY_ITEM,"Item Purchase Header")->SetLabel("Buying "+item->GetItem().lock()->DisplayName()); + Component(BUY_ITEM,"Purchase Button")->SetGrayedOut(!merchant.CanPurchaseItem(item->GetItem().lock()->ActualName(),1)); Menu::OpenMenu(BUY_ITEM); return true; }, diff --git a/Crawler/Player.cpp b/Crawler/Player.cpp index 3fc20751..641d75f5 100644 --- a/Crawler/Player.cpp +++ b/Crawler/Player.cpp @@ -870,10 +870,10 @@ void PlayerStats::RecalculateEquipStats(){ baseStats.copyTo(equipStats); for(int i=int(EquipSlot::HELMET);i<=int(EquipSlot::RING2);i<<=1){ EquipSlot slot=EquipSlot(i); - Item*equip=Inventory::GetEquip(slot); - if(equip->IsBlank())continue; + std::weak_ptrequip=Inventory::GetEquip(slot); + if(ISBLANK(equip))continue; for(ItemAttribute a=ItemAttribute(int(ItemAttribute::ENUM_START)+1);aGetStats().A_Read(a); + equipStats.A(a)+=equip.lock()->GetStats().A_Read(a); } } diff --git a/Crawler/RowItemDisplay.h b/Crawler/RowItemDisplay.h index b2273763..2cb6e5f3 100644 --- a/Crawler/RowItemDisplay.h +++ b/Crawler/RowItemDisplay.h @@ -43,30 +43,30 @@ All rights reserved. INCLUDE_game class RowItemDisplay:public MenuComponent{ - std::reference_wrapperitemRef; + std::weak_ptritemRef; std::string itemNameLabelName; std::string itemDescriptionLabelName; CompactText compact=NON_COMPACT; bool showQuantity=true; public: - inline RowItemDisplay(geom2d::rectrect,const Item&itemRef,MenuFunc onClick,std::string itemNameLabelName,std::string itemDescriptionLabelName,ButtonAttr attributes=ButtonAttr::NONE) + inline RowItemDisplay(geom2d::rectrect,const std::weak_ptritemRef,MenuFunc onClick,std::string itemNameLabelName,std::string itemDescriptionLabelName,ButtonAttr attributes=ButtonAttr::NONE) :MenuComponent(rect,"",onClick,attributes),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName),itemRef(itemRef){ - if(itemRef.IsEquippable())SetShowQuantity(false); + if(itemRef.lock()->IsEquippable())SetShowQuantity(false); } virtual inline void DrawDecal(ViewPort&window,bool focused)final{ MenuComponent::DrawDecal(window,focused); float scaleFactor=(rect.size.y-4)/24; vf2d iconSize=vf2d{scaleFactor,scaleFactor}*24.f; - window.DrawDecal(rect.pos+vf2d{2,2},const_cast(itemRef.get().Decal()),{scaleFactor,scaleFactor}); + window.DrawDecal(rect.pos+vf2d{2,2},const_cast(itemRef.lock()->Decal()),{scaleFactor,scaleFactor}); window.DrawRectDecal(rect.pos+vf2d{2,2},iconSize); - std::string itemName=itemRef.get().DisplayName(); + std::string itemName=itemRef.lock()->DisplayName(); vf2d scaledSize={std::min(1.f,(rect.size.x-6-iconSize.x)/game->GetTextSizeProp(itemName).x),1}; window.DrawShadowStringPropDecal(rect.pos+vf2d{4,4}+vf2d{iconSize.x,iconSize.y/2-4},itemName,WHITE,BLACK,scaledSize); - if(showQuantity&&itemRef.get().Amt()!=INFINITE){ - std::string quantityText=std::format("x{}",itemRef.get().Amt()); + if(showQuantity&&itemRef.lock()->Amt()!=INFINITE){ + std::string quantityText=std::format("x{}",itemRef.lock()->Amt()); float qtyTextScale=0.75f; vf2d qtyTextSize=vf2d(game->GetTextSizeProp(quantityText))*qtyTextScale; window.DrawShadowStringPropDecal(rect.pos+rect.size-vf2d{1,1}+vf2d{-qtyTextSize.x,-qtyTextSize.y},quantityText,WHITE,BLACK,{qtyTextScale,qtyTextScale}); @@ -78,7 +78,8 @@ public: } virtual inline void Update(Crawler*game)override{ MenuComponent::Update(game); - + valid=!ISBLANK(itemRef)&&itemRef.lock()->Amt()>0; + if(hovered){ UpdateLabel(); } @@ -98,19 +99,19 @@ public: virtual inline void SetShowQuantity(bool showQuantity){ this->showQuantity=showQuantity; } - virtual inline const Item&GetItem(){ + virtual inline const std::weak_ptrGetItem(){ return itemRef; } - virtual inline void SetItem(const Item&itemRef){ - this->itemRef=std::reference_wrapper(itemRef); + virtual inline void SetItem(const std::weak_ptritemRef){ + this->itemRef=itemRef; } void UpdateLabel(){ - if(itemRef.get().IsBlank())return; + if(ISBLANK(itemRef))return; std::string labelNameText; std::string labelDescriptionText; if(valid){ - labelNameText=itemRef.get().DisplayName(); - labelDescriptionText=itemRef.get().Description(compact); + labelNameText=itemRef.lock()->DisplayName(); + labelDescriptionText=itemRef.lock()->Description(compact); }else{ labelNameText=""; labelDescriptionText=""; diff --git a/Crawler/RowMerchantInventoryScrollableWindowComponent.h b/Crawler/RowMerchantInventoryScrollableWindowComponent.h index ef673cb2..dd612675 100644 --- a/Crawler/RowMerchantInventoryScrollableWindowComponent.h +++ b/Crawler/RowMerchantInventoryScrollableWindowComponent.h @@ -45,7 +45,7 @@ public: virtual inline void OnInventorySlotsUpdate(ITCategory cat)override{ - const std::vector&merchantInv=Merchant::GetCurrentTravelingMerchant().GetShopItems(); + 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=Merchant::GetCurrentTravelingMerchant().GetShopItems(); + const std::vector>&merchantInv=Merchant::GetCurrentTravelingMerchant().GetShopItems(); size_t invSize=merchantInv.size(); int invWidth=int(rect.size.x/(float(options.size.x)+options.padding)); int x=int((invSize-1)%invWidth); diff --git a/Crawler/ScrollableWindowComponent.h b/Crawler/ScrollableWindowComponent.h index edef1f79..88c76bf8 100644 --- a/Crawler/ScrollableWindowComponent.h +++ b/Crawler/ScrollableWindowComponent.h @@ -76,7 +76,8 @@ public: size_t removedCount=0; removedCount+=std::erase(buttonList,button); removedCount+=std::erase(keyboardButtonList,button); - if(removedCount!=2){ + removedCount+=Menu::menus[button->parentMenu]->components.erase(button->GetName()); + if(removedCount!=3){ std::cout<<"WARNING! Attempted to remove buttons from button listing, but not found!"; } if(buttonList.size()==0){ @@ -89,8 +90,9 @@ public: ERR("WARNING! Attempted to erase key "<originalPos.y<<" from button map, but the list still exists!") } } - Menu::menus[button->parentMenu]->components.erase(button->GetName()); - components.erase(std::find(components.begin(),components.end(),button)); + auto componentSearchResults=std::find(components.begin(),components.end(),button); + if(componentSearchResults==components.end())ERR("Could not find Component"<GetName())<<" inside the component list!"); + components.erase(componentSearchResults); Menu::menus[button->parentMenu]->RecalculateComponentCount(); delete button; CalculateBounds(); diff --git a/Crawler/Version.h b/Crawler/Version.h index 611d828d..040da977 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 4463 +#define VERSION_BUILD 4521 #define stringify(a) stringify_(a) #define stringify_(a) #a