diff --git a/Adventures in Lestoria Tests/ItemTests.cpp b/Adventures in Lestoria Tests/ItemTests.cpp index a6f0ed5f..efece0d2 100644 --- a/Adventures in Lestoria Tests/ItemTests.cpp +++ b/Adventures in Lestoria Tests/ItemTests.cpp @@ -194,6 +194,7 @@ namespace ItemTests Assert::AreEqual(1U,Inventory::GetItemCount(ITEM_DATA["Ring of the Slime King"].FragmentName()),L"Disassembly has given us a Slime King Ring Fragment."); } TEST_METHOD(DisassembleNonAccessoryTest){ + Inventory::AddItem("Ring of the Slime King"s); try{ Inventory::Disassemble(Inventory::AddItem("Test Armor"s)); Assert::Fail(L"Disassembling Test Armor succeeded! This should NOT be allowed!"); @@ -207,5 +208,25 @@ namespace ItemTests Assert::Fail(L"Disassembling a Health Potion succeeded! This should NOT be allowed!"); }catch(std::runtime_error&e){} } + TEST_METHOD(RefiningTest){ + util::random(); + util::random(); + std::weak_ptrslimeKingRing{Inventory::AddItem("Ring of the Slime King"s)}; + Assert::IsFalse(slimeKingRing.lock()->CanBeRefined(),L"Ring of the Slime King should not be allowed to be refined since we have no fragments."); + std::weak_ptrtestArmor{Inventory::AddItem("Test Armor"s)}; + Assert::IsFalse(testArmor.lock()->CanBeRefined(),L"Test Armor should not be allowed to be refined since it's not an accessory."); + Inventory::AddItem(slimeKingRing.lock()->FragmentName(),50U); + Assert::IsTrue(slimeKingRing.lock()->CanBeRefined(),L"Ring of the Slime King should now be allowed to be refined since we meet all requirements."); + player->SetMoney(0); + Assert::IsFalse(slimeKingRing.lock()->CanBeRefined(),L"Ring of the Slime King should not be allowed to be refined since we do not have enough money."); + player->SetMoney(10000); + while(slimeKingRing.lock()->CanBeRefined()){ + RefineResult result{slimeKingRing.lock()->Refine()}; + LOG(std::format("Enhanced {} by {}",result.first.Name(),result.second)); + } + for(const auto&[attr,val]:slimeKingRing.lock()->RandomStats()){ + Assert::AreEqual(ITEM_DATA[slimeKingRing.lock()->ActualName()].GetMaxStats().A_Read(attr),val,L"The current stats should be equal to the maximum stats when refinement is done."); + } + } }; } diff --git a/Adventures in Lestoria/Item.cpp b/Adventures in Lestoria/Item.cpp index 88b4f49a..96260400 100644 --- a/Adventures in Lestoria/Item.cpp +++ b/Adventures in Lestoria/Item.cpp @@ -620,6 +620,13 @@ bool Inventory::RemoveItem(std::weak_ptritemRef,ITCategory inventory,uint3 } } +bool Inventory::RemoveItem(IT it,uint32_t amt){ + if(GetItemCount(it)==0)return false; + std::weak_ptrfoundItem{GetItem(it)[0]}; + ITCategory cat = foundItem.lock()->Category(); + return RemoveItem(foundItem, cat, amt); +} + //Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory. bool Inventory::RemoveItem(std::weak_ptritemRef,uint32_t amt){ ITCategory cat = itemRef.lock()->Category(); @@ -1402,4 +1409,32 @@ const std::string&Item::FragmentName()const{ const std::string&ItemInfo::FragmentName()const{ if(!fragmentName.has_value())ERR(std::format("WARNING! Item {} does not break down into a fragment (fragment name not set)!",Name())); return fragmentName.value(); +} + +const bool Item::CanBeRefined()const{ + if(!IsAccessory()||Inventory::GetItemCount(FragmentName())<"Fragment Refine Cost"_i[0]||game->GetPlayer()->GetMoney()<"Fragment Refine Cost"_i[1])return false; + for(const auto&[attr,val]:randomizedStats){ + float maxVal{ITEM_DATA[ActualName()].GetMaxStats().A_Read(attr)}; + if(val!=maxVal)return true; + } + return false; //Maxed out, so cannot be refined. +} + +RefineResult Item::Refine(){ + if(!CanBeRefined())ERR("WARNING! Trying to refine an item that cannot be refined! Make sure you are checking with CanBeRefined() before calling this function!"); + Inventory::RemoveItem(FragmentName(),"Fragment Refine Cost"_i[0]); + game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()-"Fragment Refine Cost"_i[1]); + std::vectorstatsAvailableForRefining; + for(const auto&[attr,val]:randomizedStats){ + float maxVal{ITEM_DATA[ActualName()].GetMaxStats().A_Read(attr)}; + if(val!=maxVal)statsAvailableForRefining.push_back(attr); + } + ItemAttribute&chosenAttr{statsAvailableForRefining[util::random()%statsAvailableForRefining.size()]}; + const float pctIncreaseAmount{"Refine Stat Increase Amount"_F/100.f}; + const float maxStat{ITEM_DATA[ActualName()].GetMaxStats().A_Read(chosenAttr)}; + const int increaseAmt{int(ceil(maxStat*pctIncreaseAmount))}; + const float oldAmt{randomizedStats.A_Read(chosenAttr)}; + randomizedStats.A(chosenAttr)=std::min(maxStat,oldAmt+increaseAmt); + const float newAmt{randomizedStats.A_Read(chosenAttr)}; + return RefineResult{chosenAttr,newAmt-oldAmt}; } \ No newline at end of file diff --git a/Adventures in Lestoria/Item.h b/Adventures in Lestoria/Item.h index 87918568..7a782888 100644 --- a/Adventures in Lestoria/Item.h +++ b/Adventures in Lestoria/Item.h @@ -168,6 +168,9 @@ namespace PlayerTests{ class PlayerTest; }; +using IncreaseAmount=int; +using RefineResult=std::pair; + class Item{ friend class Inventory; friend class AiL; @@ -238,9 +241,12 @@ public: static bool IsBlank(std::weak_ptritem); const bool IsLocked()const; const std::string&FragmentName()const; + const bool CanBeRefined()const; //An item can be refined if the item has less than max stats and the player has enough fragments in their inventory. + //Assumes an item can be refined. CHECK WITH CanBeRefined() First!!! + //Refines a random stat by the parameters defined in the Accessories.txt configuration file. Also takes away the costs required to refine the item. + RefineResult Refine(); void Lock(); void Unlock(); - void Disassemble(); friend const bool operator==(std::shared_ptrlhs,std::shared_ptrrhs){return lhs->it==rhs->it&&lhs->randomizedStats==rhs->randomizedStats;}; friend const bool operator==(std::shared_ptrlhs,const IT&rhs){return lhs->ActualName()==const_cast(rhs);}; friend const bool operator==(std::weak_ptrlhs,std::weak_ptrrhs){return !lhs.expired()&&!rhs.expired()&&lhs.lock()->it==rhs.lock()->it&&lhs.lock()->randomizedStats==rhs.lock()->randomizedStats;}; @@ -263,8 +269,12 @@ public: static std::vector>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); + //Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory. static bool RemoveItem(std::weak_ptritemRef,ITCategory inventory,uint32_t amt = 1); + //Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory. static bool RemoveItem(std::weak_ptritemRef,uint32_t amt=1); + //Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory. + static bool RemoveItem(IT it,uint32_t amt=1); static const std::vector>&get(ITCategory itemCategory); static const std::weak_ptrGetInventorySlot(ITCategory itemCategory,size_t slot); static void Clear(ITCategory itemCategory); diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index b3b46deb..0c15c7c2 100644 --- a/Adventures in Lestoria/Version.h +++ b/Adventures in Lestoria/Version.h @@ -39,7 +39,7 @@ All rights reserved. #define VERSION_MAJOR 1 #define VERSION_MINOR 2 #define VERSION_PATCH 3 -#define VERSION_BUILD 10482 +#define VERSION_BUILD 10487 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/assets/config/items/Accessories.txt b/Adventures in Lestoria/assets/config/items/Accessories.txt index c5fc8916..af10b9fd 100644 --- a/Adventures in Lestoria/assets/config/items/Accessories.txt +++ b/Adventures in Lestoria/assets/config/items/Accessories.txt @@ -1,10 +1,13 @@ Fragment Description = "A small piece of concentrated material from broken down jewelry." # Number of fragments earned when breaking down an accessory. Fragment Disassemble Gain Amount = 1 -# Number of fragments required to Refine an accessory. -Fragment Refine Cost = 1 -# Number of fragments required to Enchant an accessory. -Fragment Enchant Cost = 3 +# Number of fragments and gold required to Refine an accessory. +Fragment Refine Cost = 1, 20g +# Number of fragments and gold required to Enchant an accessory. +Fragment Enchant Cost = 3, 35g + +# How much to increase refined stats by with each refinement. (Ex. if the value is 20%, it takes 5 refinements at most from an item with the lowest possible stat to the max stat with 20% increments.) +Refine Stat Increase Amount = 20% Equipment { diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index 93a46bc0..60c04912 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ