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.

pull/28/head
sigonasr2 11 months ago
parent d801a41900
commit 70423be9d4
  1. 28
      Crawler/CharacterMenuWindow.cpp
  2. 38
      Crawler/Crawler.cpp
  3. 4
      Crawler/Crawler.h
  4. 6
      Crawler/EquipSlotButton.h
  5. 2
      Crawler/InventoryConsumableWindow.cpp
  6. 5
      Crawler/InventoryScrollableWindowComponent.h
  7. 131
      Crawler/Item.cpp
  8. 39
      Crawler/Item.h
  9. 1
      Crawler/ItemLoadoutWindow.cpp
  10. 1
      Crawler/MenuComponent.h
  11. 52
      Crawler/MenuItemButton.h
  12. 43
      Crawler/MenuItemItemButton.h
  13. 38
      Crawler/Merchant.cpp
  14. 8
      Crawler/Merchant.h
  15. 14
      Crawler/MerchantWindow.cpp
  16. 6
      Crawler/Player.cpp
  17. 27
      Crawler/RowItemDisplay.h
  18. 4
      Crawler/RowMerchantInventoryScrollableWindowComponent.h
  19. 8
      Crawler/ScrollableWindowComponent.h
  20. 2
      Crawler/Version.h

@ -121,20 +121,20 @@ void Menu::InitializeCharacterMenuWindow(){
[&](MenuFuncData data){
EquipSlot slot=EquipSlot(data.component->I(Attribute::EQUIP_TYPE));
std::vector<Item>&equips=Inventory::get("Equipment");
std::vector<Item>&accessories=Inventory::get("Accessories");
std::vector<Item>availableEquipment;
std::copy_if(equips.begin(),equips.end(),std::back_inserter(availableEquipment),[&](Item&it){
return it.GetEquipSlot()&slot;
std::vector<std::shared_ptr<Item>>&equips=Inventory::get("Equipment");
std::vector<std::shared_ptr<Item>>&accessories=Inventory::get("Accessories");
std::vector<std::shared_ptr<Item>>availableEquipment;
std::copy_if(equips.begin(),equips.end(),std::back_inserter(availableEquipment),[&](std::shared_ptr<Item>it){
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_ptr<Item>it){
return it->GetEquipSlot()&slot;
});
ScrollableWindowComponent*equipList=Component<ScrollableWindowComponent>(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_ptr<Item>it:availableEquipment){
std::weak_ptr<Item>itemInvRef=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<RowItemDisplay*>(data.component);
@ -165,13 +165,13 @@ void Menu::InitializeCharacterMenuWindow(){
[&](MenuFuncData data){
RowItemDisplay*button=dynamic_cast<RowItemDisplay*>(data.component);
if(button!=nullptr){
const Item&buttonItem=button->GetItem();
const std::weak_ptr<Item>buttonItem=button->GetItem();
std::vector<int>statsBeforeEquip;
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_ptr<Item>equippedItem=Inventory::GetEquip(slot);
Inventory::EquipItem(buttonItem,slot);
for(int counter=0;ItemAttribute attribute:displayAttrs){
StatLabel*statDisplayLabel=Component<StatLabel>(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);

@ -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,7 +1303,9 @@ void Crawler::RenderCooldowns(){
}
if(loadoutSlot!=-1){
uint32_t itemAmt=GetLoadoutItem(loadoutSlot).Amt();
const std::weak_ptr<Item>loadoutItem=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};
@ -1315,6 +1317,7 @@ void Crawler::RenderCooldowns(){
keyDisplayCol=RED;
}
}
}
if(a.manaCost>0){
vf2d manaCostSize=vf2d{GetTextSize(std::to_string(a.manaCost))}*vf2d{0.5f,0.75f};
@ -2210,7 +2213,7 @@ void Crawler::SetChapter(int chapter){
this->chapter=chapter;
}
Item&Crawler::GetLoadoutItem(int slot){
const std::weak_ptr<Item>Crawler::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<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 1")->SetItem(loadout[slot]);
Component<MenuItemItemButton>(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<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 2")->SetItem(loadout[slot]);
Component<MenuItemItemButton>(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<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 3")->SetItem(loadout[slot]);
Component<MenuItemItemButton>(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<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 1")->SetItem(Item::BLANK);
Component<MenuItemItemButton>(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<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 2")->SetItem(Item::BLANK);
Component<MenuItemItemButton>(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<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 3")->SetItem(Item::BLANK);
Component<MenuItemItemButton>(MenuType::ITEM_LOADOUT,"Loadout Item 3")->UpdateIcon();
}break;
}

@ -106,7 +106,7 @@ private:
bool encounterStarted=false;
int totalBossEncounterMobs=0;
int chapter=1; //We start at chapter 1.
std::array<Item,3>loadout;
std::array<std::shared_ptr<Item>,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_ptr<Item>GetLoadoutItem(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);

@ -50,9 +50,9 @@ public:
inline EquipSlotButton(geom2d::rect<float>rect,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<Decal*>(equip.Decal());
const std::weak_ptr<Item>equip=Inventory::GetEquip(slot);
if(!ISBLANK(equip)){
icon=const_cast<Decal*>(equip.lock()->Decal());
SetItem(equip);
}else{
icon=nullptr;

@ -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");

@ -110,9 +110,10 @@ protected:
for(int j=i;j<components.size()-1;j++){
//Take the item in the next slot and move it to this slot.
Menu::menus[components[j]->parentMenu]->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.

@ -49,20 +49,21 @@ INCLUDE_GFX
safemap<std::string,ItemInfo>ITEM_DATA;
safemap<std::string,ItemScript>ITEM_SCRIPTS;
safemap<std::string,std::set<std::string>>ITEM_CATEGORIES;
Item Item::BLANK;
std::map<IT,Item>Inventory::_inventory;
std::map<ITCategory,std::vector<Item>>Inventory::sortedInv;
std::shared_ptr<Item>Item::BLANK=std::make_shared<Item>();
std::map<IT,std::shared_ptr<Item>>Inventory::_inventory;
std::map<ITCategory,std::vector<std::shared_ptr<Item>>>Inventory::sortedInv;
std::vector<ItemOverlay>ItemOverlay::items;
std::map<std::string,ItemSet>ItemSet::sets;
std::map<EquipSlot,Item*>Inventory::equipment;
std::map<EquipSlot,std::weak_ptr<Item>>Inventory::equipment;
std::map<std::string,EquipSlot>ItemInfo::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 "<<std::quoted(it)<<" does not exist in Item Database!");
//There are two places to manipulate items in (Both the sorted inventory and the actual inventory)
if(!_inventory.count(it)){
_inventory[it]=Item{amt,it};
_inventory[it]=std::make_shared<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_ptr<Item>Inventory::CopyItem(IT it){
if(!_inventory.count(it))return std::make_shared<Item>(*Item::BLANK);
return std::make_shared<Item>(*_inventory.at(it));
}
Item&Inventory::GetItem(IT it){
std::weak_ptr<Item>Inventory::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<Item>&inv=sortedInv.at(inventory);
std::vector<std::shared_ptr<Item>>&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_ptr<Item>item: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<Item>&Inventory::get(ITCategory itemCategory){
std::vector<std::shared_ptr<Item>>&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<Item>&inv=sortedInv.at(stageInventoryCategory);
std::vector<Item>::iterator it=std::find(inv.begin(),inv.end(),Item{amt,item});
std::vector<std::shared_ptr<Item>>&inv=sortedInv.at(stageInventoryCategory);
std::vector<std::shared_ptr<Item>>::iterator it=std::find(inv.begin(),inv.end(),std::make_shared<Item>(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<Item>(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<Item>&inv=sortedInv.at(itemCategory);
std::vector<std::shared_ptr<Item>>&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_ptr<Item>item1=inv.at(slot1);
std::shared_ptr<Item>item2=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,26 +465,20 @@ 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::vector<Item>itemList=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<std::shared_ptr<Item>>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>&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();
}
RemoveItem(item.ActualName(),itemCategory,uint32_t(itemQuantity));
}
itemQuantity=item->Amt();
}
const bool Item::operator==(const Item&rhs)const{
return it==rhs.it;
RemoveItem(item->ActualName(),itemCategory,uint32_t(itemQuantity));
}
const bool Item::operator==(const IT&rhs)const{
return it->Name()==rhs;
}
ItemOverlay::ItemOverlay(ItemInfo item)
@ -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_ptr<Item>it,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<Item*>(&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_ptr<Item>it){
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_ptr<Item>equip=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_ptr<Item>Inventory::GetEquip(EquipSlot slot){
return Inventory::equipment[slot];
}
const EquipSlot Item::GetEquipSlot()const{
@ -657,10 +651,10 @@ const std::map<ItemSet,int>Inventory::GetEquippedItemSets(){
std::map<ItemSet,int>setCounts;
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_ptr<Item>equip=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());
const std::weak_ptr<Item>Inventory::GetInventorySlot(ITCategory itemCategory,size_t slot){
return GetItem(get(itemCategory).at(slot)->ActualName());
}
bool Item::IsBlank(std::shared_ptr<Item>item){
Item::IsBlankStaticCallCounter++;
return item->IsBlank();
};
bool Item::IsBlank(std::weak_ptr<Item>item){
if(!item.expired())Item::IsBlankStaticCallCounter++;else return true;
return item.lock()->IsBlank();
};
const bool Item::_IsBlank()const{
Item::IsBlankStaticCallCounter++;
return IsBlank();
};

@ -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_ptr<Item>BLANK;
const std::optional<const::ItemSet*const>ItemSet()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_ptr<Item>item);
//Use ISBLANK macro instead!! This should not be called directly!!
static bool IsBlank(std::weak_ptr<Item>item);
friend const bool operator==(std::shared_ptr<Item>lhs,std::shared_ptr<Item>rhs){return lhs->it==rhs->it;};
friend const bool operator==(std::shared_ptr<Item>lhs,const IT&rhs){return lhs->ActualName()==rhs;};
friend const bool operator==(std::weak_ptr<Item>lhs,std::weak_ptr<Item>rhs){return !lhs.expired()&&!rhs.expired()&&lhs.lock()->it==rhs.lock()->it;};
friend const bool operator==(std::weak_ptr<Item>lhs,const IT&rhs){return !lhs.expired()&&lhs.lock()->ActualName()==rhs;};
friend const bool operator==(const IT&lhs,std::weak_ptr<Item>rhs){return operator==(rhs,lhs);};
friend const bool operator==(const IT&lhs,std::shared_ptr<Item>rhs){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_ptr<Item>CopyItem(IT it);
static std::weak_ptr<Item>GetItem(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<Item>&get(ITCategory itemCategory);
static const Item&GetInventorySlot(ITCategory itemCategory,size_t slot);
static std::vector<std::shared_ptr<Item>>&get(ITCategory itemCategory);
static const std::weak_ptr<Item>GetInventorySlot(ITCategory itemCategory,size_t slot);
static void Clear(ITCategory itemCategory);
static void EquipItem(const Item&it,EquipSlot slot);
static void EquipItem(const std::weak_ptr<Item>it,EquipSlot slot);
static void UnequipItem(EquipSlot slot);
static EquipSlot GetSlotEquippedIn(const Item&it);
static Item*GetEquip(EquipSlot slot);
static EquipSlot GetSlotEquippedIn(const std::weak_ptr<Item>it);
static std::weak_ptr<Item>GetEquip(EquipSlot slot);
static const std::map<ItemSet,int>GetEquippedItemSets();
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<IT,Item>_inventory;
static std::map<EquipSlot,Item*>equipment;
static std::map<IT,std::shared_ptr<Item>>_inventory;
static std::map<EquipSlot,std::weak_ptr<Item>>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<ITCategory,std::vector<Item>>sortedInv;
static std::map<ITCategory,std::vector<std::shared_ptr<Item>>>sortedInv;
};
class ItemProps{
@ -275,3 +286,5 @@ public:
static void Draw();
void ResetTimer();
};
#define ISBLANK(itemRef) Item::IsBlank(itemRef)

@ -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;

@ -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);

@ -50,40 +50,52 @@ INCLUDE_game
INCLUDE_ITEM_DATA
class MenuItemButton:public MenuIconButton{
friend class InventoryScrollableWindowComponent;
private:
std::vector<Item>&invRef;
int inventoryIndex=0;
std::vector<std::shared_ptr<Item>>&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::rect<float>rect,std::vector<Item>&invRef,int invIndex,MenuFunc onClick,MenuType itemDescriptionMenu,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE)
:MenuIconButton(rect,invRef.size()>invIndex?const_cast<Decal*>(invRef[invIndex].Decal()):nullptr,onClick,attributes),invRef(invRef),inventoryIndex(invIndex),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){
inline MenuItemButton(geom2d::rect<float>rect,std::vector<std::shared_ptr<Item>>&invRef,int invIndex,MenuFunc onClick,MenuType itemDescriptionMenu,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE)
:MenuIconButton(rect,invRef.size()>invIndex?const_cast<Decal*>(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::rect<float>rect,std::vector<Item>&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<Decal*>(invRef[invIndex].Decal()):nullptr,onClick,attributes),invRef(invRef),inventoryIndex(invIndex),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){
inline MenuItemButton(geom2d::rect<float>rect,std::vector<std::shared_ptr<Item>>&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<Decal*>(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_ptr<Item>GetItem(){
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<Decal*>(invRef[inventoryIndex].Decal());
labelNameText=invRef[inventoryIndex].DisplayName();
labelDescriptionText=invRef[inventoryIndex].Description(compact);
icon=const_cast<Decal*>(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);
}
};

@ -50,30 +50,30 @@ INCLUDE_ITEM_DATA
class MenuItemItemButton:public MenuIconButton{
private:
std::reference_wrapper<const Item>itemRef;
std::weak_ptr<Item>itemRef;
std::string itemNameLabelName;
std::string itemDescriptionLabelName;
bool hideQty=false;
CompactText compact=COMPACT;
public:
inline MenuItemItemButton(geom2d::rect<float>rect,const Item&itemRef,MenuType menuDest,MenuFunc onClick,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE)
:MenuIconButton(rect,(!itemRef.IsBlank())?const_cast<Decal*>(itemRef.Decal()):nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){
inline MenuItemItemButton(geom2d::rect<float>rect,const std::weak_ptr<Item>itemRef,MenuType menuDest,MenuFunc onClick,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE)
:MenuIconButton(rect,!ISBLANK(itemRef)?const_cast<Decal*>(itemRef.lock()->Decal()):nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){
draggable=false;
valid=!itemRef.IsBlank();
valid=!ISBLANK(itemRef);
}
inline MenuItemItemButton(geom2d::rect<float>rect,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<Decal*>(itemRef.Decal()):nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){
inline MenuItemItemButton(geom2d::rect<float>rect,const std::weak_ptr<Item>itemRef,MenuType menuDest,MenuFunc onClick,MenuFunc onHover,MenuFunc onMouseOut,std::string itemNameLabelName="",std::string itemDescriptionLabelName="",IconButtonAttr attributes=IconButtonAttr::SELECTABLE)
:MenuIconButton(rect,!ISBLANK(itemRef)?const_cast<Decal*>(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_ptr<Item>GetItem(){
return itemRef;
}
inline const Item&SetItem(const Item&newItem){
return itemRef=std::reference_wrapper<const Item>(newItem);
inline const std::weak_ptr<Item>SetItem(const std::weak_ptr<Item>newItem){
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<Decal*>(itemRef.get().Decal());
icon=const_cast<Decal*>(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<Decal*>(itemRef.get().Decal());
labelNameText=itemRef.get().DisplayName();
labelDescriptionText=itemRef.get().Description(compact);
icon=const_cast<Decal*>(itemRef.lock()->Decal());
labelNameText=itemRef.lock()->DisplayName();
labelDescriptionText=itemRef.lock()->Description(compact);
if(itemNameLabelName!=""){
Component<MenuLabel>(parentMenu,itemNameLabelName)->label=labelNameText;
Component<MenuLabel>(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);
}
}

@ -44,7 +44,7 @@ INCLUDE_game
INCLUDE_ITEM_CATEGORIES
std::map<Chapter,std::vector<Merchant>>Merchant::merchants;
std::map<ITCategory,std::vector<Item*>>Merchant::sortedItems;
std::map<ITCategory,std::vector<std::weak_ptr<Item>>>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<Item>&Merchant::GetShopItems()const{
const std::vector<std::shared_ptr<Item>>&Merchant::GetShopItems()const{
return shopItems;
}
const std::vector<Item*>&Merchant::GetShopItems(ITCategory category){
const std::vector<std::weak_ptr<Item>>&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<Item>(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*>(&item));
std::for_each(merchant.shopItems.begin(),merchant.shopItems.end(),[](const std::shared_ptr<Item>item){
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_ptr<Item>foundItem;
for(const std::shared_ptr<Item>it:shopItems){
if(it==item&&it->Amt()>=amt&&it->CanBePurchased()){
itemAvailable=true;
foundItem=&it;
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_ptr<Item>it: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_ptr<Item>it: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;

@ -49,8 +49,8 @@ public:
static void RandomizeTravelingMerchant();
static Merchant&GetCurrentTravelingMerchant();
const std::string&GetDisplayName()const;
const std::vector<Item>&GetShopItems()const;
const static std::vector<Item*>&GetShopItems(ITCategory category);
const std::vector<std::shared_ptr<Item>>&GetShopItems()const;
const static std::vector<std::weak_ptr<Item>>&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<Chapter,std::vector<Merchant>>merchants;
std::string displayName;
std::vector<Item>shopItems;
static std::map<ITCategory,std::vector<Item*>>sortedItems;
std::vector<std::shared_ptr<Item>>shopItems;
static std::map<ITCategory,std::vector<std::weak_ptr<Item>>>sortedItems;
static void UpdateSortedItemsList();
};

@ -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<RowItemDisplay*>(data.component);
Component<MenuLabel>(BUY_ITEM,"Item Purchase Header")->S(A::ITEM_NAME)=item->GetItem().ActualName();
Component<MenuLabel>(BUY_ITEM,"Price per item Amount Label")->SetLabel(std::to_string(item->GetItem().BuyValue()));
Component<MenuLabel>(BUY_ITEM,"Item Purchase Header")->S(A::ITEM_NAME)=item->GetItem().lock()->ActualName();
Component<MenuLabel>(BUY_ITEM,"Price per item Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->BuyValue()));
Component<MenuLabel>(BUY_ITEM,"Amount to buy Amount Label")->SetLabel("1");
Component<MenuLabel>(BUY_ITEM,"Total Price Amount Label")->SetLabel(std::to_string(item->GetItem().BuyValue()));
Component<MenuLabel>(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<MenuLabel>(BUY_ITEM,"Total Price Amount Label")->SetLabel(colorCode+std::to_string(item->GetItem().BuyValue()));
Component<MenuLabel>(BUY_ITEM,"Item Purchase Header")->SetLabel("Buying "+item->GetItem().DisplayName());
Component<MenuComponent>(BUY_ITEM,"Purchase Button")->SetGrayedOut(!merchant.CanPurchaseItem(item->GetItem().ActualName(),1));
Component<MenuLabel>(BUY_ITEM,"Total Price Amount Label")->SetLabel(colorCode+std::to_string(item->GetItem().lock()->BuyValue()));
Component<MenuLabel>(BUY_ITEM,"Item Purchase Header")->SetLabel("Buying "+item->GetItem().lock()->DisplayName());
Component<MenuComponent>(BUY_ITEM,"Purchase Button")->SetGrayedOut(!merchant.CanPurchaseItem(item->GetItem().lock()->ActualName(),1));
Menu::OpenMenu(BUY_ITEM);
return true;
},

@ -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_ptr<Item>equip=Inventory::GetEquip(slot);
if(ISBLANK(equip))continue;
for(ItemAttribute a=ItemAttribute(int(ItemAttribute::ENUM_START)+1);a<ItemAttribute::ENUM_END;a=ItemAttribute(int(a)+1)){
equipStats.A(a)+=equip->GetStats().A_Read(a);
equipStats.A(a)+=equip.lock()->GetStats().A_Read(a);
}
}

@ -43,30 +43,30 @@ All rights reserved.
INCLUDE_game
class RowItemDisplay:public MenuComponent{
std::reference_wrapper<const Item>itemRef;
std::weak_ptr<Item>itemRef;
std::string itemNameLabelName;
std::string itemDescriptionLabelName;
CompactText compact=NON_COMPACT;
bool showQuantity=true;
public:
inline RowItemDisplay(geom2d::rect<float>rect,const Item&itemRef,MenuFunc onClick,std::string itemNameLabelName,std::string itemDescriptionLabelName,ButtonAttr attributes=ButtonAttr::NONE)
inline RowItemDisplay(geom2d::rect<float>rect,const std::weak_ptr<Item>itemRef,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<Decal*>(itemRef.get().Decal()),{scaleFactor,scaleFactor});
window.DrawDecal(rect.pos+vf2d{2,2},const_cast<Decal*>(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,6 +78,7 @@ 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_ptr<Item>GetItem(){
return itemRef;
}
virtual inline void SetItem(const Item&itemRef){
this->itemRef=std::reference_wrapper<const Item>(itemRef);
virtual inline void SetItem(const std::weak_ptr<Item>itemRef){
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="";

@ -45,7 +45,7 @@ public:
virtual inline void OnInventorySlotsUpdate(ITCategory cat)override{
const std::vector<Item>&merchantInv=Merchant::GetCurrentTravelingMerchant().GetShopItems();
const std::vector<std::shared_ptr<Item>>&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()){//We need more space to display our items.
AddButtonOnSlotUpdate(cat);
@ -56,7 +56,7 @@ public:
}
virtual inline void AddButtonOnSlotUpdate(ITCategory cat)override{
const std::vector<Item>&merchantInv=Merchant::GetCurrentTravelingMerchant().GetShopItems();
const std::vector<std::shared_ptr<Item>>&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);

@ -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 "<<button->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"<<std::quoted(button->GetName())<<" inside the component list!");
components.erase(componentSearchResults);
Menu::menus[button->parentMenu]->RecalculateComponentCount();
delete button;
CalculateBounds();

@ -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

Loading…
Cancel
Save