338 lines
7.2 KiB
C++
338 lines
7.2 KiB
C++
|
|
#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 FILE_IO_PREFIX "furnace.txt"
|
|
|
|
|
|
|
|
|
|
|
|
typedef std::list<cFurnaceRecipe::cRecipe> RecipeList;
|
|
typedef std::list<cFurnaceRecipe::cFuel> FuelList;
|
|
|
|
|
|
|
|
|
|
|
|
struct cFurnaceRecipe::sFurnaceRecipeState
|
|
{
|
|
RecipeList Recipes;
|
|
FuelList Fuel;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
cFurnaceRecipe::cFurnaceRecipe()
|
|
: m_pState(new sFurnaceRecipeState)
|
|
{
|
|
ReloadRecipes();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cFurnaceRecipe::~cFurnaceRecipe()
|
|
{
|
|
ClearRecipes();
|
|
delete m_pState;
|
|
m_pState = nullptr;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cFurnaceRecipe::ReloadRecipes(void)
|
|
{
|
|
ClearRecipes();
|
|
LOGD("Loading furnace recipes...");
|
|
|
|
std::ifstream f(FURNACE_RECIPE_FILE, std::ios::in);
|
|
if (!f.good())
|
|
{
|
|
LOG("Could not open the furnace recipes file \"%s\". No furnace recipes are available.", FURNACE_RECIPE_FILE);
|
|
return;
|
|
}
|
|
|
|
unsigned int LineNum = 0;
|
|
AString ParsingLine;
|
|
|
|
while (std::getline(f, ParsingLine))
|
|
{
|
|
LineNum++;
|
|
if (ParsingLine.empty())
|
|
{
|
|
// There is a problem here on Android. Text files transferred from another OS may have a newline representation Android's implementation of getline doesn't expect
|
|
// Thus, part of a newline may be left in ParsingLine. ::empty() thus thinks the string isn't empty, and the below code outputs interesting errors since it was passed a nearly empty string
|
|
// Ref: http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf
|
|
// TODO: There is a solution in the above reference, but it isn't very pretty. Fix it somehow.
|
|
continue;
|
|
}
|
|
|
|
// Remove comments from the line:
|
|
size_t FirstCommentSymbol = ParsingLine.find('#');
|
|
if ((FirstCommentSymbol != AString::npos) && (FirstCommentSymbol != 0))
|
|
{
|
|
ParsingLine.erase(ParsingLine.begin() + static_cast<const long>(FirstCommentSymbol), ParsingLine.end());
|
|
}
|
|
|
|
switch (ParsingLine[0])
|
|
{
|
|
case '#':
|
|
{
|
|
// Comment
|
|
break;
|
|
}
|
|
|
|
case '!':
|
|
{
|
|
AddFuelFromLine(ParsingLine, LineNum);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
AddRecipeFromLine(ParsingLine, LineNum);
|
|
break;
|
|
}
|
|
} // switch (ParsingLine[0])
|
|
} // while (getline(ParsingLine))
|
|
|
|
LOG("Loaded " SIZE_T_FMT " furnace recipes and " SIZE_T_FMT " fuels", m_pState->Recipes.size(), m_pState->Fuel.size());
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, unsigned int a_LineNum)
|
|
{
|
|
AString Line(a_Line);
|
|
Line.erase(Line.begin()); // Remove the beginning "!"
|
|
Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
|
|
|
|
std::unique_ptr<cItem> Item = cpp14::make_unique<cItem>();
|
|
int BurnTime;
|
|
|
|
const AStringVector & Sides = StringSplit(Line, "=");
|
|
if (Sides.size() != 2)
|
|
{
|
|
LOGWARNING("furnace.txt: line %d: A single '=' was expected, got " SIZE_T_FMT, a_LineNum, Sides.size() - 1);
|
|
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
|
|
return;
|
|
}
|
|
|
|
if (!ParseItem(Sides[0], *Item))
|
|
{
|
|
LOGWARNING("furnace.txt: line %d: Cannot parse item \"%s\".", a_LineNum, Sides[0].c_str());
|
|
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
|
|
return;
|
|
}
|
|
|
|
if (!StringToInteger<int>(Sides[1], BurnTime))
|
|
{
|
|
LOGWARNING("furnace.txt: line %d: Cannot parse burn time.", a_LineNum);
|
|
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
|
|
return;
|
|
}
|
|
|
|
// Add to fuel list:
|
|
cFuel Fuel;
|
|
Fuel.In = Item.release();
|
|
Fuel.BurnTime = BurnTime;
|
|
m_pState->Fuel.push_back(Fuel);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, unsigned int a_LineNum)
|
|
{
|
|
AString Line(a_Line);
|
|
Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
|
|
|
|
int CookTime = 200;
|
|
std::unique_ptr<cItem> InputItem = cpp14::make_unique<cItem>();
|
|
std::unique_ptr<cItem> OutputItem = cpp14::make_unique<cItem>();
|
|
|
|
const AStringVector & Sides = StringSplit(Line, "=");
|
|
if (Sides.size() != 2)
|
|
{
|
|
LOGWARNING("furnace.txt: line %d: A single '=' was expected, got " SIZE_T_FMT, a_LineNum, Sides.size() - 1);
|
|
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
|
|
return;
|
|
}
|
|
|
|
const AStringVector & InputSplit = StringSplit(Sides[0], "@");
|
|
if (!ParseItem(InputSplit[0], *InputItem))
|
|
{
|
|
LOGWARNING("furnace.txt: line %d: Cannot parse input item \"%s\".", a_LineNum, InputSplit[0].c_str());
|
|
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
|
|
return;
|
|
}
|
|
|
|
if (InputSplit.size() > 1)
|
|
{
|
|
if (!StringToInteger<int>(InputSplit[1], CookTime))
|
|
{
|
|
LOGWARNING("furnace.txt: line %d: Cannot parse cook time \"%s\".", a_LineNum, InputSplit[1].c_str());
|
|
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!ParseItem(Sides[1], *OutputItem))
|
|
{
|
|
LOGWARNING("furnace.txt: line %d: Cannot parse output item \"%s\".", a_LineNum, Sides[1].c_str());
|
|
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
|
|
return;
|
|
}
|
|
|
|
cRecipe Recipe;
|
|
Recipe.In = InputItem.release();
|
|
Recipe.Out = OutputItem.release();
|
|
Recipe.CookTime = CookTime;
|
|
m_pState->Recipes.push_back(Recipe);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cFurnaceRecipe::ParseItem(const AString & a_String, cItem & a_Item)
|
|
{
|
|
AString ItemString = a_String;
|
|
|
|
const AStringVector & SplitAmount = StringSplit(ItemString, ",");
|
|
ItemString = SplitAmount[0];
|
|
|
|
const AStringVector & SplitMeta = StringSplit(ItemString, ":");
|
|
ItemString = SplitMeta[0];
|
|
|
|
if (!StringToItem(ItemString, a_Item))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (SplitAmount.size() > 1)
|
|
{
|
|
if (!StringToInteger<char>(SplitAmount[1].c_str(), a_Item.m_ItemCount))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (SplitMeta.size() > 1)
|
|
{
|
|
if (!StringToInteger<short>(SplitMeta[1].c_str(), a_Item.m_ItemDamage))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cFurnaceRecipe::ClearRecipes(void)
|
|
{
|
|
for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
|
|
{
|
|
cRecipe Recipe = *itr;
|
|
delete Recipe.In;
|
|
Recipe.In = nullptr;
|
|
delete Recipe.Out;
|
|
Recipe.Out = nullptr;
|
|
}
|
|
m_pState->Recipes.clear();
|
|
|
|
for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
|
|
{
|
|
cFuel Fuel = *itr;
|
|
delete Fuel.In;
|
|
Fuel.In = nullptr;
|
|
}
|
|
m_pState->Fuel.clear();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const cFurnaceRecipe::cRecipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const
|
|
{
|
|
const cRecipe * BestRecipe = 0;
|
|
for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
|
|
{
|
|
const cRecipe & Recipe = *itr;
|
|
if ((Recipe.In->m_ItemType == a_Ingredient.m_ItemType) && (Recipe.In->m_ItemCount <= a_Ingredient.m_ItemCount))
|
|
{
|
|
if (BestRecipe && (BestRecipe->In->m_ItemCount > Recipe.In->m_ItemCount))
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
BestRecipe = &Recipe;
|
|
}
|
|
}
|
|
}
|
|
return BestRecipe;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cFurnaceRecipe::IsFuel(const cItem & a_Item) const
|
|
{
|
|
for (auto & Fuel : m_pState->Fuel)
|
|
{
|
|
if ((Fuel.In->m_ItemType == a_Item.m_ItemType) && (Fuel.In->m_ItemCount <= a_Item.m_ItemCount))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 cFuel & Fuel = *itr;
|
|
if ((Fuel.In->m_ItemType == a_Fuel.m_ItemType) && (Fuel.In->m_ItemCount <= a_Fuel.m_ItemCount))
|
|
{
|
|
if (BestFuel > 0 && (BestFuel > Fuel.BurnTime))
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
BestFuel = Fuel.BurnTime;
|
|
}
|
|
}
|
|
}
|
|
return BestFuel;
|
|
}
|
|
|
|
|
|
|
|
|