diff --git a/Crawler/CharacterMenuWindow.cpp b/Crawler/CharacterMenuWindow.cpp index a1325921..3633d7b5 100644 --- a/Crawler/CharacterMenuWindow.cpp +++ b/Crawler/CharacterMenuWindow.cpp @@ -134,7 +134,7 @@ void Menu::InitializeCharacterMenuWindow(){ ScrollableWindowComponent*equipList=Component(data.component->parentMenu,"Equip List"); equipList->RemoveAllComponents(); for(int counter=0;Item&it:availableEquipment){ - Item&itemInvRef=Inventory::GetItem(it.Name()); + Item&itemInvRef=Inventory::GetItem(it.ActualName()); auto equip=equipList->ADD("Equip Item "+std::to_string(counter),RowItemDisplay)({{2,2+counter*28.f},{120-15,28}},itemInvRef, [](MenuFuncData data){ RowItemDisplay*comp=dynamic_cast(data.component); diff --git a/Crawler/Crawler.cpp b/Crawler/Crawler.cpp index 6ac03471..4d2eaa65 100644 --- a/Crawler/Crawler.cpp +++ b/Crawler/Crawler.cpp @@ -202,6 +202,10 @@ bool Crawler::OnUserCreate(){ utils::datafile::INITIAL_SETUP_COMPLETE=true; ValidateGameStatus(); //Checks to make sure everything has been initialized properly. + + Merchant::RandomizeTravelingMerchant(); + Merchant&myMerchant=Merchant::GetCurrentTravelingMerchant(); + const std::vector&itemsAvailable=myMerchant.GetShopItems(); return true; } @@ -1725,6 +1729,7 @@ void Crawler::ChangePlayerClass(Class cl){ Ability itemAbility1=player->useItem1; Ability itemAbility2=player->useItem2; Ability itemAbility3=player->useItem3; + uint32_t oldMoney=player->money; switch(cl){ case WARRIOR:{ player.reset(NEW Warrior(player.get())); @@ -1750,6 +1755,7 @@ void Crawler::ChangePlayerClass(Class cl){ player->SetBaseStat(ItemAttribute::attack,DATA.GetProperty(player->GetClassName()+".BaseAtk").GetInt()); player->hpGrowthRate=float(DATA.GetProperty(player->GetClassName()+".HealthGrowthRate").GetReal()); player->atkGrowthRate=float(DATA.GetProperty(player->GetClassName()+".AtkGrowthRate").GetReal()); + player->money=oldMoney; sig::Animation::SetupPlayerAnimations(); GetPlayer()->UpdateIdleAnimation(DOWN); GetPlayer()->SetItem1UseFunc(itemAbility1); @@ -2239,7 +2245,7 @@ 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(loadout[slot].Name()); + Inventory::UseItem(loadout[slot].ActualName()); GetLoadoutItem(slot).OnUseAction(); GetLoadoutItem(slot).amt--; return true; diff --git a/Crawler/Crawler.vcxproj b/Crawler/Crawler.vcxproj index 9300c074..93ab2218 100644 --- a/Crawler/Crawler.vcxproj +++ b/Crawler/Crawler.vcxproj @@ -316,6 +316,10 @@ + + + + diff --git a/Crawler/Crawler.vcxproj.filters b/Crawler/Crawler.vcxproj.filters index 290c1d93..8ba3bc07 100644 --- a/Crawler/Crawler.vcxproj.filters +++ b/Crawler/Crawler.vcxproj.filters @@ -309,6 +309,9 @@ Header Files + + Header Files + diff --git a/Crawler/EquipSlotButton.h b/Crawler/EquipSlotButton.h index c6dae1a1..52a66bcd 100644 --- a/Crawler/EquipSlotButton.h +++ b/Crawler/EquipSlotButton.h @@ -52,7 +52,7 @@ public: inline void OnEquipStatsUpdate()override{ Item&equip=*Inventory::GetEquip(slot); if(!equip.IsBlank()){ - icon=equip.Decal(); + icon=const_cast(equip.Decal()); SetItem(equip); }else{ icon=nullptr; diff --git a/Crawler/FunctionPriming.h b/Crawler/FunctionPriming.h new file mode 100644 index 00000000..a626edaf --- /dev/null +++ b/Crawler/FunctionPriming.h @@ -0,0 +1,63 @@ +#pragma region License +/* +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2018 - 2022 OneLoneCoder.com + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions or derivations of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions or derivative works in binary form must reproduce the above +copyright notice. This list of conditions and the following disclaimer must be +reproduced in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may +be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +Portions of this software are copyright © 2023 The FreeType +Project (www.freetype.org). Please see LICENSE_FT.txt for more information. +All rights reserved. +*/ +#pragma endregion +#pragma once +#include "Error.h" +#include +using IT=std::string; + +struct FunctionPrimingData{ + bool primed=false; + std::string dependentFunction=""; + inline FunctionPrimingData(std::string dependentFunction) + :dependentFunction(dependentFunction){} +}; + +struct MerchantFunctionPrimingData:public FunctionPrimingData{ + IT item=""; + uint32_t amt=0; + inline MerchantFunctionPrimingData(std::string dependentFunction) + :FunctionPrimingData(dependentFunction){} + virtual inline void Validate(IT item,uint32_t amt)const{ + if(!primed)ERR(std::format("WARNING! {} should be called before running this function! Priming Requirement!",dependentFunction)); + if(this->item!=item)ERR(std::format("WARNING! Primed items are not matching! {}!={}",this->item,item)); + if(this->amt!=amt)ERR(std::format("WARNING! Primed amounts are not matching! {}!={}",this->amt,amt)); + } + inline bool operator=(bool rhs){ + return primed=rhs; + } +}; \ No newline at end of file diff --git a/Crawler/InventoryConsumableWindow.cpp b/Crawler/InventoryConsumableWindow.cpp index faa983d4..cbd67edd 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().Name()); + data.game->SetLoadoutItem(button->selected,button->GetItem().ActualName()); return true; })END; diff --git a/Crawler/Item.cpp b/Crawler/Item.cpp index 73291787..ce35ddb8 100644 --- a/Crawler/Item.cpp +++ b/Crawler/Item.cpp @@ -102,6 +102,8 @@ void ItemInfo::InitializeItems(){ std::vector slot; float cooldownTime="Item.Item Cooldown Time"_F; std::vectorstatValueList; + uint32_t sellValue=0; + uint32_t buyValue=0; for(auto&[itemKey,itemValue]:data[key].GetKeys()){ std::string keyName=itemKey; if(keyName=="Description"){ @@ -131,6 +133,12 @@ void ItemInfo::InitializeItems(){ }else if(keyName=="PartofSet"){ setName=data[key][keyName].GetString(); + } + if(keyName=="BuyValue"){ + buyValue=data[key][keyName].GetInt(); + }else + if(keyName=="SellValue"){ + sellValue=data[key][keyName].GetInt(); }else{ //THis is a custom override modifier for a script. NO-OP } } @@ -162,6 +170,8 @@ void ItemInfo::InitializeItems(){ it.cooldownTime=cooldownTime; it.slot=EquipSlot::NONE; it.set=setName; + it.buyValue=buyValue; + it.sellValue=sellValue; if(slot.size()>0){ for(std::string&s:slot){ if(!nameToEquipSlot.count(s))ERR("WARNING! Tried to add item "<Name(); +}; +const std::string Item::DisplayName()const{ if(IsBlank())return ""; - std::string name=it->Name(); + std::string name=ActualName(); if(IsEquippable()&&EnhancementLevel()>0){ name+=" [+"+std::to_string(EnhancementLevel())+"]"; } return name; -}; -bool Item::IsEquippable()const{ +} +const bool Item::IsEquippable()const{ return Category()=="Equipment"||Category()=="Accessories"; } -std::string Item::Description(CompactText compact){ +const std::string Item::Description(CompactText compact)const{ std::string description=it->Description(); if(IsEquippable()){ description+='\n'; description+=GetStats().GetStatsString(compact); if(ItemSet()){ - const ::ItemSet*const set=ItemSet().value(); + const::ItemSet*const set=ItemSet().value(); if(compact==COMPACT){ description+="\n"+set->GetSetName()+" Set - "; }else{ @@ -424,48 +438,52 @@ std::string Item::Description(CompactText compact){ const ITCategory Item::Category()const{ return it->Category(); }; -::Decal*Item::Decal(){ +const::Decal*const Item::Decal()const{ return it->Decal(); }; -ItemScript&Item::OnUseAction(){ +const ItemScript&Item::OnUseAction()const{ return it->OnUseAction(); }; -std::string ItemInfo::Name(){ +const std::string&ItemInfo::Name()const{ return name; }; -std::string ItemInfo::Description(){ +const std::string&ItemInfo::Description()const{ return description; }; -ITCategory ItemInfo::Category(){ +const ITCategory ItemInfo::Category()const{ return category; }; -::Decal*ItemInfo::Decal(){ +const::Decal*const ItemInfo::Decal()const{ return img; }; -ItemScript&ItemInfo::OnUseAction(){ +const ItemScript&ItemInfo::OnUseAction()const{ return ITEM_SCRIPTS.at(useFunc); }; -bool Item::IsBlank(){ +const bool Item::IsBlank()const{ 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.Name());//Normally we want to clear all the items that are actually in our inventory...But... + 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.Name(),itemCategory,uint32_t(itemQuantity)); + RemoveItem(item.ActualName(),itemCategory,uint32_t(itemQuantity)); } } -bool Item::operator==(const Item&rhs)const{ +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; @@ -487,7 +505,7 @@ void ItemOverlay::Draw(){ Pixel lightCol=Menu::GetCurrentTheme().GetButtonCol()*1.2f; game->GradientFillRectDecal(pos,{item.width,8},darkCol,darkCol,darkCol,lightCol); game->DrawRectDecal(pos,{item.width,8},Menu::GetCurrentTheme().GetHighlightCol()); - game->DrawDecal(pos,item.it.Decal(),{itemScale,itemScale}); + game->DrawDecal(pos,const_cast(item.it.Decal()),{itemScale,itemScale}); game->DrawShadowStringPropDecal(pos+vf2d{itemScale*24+2,2},item.it.Name(),WHITE,BLACK,{0.5f,0.7f}); counter++; } @@ -498,11 +516,11 @@ void ItemOverlay::AddToItemOverlay(const ItemInfo&it){ std::for_each(items.begin(),items.end(),[](ItemOverlay&it){it.ResetTimer();}); } -float ItemInfo::CastTime(){ +const float ItemInfo::CastTime()const{ return castTime; } -float ItemInfo::CooldownTime(){ +const float ItemInfo::CooldownTime()const{ return cooldownTime; } @@ -510,11 +528,11 @@ void ItemOverlay::ResetTimer(){ timer=0; } -float Item::CastTime(){ +const float Item::CastTime()const{ return it->CastTime(); } -float Item::CooldownTime(){ +const float Item::CooldownTime()const{ return it->CooldownTime(); } @@ -524,7 +542,7 @@ const Stats&EnhancementInfo::operator[](int level)const{ const std::optionalItemInfo::ItemSet()const{ if(ItemSet::sets.count(set)){ - return &ItemSet::sets[set]; + return &ItemSet::sets.at(set); } return {}; }; @@ -560,17 +578,17 @@ EquipSlot Inventory::GetSlotEquippedIn(Item&it){ EquipSlot slot=EquipSlot(i); Item*equip=GetEquip(slot); if(equip==nullptr)continue; - if(equip->Name()==it.Name())return slot; + if(equip->ActualName()==it.ActualName())return slot; } return EquipSlot::NONE; }; Item*Inventory::GetEquip(EquipSlot slot){ return Inventory::equipment[slot]; } -EquipSlot Item::GetEquipSlot(){ +const EquipSlot Item::GetEquipSlot()const{ return it->Slot(); } -EquipSlot ItemInfo::Slot(){ +const EquipSlot ItemInfo::Slot()const{ return slot; } @@ -600,7 +618,7 @@ void ItemInfo::InitializeSets(){ } } -Stats ItemInfo::GetStats(int enhancementLevel){ +const Stats&ItemInfo::GetStats(int enhancementLevel)const{ if(enhancement.size()<=enhancementLevel){ return {}; } @@ -620,11 +638,11 @@ const std::optionalItem::ItemSet()const{ return it->ItemSet(); }; -uint8_t Item::EnhancementLevel()const{ +const uint8_t Item::EnhancementLevel()const{ return enhancementLevel; }; void Item::EnhanceItem(){ - if(enhancementLevel+1>"Item.Item Max Enhancement Level"_I)ERR("WARNING! Attempted to enhance "<"Item.Item Max Enhancement Level"_I)ERR("WARNING! Attempted to enhance "<0; +} +const bool ItemInfo::CanBePurchased()const{ + return GetBuyValue()>0; +} + +const uint32_t Item::BuyValue()const{ + return it->GetBuyValue(); +} +const uint32_t Item::SellValue()const{ + return it->GetSellValue(); +} +const bool Item::CanBeSold()const{ + return it->CanBeSold(); +} +const bool Item::CanBePurchased()const{ + return it->CanBePurchased(); +} + + +void Item::SetAmt(uint32_t newAmt){ + amt=newAmt; } \ No newline at end of file diff --git a/Crawler/Item.h b/Crawler/Item.h index e056f22a..fa7d1b0f 100644 --- a/Crawler/Item.h +++ b/Crawler/Item.h @@ -44,6 +44,7 @@ All rights reserved. #include "AttributableStat.h" #include "BitwiseEnum.h" #include +#include "Merchant.h" class Crawler; class ItemInfo; @@ -129,31 +130,42 @@ class Item{ friend class Inventory; friend class Crawler; friend class Menu; + friend void Merchant::PurchaseItem(IT item,uint32_t amt); + friend void Merchant::SellItem(IT item,uint32_t amt); private: //The amount in the current item stack. uint32_t amt; uint8_t enhancementLevel; ItemInfo*it; + void SetAmt(uint32_t newAmt); public: Item(); Item(uint32_t amt,IT item,uint8_t enhancementLevel=0); - uint32_t Amt(); - std::string Name(); - std::string Description(CompactText compact=COMPACT); + uint32_t Amt()const; + //Use this for places where the item name is used for item tracking. NOT for drawing. Hooks directly into item info's base item name. + const std::string&ActualName()const; + //Use for places where the item name is actually displayed. Provides modified text that shows additional information like enhancement levels. + const std::string DisplayName()const; + const std::string Description(CompactText compact=COMPACT)const; const ITCategory Category()const; - EquipSlot GetEquipSlot(); - ::Decal*Decal(); + const EquipSlot GetEquipSlot()const; + const::Decal*const Decal()const; const Stats GetStats()const; - ItemScript&OnUseAction(); - float CastTime(); - float CooldownTime(); - bool IsBlank(); - uint8_t EnhancementLevel()const; + const ItemScript&OnUseAction()const; + const float CastTime()const; + const float CooldownTime()const; + const bool IsBlank()const; + const uint8_t EnhancementLevel()const; void EnhanceItem(); static Item BLANK; - bool operator==(const Item&rhs)const; + const bool operator==(const Item&rhs)const; + const bool operator==(const IT&rhs)const; const std::optionalItemSet()const; - bool IsEquippable()const; + const bool IsEquippable()const; + const uint32_t BuyValue()const; + const uint32_t SellValue()const; + const bool CanBeSold()const; + const bool CanBePurchased()const; }; class Inventory{ @@ -220,7 +232,8 @@ class ItemInfo{ //Custom properties for this specific item's script. static utils::datafile NOPROPS; ItemProps customProps; - + uint32_t buyValue=0; + uint32_t sellValue=0; private: static void InitializeScripts(); static void InitializeSets(); @@ -228,20 +241,24 @@ private: public: static void InitializeItems(); ItemInfo(); - std::string Name(); - std::string Description(); - ITCategory Category(); - ::Decal*Decal(); + const std::string&Name()const; + const std::string&Description()const; + const ITCategory Category()const; + const::Decal*const Decal()const; /* For the useFunc, return true if the item can be used, false otherwise. */ - ItemScript&OnUseAction(); - Stats GetStats(int enhancementLevel); - float CastTime(); - float CooldownTime(); - EquipSlot Slot(); + const ItemScript&OnUseAction()const; + const Stats&GetStats(int enhancementLevel)const; + const float CastTime()const; + const float CooldownTime()const; + const EquipSlot Slot()const; const std::optionalItemSet()const; ItemInfo&operator[](const IT&item); + const uint32_t GetBuyValue()const; + const uint32_t GetSellValue()const; + const bool CanBeSold()const; + const bool CanBePurchased()const; }; class ItemOverlay{ diff --git a/Crawler/ItemDrop.cpp b/Crawler/ItemDrop.cpp index 6703aff6..195cf601 100644 --- a/Crawler/ItemDrop.cpp +++ b/Crawler/ItemDrop.cpp @@ -78,9 +78,9 @@ void ItemDrop::Draw(){ yOffset=sin((game->levelTime+randomSpinOffset)*3)*0.5f; } game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()+yOffset},GFX["skill_overlay_icon_overlay.png"].Decal(),0,GFX["skill_overlay_icon_overlay.png"].Decal()->sprite->Size()/2,{"ItemDrop.Item Drop Scale"_F,"ItemDrop.Item Drop Scale"_F},YELLOW); - game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()+yOffset},item->Decal(),0,item->Decal()->sprite->Size()/2,{"ItemDrop.Item Drop Scale"_F,"ItemDrop.Item Drop Scale"_F},{255,255,255,128}); + game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()+yOffset},const_cast(item->Decal()),0,item->Decal()->sprite->Size()/2,{"ItemDrop.Item Drop Scale"_F,"ItemDrop.Item Drop Scale"_F},{255,255,255,128}); game->SetDecalMode(DecalMode::ADDITIVE); - game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()+yOffset},item->Decal(),0,item->Decal()->sprite->Size()/2,{"ItemDrop.Item Drop Scale"_F,"ItemDrop.Item Drop Scale"_F},{uint8_t(abs(sin(game->levelTime*1.5)*255.f)),uint8_t(abs(sin(game->levelTime*1.5)*255.f)),uint8_t(abs(sin(game->levelTime*1.5)*255.f)),128}); + game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()+yOffset},const_cast(item->Decal()),0,item->Decal()->sprite->Size()/2,{"ItemDrop.Item Drop Scale"_F,"ItemDrop.Item Drop Scale"_F},{uint8_t(abs(sin(game->levelTime*1.5)*255.f)),uint8_t(abs(sin(game->levelTime*1.5)*255.f)),uint8_t(abs(sin(game->levelTime*1.5)*255.f)),128}); game->SetDecalMode(DecalMode::NORMAL); } diff --git a/Crawler/MenuItemButton.h b/Crawler/MenuItemButton.h index 9d17717b..c9f8c468 100644 --- a/Crawler/MenuItemButton.h +++ b/Crawler/MenuItemButton.h @@ -60,12 +60,12 @@ private: 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?invRef[invIndex].Decal():nullptr,onClick,attributes),invRef(invRef),inventoryIndex(invIndex),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ + :MenuIconButton(rect,invRef.size()>invIndex?const_cast(invRef[invIndex].Decal()):nullptr,onClick,attributes),invRef(invRef),inventoryIndex(invIndex),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ draggable=false; 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?invRef[invIndex].Decal():nullptr,onClick,attributes),invRef(invRef),inventoryIndex(invIndex),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ + :MenuIconButton(rect,invRef.size()>invIndex?const_cast(invRef[invIndex].Decal()):nullptr,onClick,attributes),invRef(invRef),inventoryIndex(invIndex),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ draggable=false; runHoverFunctions=true; valid=invRef.size()>invIndex; @@ -73,12 +73,12 @@ public: SetMouseOutFunc(onMouseOut); } inline Item&GetItem(){ - return Inventory::GetItem(invRef.at(inventoryIndex).Name()); + 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).Name(),amt); + return Inventory::UseItem(invRef.at(inventoryIndex).ActualName(),amt); } inline void SetCompactDescriptions(bool compact){ if(compact)this->compact=COMPACT; @@ -98,8 +98,8 @@ protected: std::string labelNameText; std::string labelDescriptionText; if(valid){ - icon=invRef[inventoryIndex].Decal(); - labelNameText=invRef[inventoryIndex].Name(); + icon=const_cast(invRef[inventoryIndex].Decal()); + labelNameText=invRef[inventoryIndex].DisplayName(); labelDescriptionText=invRef[inventoryIndex].Description(compact); }else{ icon=nullptr; @@ -120,7 +120,7 @@ protected: } virtual inline void Update(Crawler*game)override{ MenuIconButton::Update(game); - valid=invRef.size()>inventoryIndex&&ITEM_DATA.count(invRef[inventoryIndex].Name()); + valid=invRef.size()>inventoryIndex&&ITEM_DATA.count(invRef[inventoryIndex].ActualName()); if(hovered){ UpdateLabel(); @@ -132,7 +132,7 @@ protected: drawutil::DrawCrosshairDecalViewPort(window,{rect.pos,rect.size},0); } if(valid){ - int itemQuantity=Inventory::GetItemCount(invRef.at(inventoryIndex).Name()); //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. } diff --git a/Crawler/MenuItemItemButton.h b/Crawler/MenuItemItemButton.h index b6033683..8837414f 100644 --- a/Crawler/MenuItemItemButton.h +++ b/Crawler/MenuItemItemButton.h @@ -57,12 +57,12 @@ private: CompactText compact=COMPACT; public: inline MenuItemItemButton(geom2d::rectrect,Item&itemRef,MenuType menuDest,MenuFunc onClick,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE) - :MenuIconButton(rect,(!itemRef.IsBlank())?itemRef.Decal():nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ + :MenuIconButton(rect,(!itemRef.IsBlank())?const_cast(itemRef.Decal()):nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ draggable=false; valid=!itemRef.IsBlank(); } inline MenuItemItemButton(geom2d::rectrect,Item&itemRef,MenuType menuDest,MenuFunc onClick,MenuFunc onHover,MenuFunc onMouseOut,std::string itemNameLabelName="",std::string itemDescriptionLabelName="",IconButtonAttr attributes=IconButtonAttr::SELECTABLE) - :MenuIconButton(rect,(!itemRef.IsBlank())?itemRef.Decal():nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ + :MenuIconButton(rect,(!itemRef.IsBlank())?const_cast(itemRef.Decal()):nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){ runHoverFunctions=true; draggable=false; valid=!itemRef.IsBlank(); @@ -87,7 +87,7 @@ public: icon=nullptr; return; } - icon=itemRef.get().Decal(); + icon=const_cast(itemRef.get().Decal()); } protected: virtual inline void OnMouseOut()override{ @@ -112,8 +112,8 @@ protected: return; } - icon=itemRef.get().Decal(); - labelNameText=itemRef.get().Name(); + icon=const_cast(itemRef.get().Decal()); + labelNameText=itemRef.get().DisplayName(); labelDescriptionText=itemRef.get().Description(compact); if(itemNameLabelName!=""){ Component(parentMenu,itemNameLabelName)->label=labelNameText; diff --git a/Crawler/Merchant.cpp b/Crawler/Merchant.cpp index e8dc23a6..84ed6230 100644 --- a/Crawler/Merchant.cpp +++ b/Crawler/Merchant.cpp @@ -37,10 +37,16 @@ All rights reserved. #pragma endregion #include "Merchant.h" +#include "Crawler.h" + +INCLUDE_game std::map>Merchant::merchants; +MerchantFunctionPrimingData Merchant::purchaseFunctionPrimed("CanPurchaseItem()"); +MerchantFunctionPrimingData Merchant::sellFunctionPrimed("CanSellItem()"); +Merchant Merchant::travelingMerchant; -const Merchant&Merchant::GetRandomMerchant(Chapter chapter)const{ +const Merchant&Merchant::GetRandomMerchant(Chapter chapter){ return merchants[chapter][rand()%(merchants[chapter].size()-1)]; } @@ -57,8 +63,8 @@ Merchant&Merchant::AddMerchant(Chapter chapter){ return merchants[chapter].back(); } -void Merchant::AddItem(IT item,uint8_t enhancementLevel){ - shopItems.push_back(Item{1,item,enhancementLevel}); +void Merchant::AddItem(IT item,uint32_t amt,uint8_t enhancementLevel){ + shopItems.push_back(Item{amt,item,enhancementLevel}); } INCLUDE_DATA @@ -82,7 +88,12 @@ void Merchant::Initialize(){ std::string itemKey=std::format("Item[{}]",itemNumber); if(data.HasProperty(itemKey)){ IT itemName=data[itemKey].GetString(); - newMerchant.AddItem(itemName); + if(data[itemKey].GetValueCount()>1){ + int qty=data[itemKey].GetInt(1); + newMerchant.AddItem(itemName,qty); + }else{ + newMerchant.AddItem(itemName,INFINITE); + } }else{ ERR("Could not find item "<=amt&&it.CanBePurchased()){ + itemAvailable=true; + foundItem=⁢ + break; + } + } + + purchaseFunctionPrimed.amt=amt; + purchaseFunctionPrimed.item=item; + return purchaseFunctionPrimed= + itemAvailable&& + foundItem!=nullptr&& + game->GetPlayer()->GetMoney()>=foundItem->BuyValue()*amt; +}; +bool Merchant::CanSellItem(IT item,uint32_t amt)const{ + ItemInfo&it=ITEM_DATA[item]; + + sellFunctionPrimed.amt=amt; + sellFunctionPrimed.item=item; + return sellFunctionPrimed= + it.CanBeSold()&& + Inventory::GetItemCount(item)>=amt; +}; +void Merchant::PurchaseItem(IT item,uint32_t amt){ + purchaseFunctionPrimed.Validate(item,amt); + + uint32_t totalCost=0U; + for(Item&it:shopItems){ + if(it==item){ + if(it.Amt()!=INFINITE){ + it.SetAmt(it.Amt()-amt); + } + totalCost=it.BuyValue()*amt; + break; + } + } + + Inventory::AddItem(item,amt); + game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()-totalCost); + + purchaseFunctionPrimed=false; +}; +void Merchant::SellItem(IT item,uint32_t amt){ + sellFunctionPrimed.Validate(item,amt); + + uint32_t totalCost=0U; + bool itemFound=false; + for(Item&it:shopItems){ + if(it==item){ + if(it.Amt()!=INFINITE){ + it.SetAmt(it.Amt()+amt); + } + itemFound=true; + break; + } + } + if(!itemFound){ + AddItem(item); + } + totalCost=ITEM_DATA[item].GetSellValue()*amt; + + Inventory::RemoveItem(item,amt); + game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()+totalCost); + + sellFunctionPrimed=false; +}; + +void Merchant::RandomizeTravelingMerchant(){ + travelingMerchant=GetRandomMerchant(game->GetCurrentChapter()); +}; +Merchant&Merchant::GetCurrentTravelingMerchant(){ + return travelingMerchant; +}; \ No newline at end of file diff --git a/Crawler/Merchant.h b/Crawler/Merchant.h index 55a42035..32c3e040 100644 --- a/Crawler/Merchant.h +++ b/Crawler/Merchant.h @@ -35,21 +35,33 @@ Project (www.freetype.org). Please see LICENSE_FT.txt for more information. All rights reserved. */ #pragma endregion - -#include "Crawler.h" +#pragma once +#include "olcPixelGameEngine.h" +#include "FunctionPriming.h" +class Item; using Chapter=int; +using IT=std::string; class Merchant{ public: - const Merchant&GetRandomMerchant(Chapter chapter)const; + static void RandomizeTravelingMerchant(); + static Merchant&GetCurrentTravelingMerchant(); const std::string&GetDisplayName()const; const std::vector&GetShopItems()const; - void AddItem(IT item,uint8_t enhancementLevel=0U); + 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; + void PurchaseItem(IT item,uint32_t amt=1U); + void SellItem(IT item,uint32_t amt=1U); public: static void Initialize(); static Merchant&AddMerchant(Chapter chapter); private: + static MerchantFunctionPrimingData purchaseFunctionPrimed; + static MerchantFunctionPrimingData sellFunctionPrimed; + static Merchant travelingMerchant; + static const Merchant&GetRandomMerchant(Chapter chapter); static std::map>merchants; std::string displayName; std::vectorshopItems; diff --git a/Crawler/Player.cpp b/Crawler/Player.cpp index 4cd4e579..4a3b937a 100644 --- a/Crawler/Player.cpp +++ b/Crawler/Player.cpp @@ -908,4 +908,11 @@ void Player::SetBaseStat(ItemAttribute a,int val){ const std::string&ItemSet::GetSetName()const{ return name; -} \ No newline at end of file +} + +uint32_t Player::GetMoney()const{ + return money; +}; +void Player::SetMoney(uint32_t newMoney){ + money=newMoney; +}; \ No newline at end of file diff --git a/Crawler/Player.h b/Crawler/Player.h index acf67eb6..5c281525 100644 --- a/Crawler/Player.h +++ b/Crawler/Player.h @@ -115,6 +115,7 @@ private: Ability useItem1; Ability useItem2; Ability useItem3; + uint32_t money=9999; protected: const float ATTACK_COOLDOWN="Warrior.Auto Attack.Cooldown"_F; const float MAGIC_ATTACK_COOLDOWN="Wizard.Auto Attack.Cooldown"_F; @@ -251,6 +252,9 @@ public: void SetItem3UseFunc(Ability a); static InputGroup KEY_ABILITY1, KEY_ABILITY2, KEY_ABILITY3, KEY_ABILITY4, KEY_DEFENSIVE, KEY_ITEM1, KEY_ITEM2, KEY_ITEM3; + + uint32_t GetMoney()const; + void SetMoney(uint32_t newMoney); }; struct Warrior:Player{ diff --git a/Crawler/RowItemDisplay.h b/Crawler/RowItemDisplay.h index 73481e37..5d1fa6ed 100644 --- a/Crawler/RowItemDisplay.h +++ b/Crawler/RowItemDisplay.h @@ -57,10 +57,10 @@ public: 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},itemRef.get().Decal(),{scaleFactor,scaleFactor}); + window.DrawDecal(rect.pos+vf2d{2,2},const_cast(itemRef.get().Decal()),{scaleFactor,scaleFactor}); window.DrawRectDecal(rect.pos+vf2d{2,2},iconSize); - std::string itemName=itemRef.get().Name(); + std::string itemName=itemRef.get().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); @@ -109,7 +109,7 @@ public: std::string labelNameText; std::string labelDescriptionText; if(valid){ - labelNameText=itemRef.get().Name(); + labelNameText=itemRef.get().DisplayName(); labelDescriptionText=itemRef.get().Description(compact); }else{ labelNameText=""; diff --git a/Crawler/Version.h b/Crawler/Version.h index 75ac92f9..4ef7fffc 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 4086 +#define VERSION_BUILD 4125 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Crawler/assets/config/shops/Chapter 1 Merchants.txt b/Crawler/assets/config/shops/Chapter 1 Merchants.txt index 78177770..0ff12f6c 100644 --- a/Crawler/assets/config/shops/Chapter 1 Merchants.txt +++ b/Crawler/assets/config/shops/Chapter 1 Merchants.txt @@ -5,25 +5,29 @@ Merchant Chapter1_A { DisplayName = Merchant - Item[1] = Minor Health Potion - Item[2] = Minor Mana Potion + # Specify items this merchant sells. Add an optional quantity as a second argument. Not specifying a second argument will give the shop infinite supply. + Item[1] = Minor Health Potion,5 + Item[2] = Minor Mana Potion,5 } Chapter1_B { DisplayName = Merchant - Item[1] = Minor Health Potion + # Specify items this merchant sells. Add an optional quantity as a second argument. Not specifying a second argument will give the shop infinite supply. + Item[1] = Minor Health Potion,5 } Chapter1_C { DisplayName = Merchant - Item[1] = Minor Mana Potion + # Specify items this merchant sells. Add an optional quantity as a second argument. Not specifying a second argument will give the shop infinite supply. + Item[1] = Minor Mana Potion,5 } Chapter1_D { DisplayName = Merchant - Item[1] = Minor Health Potion - Item[2] = Minor Mana Potion - Item[3] = Bandages + # Specify items this merchant sells. Add an optional quantity as a second argument. Not specifying a second argument will give the shop infinite supply. + Item[1] = Minor Health Potion,5 + Item[2] = Minor Mana Potion,5 + Item[3] = Bandages,5 } } } \ No newline at end of file