From 6412f34dce177762c9de668bbc5dc45a69d157c8 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Tue, 26 Dec 2023 01:54:14 -0600 Subject: [PATCH] Implemented Merchant Selling Screen --- Crawler/BuyItemWindow.cpp | 4 +- Crawler/Crawler.cpp | 8 +-- Crawler/Crawler.vcxproj | 5 +- Crawler/Crawler.vcxproj.filters | 4 +- Crawler/InventoryConsumableWindow.cpp | 2 +- Crawler/InventoryScrollableWindowComponent.h | 16 +++--- Crawler/InventoryWindow.cpp | 8 ++- Crawler/LevelCompleteWindow.cpp | 4 +- Crawler/Menu.cpp | 2 + Crawler/MenuItemItemButton.h | 14 ++--- Crawler/MenuLabel.h | 4 +- Crawler/Merchant.cpp | 17 ++++-- Crawler/MerchantWindow.cpp | 34 ++++++------ .../RowInventoryScrollableWindowComponent.h | 21 ++++++-- Crawler/RowItemDisplay.h | 35 +++++++++++++ ...rchantInventoryScrollableWindowComponent.h | 24 +++------ Crawler/ScrollableWindowComponent.h | 4 ++ Crawler/SellItemWindow.cpp | 52 ++++++++++++++++++- Crawler/TODO.txt | 4 +- Crawler/Version.h | 2 +- Crawler/assets/config/gfx/gfx.txt | 1 + Crawler/olcPGEX_ViewPort.h | 8 +-- Crawler/olcPixelGameEngine.h | 8 +-- 23 files changed, 201 insertions(+), 80 deletions(-) diff --git a/Crawler/BuyItemWindow.cpp b/Crawler/BuyItemWindow.cpp index 4e6d29d7..0b0576c6 100644 --- a/Crawler/BuyItemWindow.cpp +++ b/Crawler/BuyItemWindow.cpp @@ -43,10 +43,10 @@ using A=Attribute; void Menu::InitializeBuyItemWindow(){ Menu*buyItemWindow=CreateMenu(BUY_ITEM,CENTERED,{192,72}); - static auto GetQuantity = [&](){ + static auto GetQuantity=[&](){ return std::stoi(Component(BUY_ITEM,"Amount to buy Amount Label")->GetLabel()); }; - static auto UpdateMenu = [&](int32_t qty){ + static auto UpdateMenu=[&](int32_t qty){ qty=std::clamp(qty,1,99); int pricePerItem=std::stoi(Component(BUY_ITEM,"Price per item Amount Label")->GetLabel()); Component(BUY_ITEM,"Amount to buy Amount Label")->SetLabel(std::to_string(qty)); diff --git a/Crawler/Crawler.cpp b/Crawler/Crawler.cpp index 3d284d2f..66f7cc6b 100644 --- a/Crawler/Crawler.cpp +++ b/Crawler/Crawler.cpp @@ -1244,7 +1244,7 @@ void Crawler::RenderHud(){ std::stringstream castTimeDisplay; castTimeDisplay<::max(),0.75f); + DrawShadowStringPropDecal(vf2d{ScreenWidth()/2.f-GetTextSizeProp(castText).x*2/2,ScreenHeight()-64.f},castText,WHITE,BLACK,{2,3},std::numeric_limits::max(),2.f); }; if(GetPlayer()->GetCastInfo().castTimer>0){ @@ -1258,8 +1258,8 @@ void Crawler::RenderHud(){ DrawDecal({2,20},GFX["mana.png"].Decal()); std::string text=player->GetHealth()>0?std::to_string(player->GetHealth()):"X"; std::string text_mana=std::to_string(player->GetMana()); - DrawShadowStringPropDecal({20,3},text,WHITE,BLACK,{2,2},INFINITE,0.5f); - DrawShadowStringPropDecal({24,23},text_mana,{192,192,255},BLACK,{1.5f,1.5f},INFINITE,0.6f); + DrawShadowStringPropDecal({20,3},text,WHITE,BLACK,{2,2},INFINITE); + DrawShadowStringPropDecal({24,23},text_mana,{192,192,255},BLACK,{1.5f,1.5f},INFINITE); if(player->notEnoughManaDisplay.second>0){ std::string displayText="Not enough mana for "+player->notEnoughManaDisplay.first+"!"; DrawShadowStringPropDecal(vf2d{float(ScreenWidth()/2),float(ScreenHeight()/4)}-GetTextSizeProp(displayText)/2,displayText,DARK_RED,VERY_DARK_RED); @@ -1362,7 +1362,7 @@ void Crawler::RenderCooldowns(){ DrawShadowStringDecal(pos+vf2d{12,-2}-keyDisplaySize/2,a.input->GetDisplayName(),keyDisplayCol,BLACK,{0.5f,0.5f},std::numeric_limits::max(),1); vf2d shortNameSize=vf2d{GetTextSize(a.shortName)}*vf2d{0.5f,0.75f}; - DrawShadowStringDecal(pos+vf2d{13,24}-shortNameSize/2,a.shortName,shortNameCol,WHITE,{0.5f,0.75f}); + DrawShadowStringDecal(pos+vf2d{13,24}-shortNameSize/2,a.shortName,shortNameCol,{255,255,255,230},{0.5f,0.75f}); } }; diff --git a/Crawler/Crawler.vcxproj b/Crawler/Crawler.vcxproj index a5337580..ce6d0626 100644 --- a/Crawler/Crawler.vcxproj +++ b/Crawler/Crawler.vcxproj @@ -407,10 +407,7 @@ - - - - + diff --git a/Crawler/Crawler.vcxproj.filters b/Crawler/Crawler.vcxproj.filters index 17360acc..6a080a4a 100644 --- a/Crawler/Crawler.vcxproj.filters +++ b/Crawler/Crawler.vcxproj.filters @@ -375,10 +375,10 @@ Header Files\discord-files - + Header Files\Interface - + Header Files\Interface diff --git a/Crawler/InventoryConsumableWindow.cpp b/Crawler/InventoryConsumableWindow.cpp index 2432d10b..5e95aba9 100644 --- a/Crawler/InventoryConsumableWindow.cpp +++ b/Crawler/InventoryConsumableWindow.cpp @@ -55,7 +55,7 @@ void Menu::InitializeConsumableInventoryWindow(){ inventoryWindow->I(A::LOADOUT_SLOT)=0; - auto consumableWindow=inventoryWindow->ADD("inventory",InventoryScrollableWindowComponent)({{0,15},{windowSize.x,96.f}},"Consumables","itemName","itemDescription", + auto consumableWindow=inventoryWindow->ADD("inventory",InventoryScrollableWindowComponent)({{0,15},{windowSize.x,96.f}},"itemName","itemDescription", [&](MenuFuncData data){ MenuItemButton*button=(MenuItemButton*)data.component; data.game->ClearLoadoutItem(data.menu.I(A::LOADOUT_SLOT)); diff --git a/Crawler/InventoryScrollableWindowComponent.h b/Crawler/InventoryScrollableWindowComponent.h index 93288265..7c099389 100644 --- a/Crawler/InventoryScrollableWindowComponent.h +++ b/Crawler/InventoryScrollableWindowComponent.h @@ -57,7 +57,6 @@ struct InventoryWindowOptions{ // Please ensure these are overwritten, otherwise you will get errors complaining about dynamic casts unable to convert classes successfully. class InventoryScrollableWindowComponent:public ScrollableWindowComponent{ protected: - ITCategory inventoryType; std::functioninventoryButtonClickAction; std::functioninventoryButtonHoverAction; std::functioninventoryButtonMouseOutAction; @@ -67,11 +66,11 @@ protected: std::string itemDescriptionLabelName; bool inventoryButtonsActive=true; public: - inline InventoryScrollableWindowComponent(geom2d::rectrect,ITCategory invType,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) - :InventoryScrollableWindowComponent(rect,invType,itemNameLabelName,itemDescriptionLabelName,inventoryButtonClickAction,DO_NOTHING,DO_NOTHING,options,inventoryButtonsActive,attributes){ + inline InventoryScrollableWindowComponent(geom2d::rectrect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) + :InventoryScrollableWindowComponent(rect,itemNameLabelName,itemDescriptionLabelName,inventoryButtonClickAction,DO_NOTHING,DO_NOTHING,options,inventoryButtonsActive,attributes){ } - inline InventoryScrollableWindowComponent(geom2d::rectrect,ITCategory invType,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,std::functioninventoryButtonHoverAction,std::functioninventoryButtonMouseOutAction,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) - :ScrollableWindowComponent(rect,attributes),inventoryButtonHoverAction(inventoryButtonHoverAction),inventoryButtonMouseOutAction(inventoryButtonMouseOutAction),inventoryType(invType),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName), + inline InventoryScrollableWindowComponent(geom2d::rectrect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,std::functioninventoryButtonHoverAction,std::functioninventoryButtonMouseOutAction,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) + :ScrollableWindowComponent(rect,attributes),inventoryButtonHoverAction(inventoryButtonHoverAction),inventoryButtonMouseOutAction(inventoryButtonMouseOutAction),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName), options(options),inventoryButtonClickAction(inventoryButtonClickAction),inventoryButtonsActive(inventoryButtonsActive){} virtual inline void Update(Crawler*game)override{ ScrollableWindowComponent::Update(game); @@ -128,7 +127,10 @@ protected: AddButtonOnSlotUpdate(cat); }else if(components.size()>invSize){ //There are empty spots, so let's clean up. - RemoveEmptySlots(); + RemoveAllComponents(); + for(std::weak_ptr item:Inventory::get(cat)){ + AddButtonOnSlotUpdate(cat); + } } } @@ -137,7 +139,7 @@ protected: } virtual inline void AddButtonOnSlotUpdate(ITCategory cat){ - size_t invSize=Inventory::get(cat).size(); + size_t invSize=components.size()+1; int invWidth=int(rect.size.x/(float(options.size.x)+options.padding)); int x=int((invSize-1)%invWidth); int y=int((invSize-1)/invWidth); diff --git a/Crawler/InventoryWindow.cpp b/Crawler/InventoryWindow.cpp index 2fac300c..a7fec77e 100644 --- a/Crawler/InventoryWindow.cpp +++ b/Crawler/InventoryWindow.cpp @@ -88,7 +88,7 @@ void Menu::InitializeInventoryWindow(){ button->SetSelectionType(HIGHLIGHT); button->S(A::CATEGORY_NAME)=category; - auto inventoryDisplay=inventoryWindow->ADD("Inventory Display - "+category,RowInventoryScrollableWindowComponent)({{72,28},{150,inventoryWindow->size.y-44}},category,"Item Name Label","Item Description Label",DO_NOTHING, + auto inventoryDisplay=inventoryWindow->ADD("Inventory Display - "+category,RowInventoryScrollableWindowComponent)({{72,28},{150,inventoryWindow->size.y-44}},"Item Name Label","Item Description Label",DO_NOTHING, [](MenuFuncData data){ Component(data.menu.GetType(),"Item Icon")->SetItem(dynamic_cast(data.component)->GetItem()); Component(data.menu.GetType(),"Item Icon")->UpdateIcon(); @@ -131,4 +131,10 @@ void Menu::InitializeInventoryWindow(){ moneyDisplay->SetRightAlignment(true); Player::AddMoneyListener(moneyDisplay); #pragma endregion + + inventoryWindow->ADD("Back Button",MenuComponent)({{inventoryWindow->size.x/2-48,28+inventoryWindow->size.y-44+6},{96,24}},"Back",MenuType::ENUM_END, + [](MenuFuncData data){ + Menu::CloseMenu(); + return true; + },{2,2})END; } \ No newline at end of file diff --git a/Crawler/LevelCompleteWindow.cpp b/Crawler/LevelCompleteWindow.cpp index 16a54be0..4ff3adb6 100644 --- a/Crawler/LevelCompleteWindow.cpp +++ b/Crawler/LevelCompleteWindow.cpp @@ -55,12 +55,12 @@ void Menu::InitializeLevelCompleteWindow(){ levelCompleteWindow->ADD("Monster Loot Outline",MenuComponent)({{0,32},{windowSize.size.x-80.f,72}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; levelCompleteWindow->ADD("Monster Loot Label",MenuLabel)({{0,32},{windowSize.size.x-80.f,12}},"Monster Loot",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END; - auto monsterLootWindow=levelCompleteWindow->ADD("Monster Loot Window",InventoryScrollableWindowComponent)({{0,44},{windowSize.size.x-80.f,60}},"Monster Loot","Monster Loot Popup Item Name","Monster Loot Popup Item Description",DO_NOTHING)END; + auto monsterLootWindow=levelCompleteWindow->ADD("Monster Loot Window",InventoryScrollableWindowComponent)({{0,44},{windowSize.size.x-80.f,60}},"Monster Loot Popup Item Name","Monster Loot Popup Item Description",DO_NOTHING)END; Menu::AddInventoryListener(monsterLootWindow,"Monster Loot"); levelCompleteWindow->ADD("Stage Loot Outline",MenuComponent)({{0,108},{windowSize.size.x-80.f,72}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END; levelCompleteWindow->ADD("Stage Loot Label",MenuLabel)({{0,108},{windowSize.size.x-80.f,12}},"Stage Loot",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::OUTLINE)END; - auto stageLootWindow=levelCompleteWindow->ADD("Stage Loot Window",InventoryScrollableWindowComponent)({{0,120},{windowSize.size.x-80.f,60}},"Stage Loot","Stage Loot Popup Item Name","Stage Loot Popup Item Description",DO_NOTHING)END; + auto stageLootWindow=levelCompleteWindow->ADD("Stage Loot Window",InventoryScrollableWindowComponent)({{0,120},{windowSize.size.x-80.f,60}},"Stage Loot Popup Item Name","Stage Loot Popup Item Description",DO_NOTHING)END; Menu::AddInventoryListener(stageLootWindow,"Stage Loot"); auto nextButtonAction=[](MenuFuncData data){ diff --git a/Crawler/Menu.cpp b/Crawler/Menu.cpp index b8c31eb6..08b00f69 100644 --- a/Crawler/Menu.cpp +++ b/Crawler/Menu.cpp @@ -177,6 +177,8 @@ void Menu::MenuSelect(Crawler*game){ } void Menu::Update(Crawler*game){ + if(selection.x<0||selection.x>=buttons[selection.y].size()){selection={-1,-1};} + if(draggingComponent==nullptr){ HoverMenuSelect(game); } diff --git a/Crawler/MenuItemItemButton.h b/Crawler/MenuItemItemButton.h index 1bb8f327..0aa96d90 100644 --- a/Crawler/MenuItemItemButton.h +++ b/Crawler/MenuItemItemButton.h @@ -138,12 +138,14 @@ 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.lock()->Amt()); - vf2d quantityTextScale=rect.size/48.f; - vf2d textSize=vf2d(game->GetTextSizeProp(quantityText))*quantityTextScale; - vf2d drawPos=rect.pos+rect.size-textSize; - if(itemRef.lock()->Amt()!=INFINITE){ - window.DrawShadowStringDecal(drawPos,quantityText,WHITE,BLACK,quantityTextScale); + if(!ISBLANK(itemRef)){ + 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.lock()->Amt()!=INFINITE){ + window.DrawShadowStringDecal(drawPos,quantityText,WHITE,BLACK,quantityTextScale); + } } } } diff --git a/Crawler/MenuLabel.h b/Crawler/MenuLabel.h index 4f2ede14..2e75645f 100644 --- a/Crawler/MenuLabel.h +++ b/Crawler/MenuLabel.h @@ -83,9 +83,9 @@ protected: } if(shadow){ - window.DrawShadowStringPropDecal(drawPos,label,WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x); + window.DrawShadowStringPropDecal(drawPos,label,WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x,1.0f); }else{ - window.DrawStringPropDecal(drawPos,label,WHITE,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x); + window.DrawStringPropDecal(drawPos,label,WHITE,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x,1.0f); } } }; \ No newline at end of file diff --git a/Crawler/Merchant.cpp b/Crawler/Merchant.cpp index 5ad7ec93..ef7f22cb 100644 --- a/Crawler/Merchant.cpp +++ b/Crawler/Merchant.cpp @@ -73,8 +73,8 @@ Merchant&Merchant::AddMerchant(Chapter chapter){ void Merchant::AddItem(IT item,uint32_t amt,uint8_t enhancementLevel){ shopItems.push_back(std::make_shared(amt,item,enhancementLevel)); + UpdateSortedItemsList(); if(&GetCurrentTravelingMerchant()==this){ - UpdateSortedItemsList(); Menu::MerchantInventorySlotsUpdated(ITEM_DATA[item].Category()); } } @@ -161,16 +161,27 @@ void Merchant::PurchaseItem(IT item,uint32_t amt){ purchaseFunctionPrimed.Validate(item,amt); uint32_t totalCost=0U; + bool finiteItemPurchased=false; for(std::shared_ptrit:shopItems){ if(it==item){ if(it->Amt()!=INFINITE){ it->SetAmt(it->Amt()-amt); + finiteItemPurchased=true; } totalCost=it->BuyValue()*amt; break; } } + if(finiteItemPurchased){ + size_t removeCount=std::erase_if(shopItems,[](std::shared_ptr it){return it->Amt()==0;}); + if(removeCount>1)ERR("WARNING! Somehow we sold more than one item???") + if(removeCount){ + UpdateSortedItemsList(); + Menu::MerchantInventorySlotsUpdated(ITEM_DATA[item].Category()); + } + } + Inventory::AddItem(item,amt); game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()-totalCost); @@ -190,8 +201,8 @@ void Merchant::SellItem(IT item,uint32_t amt){ break; } } - if(!itemFound){ - AddItem(item); //This may not be a feature we include in future versions of the game. For now let's allow it. + if(!itemFound&&ITEM_DATA[item].CanBePurchased()){ + AddItem(item,amt); //This may not be a feature we include in future versions of the game. For now let's allow it. } totalCost=ITEM_DATA[item].GetSellValue()*amt; diff --git a/Crawler/MerchantWindow.cpp b/Crawler/MerchantWindow.cpp index d57c6588..d3dc4969 100644 --- a/Crawler/MerchantWindow.cpp +++ b/Crawler/MerchantWindow.cpp @@ -77,7 +77,7 @@ void Menu::InitializeMerchantWindow(){ buyTab->selectionType=SelectionType::HIGHLIGHT; auto sellTab=merchantWindow->ADD("Sell Tab",MenuComponent)({{merchantWindow->size.x/2+2,0},{merchantWindow->size.x/2-4,24}},"Sell",[](MenuFuncData data){ - Component(MERCHANT,"Merchant Inventory Display")->Enable(false); + Component(MERCHANT,"Merchant Inventory Display")->Enable(false); Component(MERCHANT,"Buy Tab")->selected=false; Component(MERCHANT,"Inventory Tabs Outline")->Enable(true); for(auto&[category,items]:ITEM_CATEGORIES){ @@ -119,6 +119,7 @@ void Menu::InitializeMerchantWindow(){ Component(data.menu.GetType(),"Item Icon")->UpdateIcon(); return true; },{.padding=1,.size={220-13,28}})END; + inventoryDisplay->SetPriceLabelType(PriceLabel::BUY_LABEL); for(auto&[category,items]:ITEM_CATEGORIES){ Menu::AddMerchantInventoryListener(inventoryDisplay,category); @@ -148,22 +149,24 @@ void Menu::InitializeMerchantWindow(){ button->SetSelectionType(HIGHLIGHT); button->S(A::CATEGORY_NAME)=category; - auto inventoryDisplay=merchantWindow->ADD("Inventory Display - "+category,RowInventoryScrollableWindowComponent)({{72,28},{150,merchantWindow->size.y-44}},category,"Item Name Label","Item Description Label", + auto inventoryDisplay=merchantWindow->ADD("Inventory Display - "+category,RowInventoryScrollableWindowComponent)({{72,28},{150,merchantWindow->size.y-44}},"Item Name Label","Item Description Label", [](MenuFuncData data){ RowItemDisplay*item=dynamic_cast(data.component); - Component(SELL_ITEM,"Item Sell Header")->S(A::ITEM_NAME)=item->GetItem().lock()->ActualName(); - Component(SELL_ITEM,"Price per item Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->BuyValue())); - Component(SELL_ITEM,"Amount to sell Amount Label")->SetLabel("1"); - Component(SELL_ITEM,"Total Price Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->BuyValue())); - Merchant&merchant=Merchant::GetCurrentTravelingMerchant(); - bool canPurchase=merchant.CanPurchaseItem(item->GetItem().lock()->ActualName(),1); - - std::string colorCode=""; - if(!canPurchase)colorCode="#FF0000"; - Component(SELL_ITEM,"Total Price Amount Label")->SetLabel(colorCode+std::to_string(item->GetItem().lock()->BuyValue())); - Component(SELL_ITEM,"Item Purchase Header")->SetLabel("Buying "+item->GetItem().lock()->DisplayName()); - Component(SELL_ITEM,"Purchase Button")->SetGrayedOut(!merchant.CanPurchaseItem(item->GetItem().lock()->ActualName(),1)); - Menu::OpenMenu(SELL_ITEM); + if(item->GetItem().lock()->CanBeSold()){ + Component(SELL_ITEM,"Item Sell Header")->S(A::ITEM_NAME)=item->GetItem().lock()->ActualName(); + Component(SELL_ITEM,"Price per item Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->SellValue())); + Component(SELL_ITEM,"Amount to sell Amount Label")->SetLabel("1"); + Component(SELL_ITEM,"Total Price Amount Label")->SetLabel(std::to_string(item->GetItem().lock()->SellValue())); + Merchant&merchant=Merchant::GetCurrentTravelingMerchant(); + bool canPurchase=merchant.CanSellItem(item->GetItem().lock()->ActualName(),1); + + std::string colorCode=""; + if(!canPurchase)colorCode="#FF0000"; + Component(SELL_ITEM,"Total Price Amount Label")->SetLabel(colorCode+std::to_string(item->GetItem().lock()->SellValue())); + Component(SELL_ITEM,"Item Sell Header")->SetLabel("Selling "+item->GetItem().lock()->DisplayName()); + Component(SELL_ITEM,"Sell Button")->SetGrayedOut(!merchant.CanSellItem(item->GetItem().lock()->ActualName(),1)); + Menu::OpenMenu(SELL_ITEM); + } return true; }, [](MenuFuncData data){ @@ -176,6 +179,7 @@ void Menu::InitializeMerchantWindow(){ Component(data.menu.GetType(),"Item Icon")->UpdateIcon(); return true; },{.padding=1,.size={137,28}})END; + inventoryDisplay->SetPriceLabelType(PriceLabel::SELL_LABEL); if(first){ merchantWindow->S(A::LAST_INVENTORY_TYPE_OPENED)=category; diff --git a/Crawler/RowInventoryScrollableWindowComponent.h b/Crawler/RowInventoryScrollableWindowComponent.h index aa3a5e5d..c09a06ab 100644 --- a/Crawler/RowInventoryScrollableWindowComponent.h +++ b/Crawler/RowInventoryScrollableWindowComponent.h @@ -40,9 +40,11 @@ All rights reserved. #include "RowItemDisplay.h" class RowInventoryScrollableWindowComponent:public InventoryScrollableWindowComponent{ +protected: + PriceLabel::PriceLabel priceLabel=PriceLabel::NONE; public: - inline RowInventoryScrollableWindowComponent(geom2d::rectrect,ITCategory invType,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,std::functioninventoryButtonHoverAction,std::functioninventoryButtonMouseOutAction,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) - :InventoryScrollableWindowComponent(rect,invType,itemNameLabelName,itemDescriptionLabelName,inventoryButtonClickAction,inventoryButtonHoverAction,inventoryButtonMouseOutAction,options,inventoryButtonsActive,attributes){} + inline RowInventoryScrollableWindowComponent(geom2d::rectrect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,std::functioninventoryButtonHoverAction,std::functioninventoryButtonMouseOutAction,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) + :InventoryScrollableWindowComponent(rect,itemNameLabelName,itemDescriptionLabelName,inventoryButtonClickAction,inventoryButtonHoverAction,inventoryButtonMouseOutAction,options,inventoryButtonsActive,attributes){} virtual inline void SetCompactDescriptions(bool compact)override final{ if(compact)this->compact=COMPACT; @@ -57,8 +59,20 @@ public: } } + virtual inline void SetPriceLabelType(PriceLabel::PriceLabel labelType)final{ + this->priceLabel=labelType; + for(MenuComponent*component:components){ + RowItemDisplay*itemButton=dynamic_cast(component); + if(itemButton){ + itemButton->SetPriceLabelType(labelType); + }else{ + ERR("WARNING! Attempting to cast a button that isn't a MenuItemButton!"); + } + } + } + virtual inline void AddButtonOnSlotUpdate(ITCategory cat)override{ - size_t invSize=Inventory::get(cat).size(); + size_t invSize=components.size()+1; int invWidth=int(rect.size.x/(float(options.size.x)+options.padding)); int x=int((invSize-1)%invWidth); int y=int((invSize-1)/invWidth); @@ -69,6 +83,7 @@ public: auto newItem=ADD("item_"+cat+"_"+std::to_string(itemIndex),RowItemDisplay)({totalSpacing*vf2d{float(x),float(y)},buttonSize},Inventory::GetInventorySlot(cat,itemIndex),inventoryButtonClickAction,itemNameLabelName,itemDescriptionLabelName,inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END; newItem->SetCompactDescriptions(compact==COMPACT); + newItem->SetPriceLabelType(priceLabel); newItem->SetHoverFunc(inventoryButtonHoverAction); newItem->SetMouseOutFunc(inventoryButtonMouseOutAction); diff --git a/Crawler/RowItemDisplay.h b/Crawler/RowItemDisplay.h index 2cb6e5f3..805cc994 100644 --- a/Crawler/RowItemDisplay.h +++ b/Crawler/RowItemDisplay.h @@ -41,6 +41,15 @@ All rights reserved. #include "MenuLabel.h" INCLUDE_game +INCLUDE_GFX + +namespace PriceLabel{ + enum PriceLabel{ + NONE, + BUY_LABEL, + SELL_LABEL, + }; +} class RowItemDisplay:public MenuComponent{ std::weak_ptritemRef; @@ -48,6 +57,7 @@ class RowItemDisplay:public MenuComponent{ std::string itemDescriptionLabelName; CompactText compact=NON_COMPACT; bool showQuantity=true; + PriceLabel::PriceLabel priceLabel=PriceLabel::NONE; public: 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){ @@ -71,6 +81,28 @@ public: 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}); } + + if(priceLabel!=PriceLabel::NONE){ + if(priceLabel==PriceLabel::SELL_LABEL){ + std::string priceText=std::to_string(itemRef.lock()->SellValue()); + if(itemRef.lock()->SellValue()==0)priceText="Cannot Sell"; + vf2d priceTextSize=vf2d(game->GetTextSize(priceText)); + + if(itemRef.lock()->SellValue()!=0){ + window.DrawShadowStringDecal(rect.pos+vf2d{rect.size.x-priceTextSize.x-15,3},priceText); + window.DrawDecal(rect.pos+vf2d{rect.size.x-14,1},GFX["currency_coin.png"].Decal(),{0.5f,0.5f}); + }else{ + vf2d priceTextSize=vf2d(game->GetTextSizeProp(priceText))*vf2d{0.5f,0.85f}; + window.DrawShadowStringPropDecal(rect.pos+vf2d{rect.size.x-priceTextSize.x-1,3},priceText,WHITE,BLACK,{0.5f,0.85f}); + } + }else{ + std::string priceText=std::to_string(itemRef.lock()->BuyValue()); + vf2d priceTextSize=vf2d(game->GetTextSize(priceText)); + + window.DrawShadowStringDecal(rect.pos+vf2d{rect.size.x-priceTextSize.x-15,3},priceText); + window.DrawDecal(rect.pos+vf2d{rect.size.x-14,1},GFX["currency_coin.png"].Decal(),{0.5f,0.5f}); + } + } if(Inventory::GetSlotEquippedIn(itemRef)!=EquipSlot::NONE){ window.DrawShadowStringDecal(rect.pos+vf2d{2,2}+iconSize-vf2d{8,8},"E",GREEN,VERY_DARK_GREEN); @@ -128,4 +160,7 @@ public: virtual inline void OnHover()override{ UpdateLabel(); } + inline void SetPriceLabelType(PriceLabel::PriceLabel labelType){ + this->priceLabel=labelType; + } }; \ No newline at end of file diff --git a/Crawler/RowMerchantInventoryScrollableWindowComponent.h b/Crawler/RowMerchantInventoryScrollableWindowComponent.h index dd612675..9ae0429a 100644 --- a/Crawler/RowMerchantInventoryScrollableWindowComponent.h +++ b/Crawler/RowMerchantInventoryScrollableWindowComponent.h @@ -41,7 +41,7 @@ All rights reserved. class RowMerchantInventoryScrollableWindowComponent:public RowInventoryScrollableWindowComponent{ public: inline RowMerchantInventoryScrollableWindowComponent(geom2d::rectrect,std::string itemNameLabelName,std::string itemDescriptionLabelName,std::functioninventoryButtonClickAction,std::functioninventoryButtonHoverAction,std::functioninventoryButtonMouseOutAction,InventoryWindowOptions options={.padding=8,.size={24,24}},bool inventoryButtonsActive=true,ComponentAttr attributes=ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE) - :RowInventoryScrollableWindowComponent(rect,"",itemNameLabelName,itemDescriptionLabelName,inventoryButtonClickAction,inventoryButtonHoverAction,inventoryButtonMouseOutAction,options,inventoryButtonsActive,attributes){} + :RowInventoryScrollableWindowComponent(rect,itemNameLabelName,itemDescriptionLabelName,inventoryButtonClickAction,inventoryButtonHoverAction,inventoryButtonMouseOutAction,options,inventoryButtonsActive,attributes){} virtual inline void OnInventorySlotsUpdate(ITCategory cat)override{ @@ -51,13 +51,16 @@ public: AddButtonOnSlotUpdate(cat); }else if(components.size()>merchantInv.size()){ //There are empty spots, so let's clean up. - RemoveEmptySlots(); + RemoveAllComponents(); + for(std::shared_ptr item:merchantInv){ + AddButtonOnSlotUpdate(cat); + } } } virtual inline void AddButtonOnSlotUpdate(ITCategory cat)override{ const std::vector>&merchantInv=Merchant::GetCurrentTravelingMerchant().GetShopItems(); - size_t invSize=merchantInv.size(); + size_t invSize=components.size()+1; int invWidth=int(rect.size.x/(float(options.size.x)+options.padding)); int x=int((invSize-1)%invWidth); int y=int((invSize-1)/invWidth); @@ -66,21 +69,10 @@ public: vf2d buttonSize=options.size; vf2d totalSpacing={options.padding+buttonSize.x,options.padding+buttonSize.y}; - auto newItem=ADD("item_"+cat+"_"+std::to_string(itemIndex),RowItemDisplay)({totalSpacing*vf2d{float(x),float(y)},buttonSize},Merchant::GetCurrentTravelingMerchant().GetShopItems()[itemIndex],inventoryButtonClickAction,itemNameLabelName,itemDescriptionLabelName,inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END; + auto newItem=ADD("merchant_item_"+cat+"_"+std::to_string(itemIndex),RowItemDisplay)({totalSpacing*vf2d{float(x),float(y)},buttonSize},Merchant::GetCurrentTravelingMerchant().GetShopItems()[itemIndex],inventoryButtonClickAction,itemNameLabelName,itemDescriptionLabelName,inventoryButtonsActive?ButtonAttr::NONE:ButtonAttr::UNSELECTABLE)END; newItem->SetCompactDescriptions(compact==COMPACT); + newItem->SetPriceLabelType(priceLabel); newItem->SetHoverFunc(inventoryButtonHoverAction); newItem->SetMouseOutFunc(inventoryButtonMouseOutAction); - - //Since we are indexing into a vector reference which is inevitably going to get erased as we add more items, - //we must update all previous menu component references in case that memory has been overwritten! - for(int counter=0;MenuComponent*component:components){ - RowItemDisplay*item=dynamic_cast(component); - if(item!=nullptr){ - item->SetItem(merchantInv[itemIndex]); - }else{ - ERR("WARNING! Could not properly cast item to RowItemDisplay* type!"); - } - counter++; - } } }; \ No newline at end of file diff --git a/Crawler/ScrollableWindowComponent.h b/Crawler/ScrollableWindowComponent.h index 88c76bf8..aea37643 100644 --- a/Crawler/ScrollableWindowComponent.h +++ b/Crawler/ScrollableWindowComponent.h @@ -236,6 +236,7 @@ public: components.push_back(button); button->renderInMain=false; //Now we are in control! button->parentComponent=this; + button->disabled=disabled; CalculateBounds(); @@ -259,6 +260,9 @@ public: } virtual inline void Enable(bool enabled)override final{ disabled=!enabled; + for(MenuComponent*component:components){ + component->Enable(enabled); + } if(upButton){upButton->Enable(enabled);} if(downButton){downButton->Enable(enabled);} }; diff --git a/Crawler/SellItemWindow.cpp b/Crawler/SellItemWindow.cpp index 1b43bdb6..bca7c1f8 100644 --- a/Crawler/SellItemWindow.cpp +++ b/Crawler/SellItemWindow.cpp @@ -39,7 +39,57 @@ All rights reserved. #include "Menu.h" #include "MenuLabel.h" +using A=Attribute; + void Menu::InitializeSellItemWindow(){ - Menu*sellItemWindow=CreateMenu(SELL_ITEM,CENTERED,{120,72}); + Menu*sellItemWindow=CreateMenu(SELL_ITEM,CENTERED,{192,72}); + static auto GetQuantity=[&](){ + return std::stoi(Component(SELL_ITEM,"Amount to sell Amount Label")->GetLabel()); + }; + static auto UpdateMenu=[&](int32_t qty){ + qty=std::clamp(qty,1,99); + int pricePerItem=std::stoi(Component(SELL_ITEM,"Price per item Amount Label")->GetLabel()); + Component(SELL_ITEM,"Amount to sell Amount Label")->SetLabel(std::to_string(qty)); + + Merchant&merchant=Merchant::GetCurrentTravelingMerchant(); + const std::string&item=Component(SELL_ITEM,"Item Sell Header")->GetString(A::ITEM_NAME); + bool canSell=merchant.CanSellItem(item,GetQuantity()); + + std::string colorCode=""; + if(!canSell)colorCode="#FF0000"; + Component(SELL_ITEM,"Total Price Amount Label")->SetLabel(colorCode+std::to_string(qty*pricePerItem)); + Component(SELL_ITEM,"Sell Button")->SetGrayedOut(!canSell); + }; + + sellItemWindow->ADD("Item Sell Header",MenuLabel)({{2,2},{188,12}},"Selling ",1,ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND|ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END; + + sellItemWindow->ADD("Price Per Item Label",MenuLabel)({{4,18},{188,12}},"Price Per Item",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; + sellItemWindow->ADD("Amount to Sell Label",MenuLabel)({{4,34},{188,12}},"Amount to Sell",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; + sellItemWindow->ADD("Price Label",MenuLabel)({{4,50},{188,12}},"Total Price",1.0f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; + + sellItemWindow->ADD("Price per item Amount Label",MenuLabel)({{sellItemWindow->size.x/2+28,18},{72,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END; + sellItemWindow->ADD("Amount to sell Amount Label",MenuLabel)({{sellItemWindow->size.x/2+48,34},{32,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::FIT_TO_LABEL)END; + + sellItemWindow->ADD("Increase sell amount Button",MenuComponent)({{sellItemWindow->size.x/2+80+2,34},{12,12}},"+",[&](MenuFuncData data){ + UpdateMenu(GetQuantity()+1); + return true; + })END; + sellItemWindow->ADD("Decrease sell amount Button",MenuComponent)({{sellItemWindow->size.x/2+48-14,34},{12,12}},"-",[](MenuFuncData data){ + UpdateMenu(GetQuantity()-1); + return true; + })END; + + sellItemWindow->ADD("Total Price Amount Label",MenuLabel)({{sellItemWindow->size.x/2+28,50},{72,12}},"0",1.0f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END; + sellItemWindow->ADD("Sell Button",MenuComponent)({{sellItemWindow->size.x/2+18,70},{64,12}},"Sell",[&](MenuFuncData data){ + Merchant&merchant=Merchant::GetCurrentTravelingMerchant(); + const std::string&item=Component(SELL_ITEM,"Item Sell Header")->GetString(A::ITEM_NAME); + merchant.SellItem(item,GetQuantity()); + Menu::CloseMenu(); + return true; + })END; + sellItemWindow->ADD("Cancel Button",MenuComponent)({{sellItemWindow->size.x/2-82,70},{64,12}},"Cancel",[](MenuFuncData data){ + Menu::CloseMenu(); + return true; + })END; } \ No newline at end of file diff --git a/Crawler/TODO.txt b/Crawler/TODO.txt index 494decfa..a7edb971 100644 --- a/Crawler/TODO.txt +++ b/Crawler/TODO.txt @@ -1,6 +1,5 @@ January 1st =========== -Sell Item Merchant Screen Blacksmith Item Crafting Screen Randomized Item Stats - Get Item may return multiples of the same item. @@ -9,9 +8,10 @@ Randomized Item Stats The Hub / NPC Interactions Save/Load Game - Save Inventory Items/Equips - - Player Base Level/Stats + - Player Base Level/Stats/Accumulated Exp - Unlock Progress - World Map Location + - Chapter # Audio Engine - Audio Ambience Zones - Menu Sound Effects diff --git a/Crawler/Version.h b/Crawler/Version.h index 7d9a5a02..3c901d10 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 4798 +#define VERSION_BUILD 4854 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Crawler/assets/config/gfx/gfx.txt b/Crawler/assets/config/gfx/gfx.txt index 418458a6..36547d5e 100644 --- a/Crawler/assets/config/gfx/gfx.txt +++ b/Crawler/assets/config/gfx/gfx.txt @@ -43,6 +43,7 @@ Images GFX_SqareSkillIcon = square_skill_overlay_icon.png GFX_SqareSkillIconEmpty = square_skill_overlay_icon_empty.png GFX_Money = money.png + GFX_CurrencyCoin = currency_coin.png # Ability Icons GFX_Warrior_BattleCry_Icon = Ability Icons/battlecry.png diff --git a/Crawler/olcPGEX_ViewPort.h b/Crawler/olcPGEX_ViewPort.h index 8ee68e82..a54b9771 100644 --- a/Crawler/olcPGEX_ViewPort.h +++ b/Crawler/olcPGEX_ViewPort.h @@ -736,7 +736,7 @@ void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view pge->Clear(BLANK); pge->DrawString({0,0},sText,WHITE,1U,width/scale.x); newDecal->Update(); - float adjustedShadowSizeFactor=shadowSizeFactor*4; + float adjustedShadowSizeFactor=shadowSizeFactor*4/std::max(scale.x,scale.y); Decal*newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor*2,(imageSize.y/scale.x*4)+adjustedShadowSizeFactor*2)); garbageCollector[key+"_SHADOW"].decal=newShadowDecal; pge->SetDrawTarget(newShadowDecal->sprite); @@ -760,7 +760,7 @@ void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view } return false; }); - DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor}*scale,garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); + DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); DrawDecal(pos,garbageCollector[key].decal,scale,col); } @@ -784,7 +784,7 @@ void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_ pge->Clear(BLANK); pge->DrawStringProp({0,0},sText,WHITE,1U,width/scale.x); newDecal->Update(); - float adjustedShadowSizeFactor=shadowSizeFactor*4; + float adjustedShadowSizeFactor=shadowSizeFactor*4/std::max(scale.x,scale.y); Decal*newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor*2,(imageSize.y/scale.x*4)+adjustedShadowSizeFactor*2)); garbageCollector[key+"_SHADOW"].decal=newShadowDecal; pge->SetDrawTarget(newShadowDecal->sprite); @@ -808,7 +808,7 @@ void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_ } return false; }); - DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor}*scale,garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); + DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); DrawDecal(pos,garbageCollector[key].decal,scale,col); } diff --git a/Crawler/olcPixelGameEngine.h b/Crawler/olcPixelGameEngine.h index 29f780e7..33e019d2 100644 --- a/Crawler/olcPixelGameEngine.h +++ b/Crawler/olcPixelGameEngine.h @@ -3440,7 +3440,7 @@ namespace olc Clear(BLANK); DrawString({0,0},sText,WHITE,1U,width/scale.x); newDecal->Update(); - float adjustedShadowSizeFactor=shadowSizeFactor*4; + float adjustedShadowSizeFactor=shadowSizeFactor*4/std::max(scale.x,scale.y); Decal*newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor*2,(imageSize.y/scale.x*4)+adjustedShadowSizeFactor*2)); garbageCollector[key+"_SHADOW"].decal=newShadowDecal; SetDrawTarget(newShadowDecal->sprite); @@ -3464,7 +3464,7 @@ namespace olc } return false; }); - DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor}*scale,garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); + DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); DrawDecal(pos,garbageCollector[key].decal,scale,col); } @@ -3564,7 +3564,7 @@ namespace olc Clear(BLANK); DrawStringProp({0,0},sText,WHITE,1U,width/scale.x); newDecal->Update(); - float adjustedShadowSizeFactor=shadowSizeFactor*4; + float adjustedShadowSizeFactor=shadowSizeFactor*4/std::max(scale.x,scale.y); Decal*newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor*2,(imageSize.y/scale.x*4)+adjustedShadowSizeFactor*2)); garbageCollector[key+"_SHADOW"].decal=newShadowDecal; SetDrawTarget(newShadowDecal->sprite); @@ -3588,7 +3588,7 @@ namespace olc } return false; }); - DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor}*scale,garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); + DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); DrawDecal(pos,garbageCollector[key].decal,scale,col); }