# 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 <EFBFBD> 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 "Crawler.h"
INCLUDE_game
INCLUDE_ITEM_CATEGORIES
std : : map < Chapter , std : : vector < Merchant > > Merchant : : merchants ;
std : : map < ITCategory , std : : vector < 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 ] [ rand ( ) % ( merchants [ chapter ] . size ( ) - 1 ) ] ;
return newMerchant ;
}
const std : : string & Merchant : : GetDisplayName ( ) const {
return displayName ;
}
const std : : vector < Item > & Merchant : : GetShopItems ( ) const {
return shopItems ;
}
const std : : vector < Item * > & Merchant : : GetShopItems ( ITCategory category ) {
return sortedItems [ category ] ;
}
Merchant & Merchant : : AddMerchant ( Chapter chapter ) {
merchants [ chapter ] . push_back ( { } ) ;
return merchants [ chapter ] . back ( ) ;
}
void Merchant : : AddItem ( IT item , uint32_t amt , uint8_t enhancementLevel ) {
shopItems . push_back ( Item { amt , item , enhancementLevel } ) ;
if ( & GetCurrentTravelingMerchant ( ) = = this ) {
UpdateSortedItemsList ( ) ;
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 Item & item ) {
sortedItems [ item . Category ( ) ] . push_back ( const_cast < Item * > ( & 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 ) ;
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 + + ;
}
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 ;
const Item * foundItem = nullptr ;
for ( const 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 ! = nullptr & &
game - > GetPlayer ( ) - > GetMoney ( ) > = foundItem - > BuyValue ( ) * amt ;
} ;
bool Merchant : : CanSellItem ( IT item , uint32_t amt ) const {
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 ) ; //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 ;
Inventory : : RemoveItem ( item , amt ) ;
game - > GetPlayer ( ) - > SetMoney ( game - > GetPlayer ( ) - > GetMoney ( ) + totalCost ) ;
sellFunctionPrimed = false ;
} ;
void Merchant : : RandomizeTravelingMerchant ( ) {
travelingMerchant = GetRandomMerchant ( game - > GetCurrentChapter ( ) ) ;
for ( auto & [ key , items ] : ITEM_CATEGORIES ) {
Menu : : MerchantInventorySlotsUpdated ( key ) ;
}
UpdateSortedItemsList ( ) ;
} ;
Merchant & Merchant : : GetCurrentTravelingMerchant ( ) {
return travelingMerchant ;
} ;