240 lines
8.2 KiB
C++
240 lines
8.2 KiB
C++
#pragma region License
|
||
/*
|
||
License (OLC-3)
|
||
~~~~~~~~~~~~~~~
|
||
|
||
Copyright 2024 Joshua Sigona <sigonasr2@gmail.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 <20> 2023 The FreeType
|
||
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
||
All rights reserved.
|
||
*/
|
||
#pragma endregion
|
||
|
||
#include "Menu.h"
|
||
#include "Merchant.h"
|
||
#include "AdventuresInLestoria.h"
|
||
#include "util.h"
|
||
|
||
INCLUDE_game
|
||
INCLUDE_ITEM_CATEGORIES
|
||
|
||
std::map<Chapter,std::vector<Merchant>>Merchant::merchants;
|
||
std::map<ITCategory,std::vector<std::weak_ptr<Item>>>Merchant::sortedItems;
|
||
MerchantFunctionPrimingData Merchant::purchaseFunctionPrimed("CanPurchaseItem()");
|
||
MerchantFunctionPrimingData Merchant::sellFunctionPrimed("CanSellItem()");
|
||
Merchant Merchant::travelingMerchant;
|
||
|
||
const Merchant&Merchant::GetRandomMerchant(Chapter chapter){
|
||
const Merchant&newMerchant=merchants[chapter][util::random()%(merchants[chapter].size()-1)];
|
||
return newMerchant;
|
||
}
|
||
|
||
const std::string&Merchant::GetDisplayName()const{
|
||
return displayName;
|
||
}
|
||
|
||
const std::vector<std::shared_ptr<Item>>&Merchant::GetShopItems()const{
|
||
return shopItems;
|
||
}
|
||
|
||
const std::vector<std::weak_ptr<Item>>&Merchant::GetShopItems(ITCategory category){
|
||
return sortedItems[category];
|
||
}
|
||
|
||
Merchant&Merchant::AddMerchant(Chapter chapter,std::string_view keyName){
|
||
merchants[chapter].push_back({});
|
||
merchants[chapter].back().keyName=keyName;
|
||
return merchants[chapter].back();
|
||
}
|
||
|
||
void Merchant::AddItem(IT item,uint32_t amt,uint8_t enhancementLevel){
|
||
shopItems.push_back(std::make_shared<Item>(amt,item,enhancementLevel));
|
||
UpdateSortedItemsList();
|
||
if(&GetCurrentTravelingMerchant()==this){
|
||
Menu::MerchantInventorySlotsUpdated(ITEM_DATA[item].Category());
|
||
}
|
||
}
|
||
|
||
void Merchant::UpdateSortedItemsList(){
|
||
const Merchant&merchant=GetCurrentTravelingMerchant();
|
||
for(auto&[key,items]:ITEM_CATEGORIES){
|
||
sortedItems[key].clear();
|
||
}
|
||
std::for_each(merchant.shopItems.begin(),merchant.shopItems.end(),[](const std::shared_ptr<Item>item){
|
||
sortedItems[item->Category()].push_back(item);
|
||
});
|
||
}
|
||
|
||
INCLUDE_DATA
|
||
|
||
void Merchant::Initialize(){
|
||
for(int chapter=1;chapter<=6;chapter++){
|
||
std::string merchantChapterFilename="assets/"+"merchant_directory"_S+"Chapter "+std::to_string(chapter)+" Merchants.txt";
|
||
if(!std::filesystem::exists(merchantChapterFilename))ERR("WARNING! Could not find file "<<std::quoted(merchantChapterFilename)<<" for merchant reading!");
|
||
utils::datafile::Read(DATA,merchantChapterFilename);
|
||
}
|
||
for(int chapter=1;chapter<=6;chapter++){
|
||
int merchantCount=0;
|
||
utils::datafile&chapterMerchantInfo=DATA["Merchant"][std::format("Chapter {}",chapter)];
|
||
for(auto&[key,size]:chapterMerchantInfo){
|
||
utils::datafile&data=chapterMerchantInfo.GetProperty(key);
|
||
Merchant&newMerchant=AddMerchant(chapter,key);
|
||
for(int itemNumber=1;auto&[key,size]:data){
|
||
if(key=="DisplayName")newMerchant.displayName=data[key].GetString();
|
||
else
|
||
if(key.starts_with("Item[")){
|
||
std::string itemKey=std::format("Item[{}]",itemNumber);
|
||
if(data.HasProperty(itemKey)){
|
||
IT itemName=data[itemKey].GetString();
|
||
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 "<<itemNumber<<" in Merchant "<<merchantCount<<" of Chapter "<<chapter<<"!");
|
||
}
|
||
itemNumber++;
|
||
}else{
|
||
ERR("Unhandled key "<<std::quoted(key)<<" inside of Merchant "<<merchantCount<<" of Chapter "<<chapter<<"!");
|
||
}
|
||
}
|
||
merchantCount++;
|
||
}
|
||
if(merchantCount==0)ERR(std::format("WARNING! No merchants available for Chapter {}!",chapter));
|
||
std::cout<<std::format("Added {} merchants to Chapter {}",merchantCount,chapter)<<std::endl;
|
||
}
|
||
Merchant::RandomizeTravelingMerchant();
|
||
}
|
||
bool Merchant::CanPurchaseItem(IT item,uint32_t amt)const{
|
||
bool itemAvailable=false;
|
||
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;
|
||
break;
|
||
}
|
||
}
|
||
|
||
purchaseFunctionPrimed.amt=amt;
|
||
purchaseFunctionPrimed.item=item;
|
||
return purchaseFunctionPrimed=
|
||
itemAvailable&&
|
||
!foundItem.expired()&&
|
||
game->GetPlayer()->GetMoney()>=foundItem.lock()->BuyValue()*amt;
|
||
};
|
||
bool Merchant::CanSellItem(std::weak_ptr<Item>item,uint32_t amt)const{
|
||
sellFunctionPrimed.amt=amt;
|
||
sellFunctionPrimed.item=item.lock()->ActualName();
|
||
return sellFunctionPrimed=
|
||
item.lock()->CanBeSold()&&
|
||
item.lock()->Amt()>=amt;
|
||
};
|
||
void Merchant::PurchaseItem(IT item,uint32_t amt){
|
||
purchaseFunctionPrimed.Validate(item,amt);
|
||
|
||
uint32_t totalCost=0U;
|
||
bool finiteItemPurchased=false;
|
||
for(std::shared_ptr<Item>it: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<Item> 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);
|
||
};
|
||
void Merchant::SellItem(std::weak_ptr<Item>item,uint32_t amt){
|
||
sellFunctionPrimed.Validate(item.lock()->ActualName(),amt);
|
||
|
||
uint32_t totalCost=0U;
|
||
bool itemFound=false;
|
||
for(std::shared_ptr<Item>it:shopItems){
|
||
if(it==item){
|
||
if(it->Amt()!=INFINITE){
|
||
it->SetAmt(it->Amt()+amt);
|
||
}
|
||
itemFound=true;
|
||
break;
|
||
}
|
||
}
|
||
if(!itemFound&&item.lock()->CanBePurchased()){
|
||
AddItem(item.lock()->ActualName(),amt); //This may not be a feature we include in future versions of the game. For now let's allow it.
|
||
}
|
||
totalCost=item.lock()->SellValue()*amt;
|
||
|
||
Inventory::RemoveItem(item,amt);
|
||
game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()+totalCost);
|
||
|
||
sellFunctionPrimed=false;
|
||
};
|
||
|
||
void Merchant::RandomizeTravelingMerchant(){
|
||
SetTravelingMerchant(const_cast<Merchant&>(GetRandomMerchant(game->GetCurrentChapter())).GetKeyName());
|
||
};
|
||
Merchant&Merchant::GetCurrentTravelingMerchant(){
|
||
return travelingMerchant;
|
||
};
|
||
|
||
std::string_view Merchant::GetKeyName()const{
|
||
return keyName;
|
||
}
|
||
|
||
void Merchant::SetTravelingMerchant(std::string_view key){
|
||
for(auto&[merchantKey,merchantList]:merchants){
|
||
auto it=std::find_if(merchantList.begin(),merchantList.end(),[&](const Merchant&merchant){return merchant.GetKeyName()==key;});
|
||
if(it!=merchantList.end()){
|
||
travelingMerchant=*it;
|
||
for(auto&[key,items]:ITEM_CATEGORIES){
|
||
Menu::MerchantInventorySlotsUpdated(key);
|
||
}
|
||
UpdateSortedItemsList();
|
||
return;
|
||
}
|
||
}
|
||
std::cout<<std::format("WARNING! Could not set traveling merchant with key {}!",std::string(key))<<std::endl;
|
||
std::cout<<"Falling back to a randomized merchant."<<std::endl;
|
||
RandomizeTravelingMerchant();
|
||
} |