summaryrefslogblamecommitdiffstats
path: root/src/FurnaceRecipe.cpp
blob: ce9233bccfba7a7090c29dc0eada5efee53ad521 (plain) (tree)
1
2
3
4
5
6
7
8
9


                                                                                              

                          

                  

                                         









































                                                       
                                        

                       
                                           
 

                                                           
         

                                                                                           

                       



                              
 
                                    
 




                                                                                                                                           
                 


                                 

                                          
                 
                                 
                 
 
                                                                        
                 







                                                                                                                                                                               
                         
                                      

                         

                                           


                                                                                                      

                                 
                                                                                    
                 




















                                                                                                                                                                                                         
                 

                  
 








                                                                                                                                       
 
 






















                                                                                                                                                                                                            
                 

                                                                                   
                 
         


                                                                                                
         





















































                                                                                                                                                                                                                                                                    
         










                                                                                                                      





 
                                       
 
                                                                                                         






                                  
                                                                                                 






                               




                                                                                              
 

                                                                                                               

                                        
                                                                                                                     
                 
                                                                                            











                                                




                                                           
 

                                                                                                       

                                      
                                                                                                         
                 
                                                                    









                                                      




 

#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "FurnaceRecipe.h"
#include "Item.h"

#include <fstream>

#define FURNACE_RECIPE_FILE "furnace.txt"





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;
}





void cFurnaceRecipe::ReloadRecipes(void)
{
	ClearRecipes();
	LOGD("Loading furnace recipes...");

	std::ifstream F(FURNACE_RECIPE_FILE, std::ios::in);
	if (!F.good())
	{
		F.close();
		LOG("Could not open the furnace recipes file \"%s\"", FURNACE_RECIPE_FILE);
		return;
	}
	
	AString SyntaxError;
	unsigned int Line = 0;
	AString ParsingLine;

	ASSERT(ParsingLine.empty());

	while (std::getline(F, ParsingLine))
	{
		Line++;
		ParsingLine.erase(std::remove_if(ParsingLine.begin(), ParsingLine.end(), isspace), ParsingLine.end()); // Remove whitespace
		if (ParsingLine.empty())
		{
			continue;
		}

		// Comments
		if (ParsingLine[0] == '#')
		{
			continue;
		}

		if (ParsingLine[0] == '!') // Fuels start with a bang :)
		{
			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
				)
			{
				break;
			}

			// Add to fuel list
			Fuel F;
			F.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth);
			F.BurnTime = IBurnTime;
			m_pState->Fuel.push_back(F);
			continue;
		}
		else if (isdigit(ParsingLine[0])) // Recipes start with a numeral :)
		{
			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);
		}
	}
	F.close();

	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());
	}
}





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)
		{
			SetParseError(a_ErrorString, a_Line, a_Begin, a_Delimiter);
			return false;
		}
	}
	
	// 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)))
	{
		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);
		}
	}
	
	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);
}





void cFurnaceRecipe::ClearRecipes(void)
{
	for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
	{
		Recipe R = *itr;
		delete R.In;
		delete R.Out;
	}
	m_pState->Recipes.clear();

	for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
	{
		Fuel F = *itr;
		delete F.In;
	}
	m_pState->Fuel.clear();
}





const cFurnaceRecipe::Recipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const
{
	const Recipe * BestRecipe = 0;
	for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
	{
		const Recipe & R = *itr;
		if ((R.In->m_ItemType == a_Ingredient.m_ItemType) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount))
		{
			if (BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount))
			{
				continue;
			}
			else
			{
				BestRecipe = &R;
			}
		}
	}
	return BestRecipe;
}





int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const
{
	int BestFuel = 0;
	for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
	{
		const Fuel & F = *itr;
		if ((F.In->m_ItemType == a_Fuel.m_ItemType) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount))
		{
			if (BestFuel > 0 && (BestFuel > F.BurnTime))
			{
				continue;
			}
			else
			{
				BestFuel = F.BurnTime;
			}
		}
	}
	return BestFuel;
}