2012-06-14 13:06:06 +00:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2012-09-23 22:09:57 +00:00
# include "FurnaceRecipe.h"
# include "Item.h"
2012-06-14 13:06:06 +00:00
# include <fstream>
2014-06-21 19:33:23 +00:00
# define FURNACE_RECIPE_FILE "furnace.txt"
2012-06-14 13:06:06 +00:00
typedef std : : list < cFurnaceRecipe : : Recipe > RecipeList ;
typedef std : : list < cFurnaceRecipe : : Fuel > FuelList ;
struct cFurnaceRecipe : : sFurnaceRecipeState
{
RecipeList Recipes ;
FuelList Fuel ;
} ;
cFurnaceRecipe : : cFurnaceRecipe ( )
: m_pState ( new sFurnaceRecipeState )
{
ReloadRecipes ( ) ;
}
cFurnaceRecipe : : ~ cFurnaceRecipe ( )
{
ClearRecipes ( ) ;
delete m_pState ;
}
2013-06-16 20:24:07 +00:00
void cFurnaceRecipe : : ReloadRecipes ( void )
2012-06-14 13:06:06 +00:00
{
ClearRecipes ( ) ;
2013-09-28 19:36:01 +00:00
LOGD ( " Loading furnace recipes... " ) ;
2012-06-14 13:06:06 +00:00
2014-06-21 19:33:23 +00:00
std : : ifstream F ( FURNACE_RECIPE_FILE , std : : ios : : in ) ;
if ( ! F . good ( ) )
2012-06-14 13:06:06 +00:00
{
2014-06-21 19:33:23 +00:00
F . close ( ) ;
LOG ( " Could not open the furnace recipes file \" %s \" " , FURNACE_RECIPE_FILE ) ;
2012-06-14 13:06:06 +00:00
return ;
}
2014-06-21 19:33:23 +00:00
AString SyntaxError ;
unsigned int Line = 0 ;
AString ParsingLine ;
2012-06-14 13:06:06 +00:00
2014-06-21 19:33:23 +00:00
ASSERT ( ParsingLine . empty ( ) ) ;
2012-06-14 13:06:06 +00:00
2014-06-21 19:33:23 +00:00
while ( std : : getline ( F , ParsingLine ) )
{
Line + + ;
ParsingLine . erase ( std : : remove_if ( ParsingLine . begin ( ) , ParsingLine . end ( ) , isspace ) , ParsingLine . end ( ) ) ; // Remove whitespace
if ( ParsingLine . empty ( ) )
2012-06-14 13:06:06 +00:00
{
continue ;
}
2014-06-21 19:33:23 +00:00
// Comments
if ( ParsingLine [ 0 ] = = ' # ' )
2013-06-16 20:24:07 +00:00
{
2014-06-21 19:33:23 +00:00
continue ;
2013-06-16 20:24:07 +00:00
}
2012-06-14 13:06:06 +00:00
2014-06-21 19:33:23 +00:00
if ( ParsingLine [ 0 ] = = ' ! ' ) // Fuels start with a bang :)
2012-06-14 13:06:06 +00:00
{
2014-06-21 19:33:23 +00:00
int IItemID = 0 , IItemCount = 0 , IItemHealth = 0 , IBurnTime = 0 ;
AString : : size_type BeginPos = 1 ; // Begin at one after exclamation mark (bang)
if (
! ReadMandatoryNumber ( BeginPos , " : " , ParsingLine , SyntaxError , Line , IItemID ) | | // Read item ID
! ReadOptionalNumbers ( BeginPos , " : " , " = " , ParsingLine , SyntaxError , Line , IItemCount , IItemHealth ) | | // Read item count (and optionally health)
! ReadMandatoryNumber ( BeginPos , " 0123456789 " , ParsingLine , SyntaxError , Line , IBurnTime , true ) // Read item burn time - last value
)
2012-06-14 13:06:06 +00:00
{
2014-06-21 19:33:23 +00:00
break ;
2012-06-14 13:06:06 +00:00
}
// Add to fuel list
Fuel F ;
2014-06-21 19:33:23 +00:00
F . In = new cItem ( ( ENUM_ITEM_ID ) IItemID , ( char ) IItemCount , ( short ) IItemHealth ) ;
F . BurnTime = IBurnTime ;
m_pState - > Fuel . push_back ( F ) ;
2012-06-14 13:06:06 +00:00
continue ;
}
2014-06-21 19:33:23 +00:00
else if ( isdigit ( ParsingLine [ 0 ] ) ) // Recipes start with a numeral :)
2012-06-14 13:06:06 +00:00
{
2014-06-21 19:33:23 +00:00
int IItemID = 0 , IItemCount = 0 , IItemHealth = 0 , IBurnTime = 0 ;
int OItemID = 0 , OItemCount = 0 , OItemHealth = 0 ;
AString : : size_type BeginPos = 0 ; // Begin at start of line
if (
! ReadMandatoryNumber ( BeginPos , " : " , ParsingLine , SyntaxError , Line , IItemID ) | | // Read item ID
! ReadOptionalNumbers ( BeginPos , " : " , " @ " , ParsingLine , SyntaxError , Line , IItemCount , IItemHealth ) | | // Read item count (and optionally health)
! ReadMandatoryNumber ( BeginPos , " = " , ParsingLine , SyntaxError , Line , IBurnTime ) | | // Read item burn time
! ReadMandatoryNumber ( BeginPos , " : " , ParsingLine , SyntaxError , Line , OItemID ) | | // Read result ID
! ReadOptionalNumbers ( BeginPos , " : " , " 012456789 " , ParsingLine , SyntaxError , Line , OItemCount , OItemHealth , true ) // Read result count (and optionally health) - last value
)
{
break ;
}
// Add to recipe list
Recipe R ;
R . In = new cItem ( ( ENUM_ITEM_ID ) IItemID , ( char ) IItemCount , ( short ) IItemHealth ) ;
R . Out = new cItem ( ( ENUM_ITEM_ID ) OItemID , ( char ) OItemCount , ( short ) OItemHealth ) ;
R . CookTime = IBurnTime ;
m_pState - > Recipes . push_back ( R ) ;
2012-06-14 13:06:06 +00:00
}
2014-06-21 19:33:23 +00:00
}
F . close ( ) ;
2012-06-14 13:06:06 +00:00
2014-06-21 19:33:23 +00:00
if ( ! SyntaxError . empty ( ) )
{
LOGWARN ( " Error parsing furnace recipes at %s " , SyntaxError . c_str ( ) ) ;
}
else
{
LOG ( " Loaded " SIZE_T_FMT " furnace recipes and " SIZE_T_FMT " fuels " , m_pState - > Recipes . size ( ) , m_pState - > Fuel . size ( ) ) ;
}
}
2012-06-14 13:06:06 +00:00
2014-06-21 19:33:23 +00:00
void cFurnaceRecipe : : SetParseError ( AString & a_ErrorString , unsigned int a_Line , size_t a_Position , const AString & a_CharactersMissing )
{
Printf ( a_ErrorString , " line %i pos %i: missing '%s' " , a_Line , a_Position , a_CharactersMissing . c_str ( ) ) ;
}
bool cFurnaceRecipe : : ReadMandatoryNumber ( AString : : size_type & a_Begin , const AString & a_Delimiter , const AString & a_Text , AString & a_ErrorString , unsigned int a_Line , int & a_Value , bool a_IsLastValue )
{
AString : : size_type End ;
if ( a_IsLastValue )
{
End = a_Text . find_first_not_of ( a_Delimiter , a_Begin ) ;
}
else
{
End = a_Text . find_first_of ( a_Delimiter , a_Begin ) ;
if ( End = = AString : : npos )
2012-06-14 13:06:06 +00:00
{
2014-06-21 19:33:23 +00:00
SetParseError ( a_ErrorString , a_Line , a_Begin , a_Delimiter ) ;
return false ;
2012-06-14 13:06:06 +00:00
}
}
2014-06-21 19:33:23 +00:00
// stoi won't throw an exception if the string is alphanumeric, we should check for this
if ( ! DoesStringContainOnlyNumbers ( a_Text . substr ( a_Begin , End - a_Begin ) ) )
2012-06-14 13:06:06 +00:00
{
2014-06-21 19:33:23 +00:00
SetParseError ( a_ErrorString , a_Line , a_Begin , " number " ) ;
return false ;
}
a_Value = std : : stoi ( a_Text . substr ( a_Begin , End - a_Begin ) ) ;
a_Begin = End + 1 ; // Jump over delimiter
return true ;
}
bool cFurnaceRecipe : : ReadOptionalNumbers ( AString : : size_type & a_Begin , const AString & a_DelimiterOne , const AString & a_DelimiterTwo , const AString & a_Text , AString & a_ErrorString , unsigned int a_Line , int & a_ValueOne , int & a_ValueTwo , bool a_IsLastValue )
{
unsigned int End , Begin = a_Begin ;
End = a_Text . find_first_of ( a_DelimiterOne , Begin ) ;
if ( End ! = AString : : npos )
{
if ( DoesStringContainOnlyNumbers ( a_Text . substr ( Begin , End - Begin ) ) )
{
a_ValueOne = std : : stoi ( a_Text . substr ( Begin , End - Begin ) ) ;
Begin = End + 1 ;
if ( a_IsLastValue )
{
End = a_Text . find_first_not_of ( a_DelimiterTwo , Begin ) ;
}
else
{
End = a_Text . find_first_of ( a_DelimiterTwo , Begin ) ;
if ( End = = AString : : npos )
{
SetParseError ( a_ErrorString , a_Line , Begin , a_DelimiterTwo ) ;
return false ;
}
}
// stoi won't throw an exception if the string is alphanumeric, we should check for this
if ( ! DoesStringContainOnlyNumbers ( a_Text . substr ( Begin , End - Begin ) ) )
{
SetParseError ( a_ErrorString , a_Line , Begin , " number " ) ;
return false ;
}
a_ValueTwo = std : : stoi ( a_Text . substr ( Begin , End - Begin ) ) ;
a_Begin = End + 1 ; // Jump over delimiter
return true ;
}
else
{
return ReadMandatoryNumber ( a_Begin , a_DelimiterTwo , a_Text , a_ErrorString , a_Line , a_ValueOne , a_IsLastValue ) ;
}
2012-06-14 13:06:06 +00:00
}
2014-06-21 19:33:23 +00:00
return ReadMandatoryNumber ( a_Begin , a_DelimiterTwo , a_Text , a_ErrorString , a_Line , a_ValueOne , a_IsLastValue ) ;
}
bool cFurnaceRecipe : : DoesStringContainOnlyNumbers ( const AString & a_String )
{
return std : : all_of ( a_String . begin ( ) , a_String . end ( ) , isdigit ) ;
2012-06-14 13:06:06 +00:00
}
2013-06-16 20:24:07 +00:00
void cFurnaceRecipe : : ClearRecipes ( void )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
for ( RecipeList : : iterator itr = m_pState - > Recipes . begin ( ) ; itr ! = m_pState - > Recipes . end ( ) ; + + itr )
2012-06-14 13:06:06 +00:00
{
Recipe R = * itr ;
delete R . In ;
delete R . Out ;
}
m_pState - > Recipes . clear ( ) ;
2013-06-16 20:24:07 +00:00
for ( FuelList : : iterator itr = m_pState - > Fuel . begin ( ) ; itr ! = m_pState - > Fuel . end ( ) ; + + itr )
2012-06-14 13:06:06 +00:00
{
Fuel F = * itr ;
delete F . In ;
}
m_pState - > Fuel . clear ( ) ;
}
2013-06-16 20:24:07 +00:00
const cFurnaceRecipe : : Recipe * cFurnaceRecipe : : GetRecipeFrom ( const cItem & a_Ingredient ) const
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
const Recipe * BestRecipe = 0 ;
for ( RecipeList : : const_iterator itr = m_pState - > Recipes . begin ( ) ; itr ! = m_pState - > Recipes . end ( ) ; + + itr )
2012-06-14 13:06:06 +00:00
{
const Recipe & R = * itr ;
2013-06-16 20:24:07 +00:00
if ( ( R . In - > m_ItemType = = a_Ingredient . m_ItemType ) & & ( R . In - > m_ItemCount < = a_Ingredient . m_ItemCount ) )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
if ( BestRecipe & & ( BestRecipe - > In - > m_ItemCount > R . In - > m_ItemCount ) )
2012-06-14 13:06:06 +00:00
{
continue ;
}
else
{
BestRecipe = & R ;
}
}
}
return BestRecipe ;
}
2013-06-16 20:24:07 +00:00
int cFurnaceRecipe : : GetBurnTime ( const cItem & a_Fuel ) const
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
int BestFuel = 0 ;
for ( FuelList : : const_iterator itr = m_pState - > Fuel . begin ( ) ; itr ! = m_pState - > Fuel . end ( ) ; + + itr )
2012-06-14 13:06:06 +00:00
{
const Fuel & F = * itr ;
2013-06-16 20:24:07 +00:00
if ( ( F . In - > m_ItemType = = a_Fuel . m_ItemType ) & & ( F . In - > m_ItemCount < = a_Fuel . m_ItemCount ) )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
if ( BestFuel > 0 & & ( BestFuel > F . BurnTime ) )
2012-06-14 13:06:06 +00:00
{
continue ;
}
else
{
BestFuel = F . BurnTime ;
}
}
}
return BestFuel ;
2013-06-16 20:24:07 +00:00
}