#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "FurnaceRecipe.h" #include "Item.h" #include #define FURNACE_RECIPE_FILE "furnace.txt" typedef std::list RecipeList; typedef std::list 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++; // Remove comments from the line: size_t FirstCommentSymbol = ParsingLine.find('#'); if ((FirstCommentSymbol != AString::npos) && (FirstCommentSymbol != 0)) { ParsingLine.erase(ParsingLine.begin() + static_cast(FirstCommentSymbol), ParsingLine.end()); } if (IsOnlyWhitespace(ParsingLine)) { // Ignore empty and whitespace only lines continue; } switch (ParsingLine[0]) { case '#': { // Comment break; } case '!': { AddFuelFromLine(ParsingLine, LineNum); break; } default: { AddRecipeFromLine(ParsingLine, LineNum); break; } } // switch (ParsingLine[0]) } // while (getline(ParsingLine)) LOG("Loaded %zu furnace recipes and %zu 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 Item = std::make_unique(); int BurnTime; const AStringVector & Sides = StringSplit(Line, "="); if (Sides.size() != 2) { LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %zu", 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(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; float Reward = 0; std::unique_ptr InputItem = std::make_unique(); std::unique_ptr OutputItem = std::make_unique(); const AStringVector & Sides = StringSplit(Line, "="); if (Sides.size() != 2) { LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %zu", 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(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; } } const AStringVector & OutputSplit = StringSplit(Sides[1], "$"); if (!ParseItem(OutputSplit[0], *OutputItem)) { LOGWARNING("furnace.txt: line %d: Cannot parse output item \"%s\".", a_LineNum, OutputSplit[0].c_str()); LOGINFO("Offending line: \"%s\"", a_Line.c_str()); return; } if (OutputSplit.size() > 1) { if (!StringToFloat(OutputSplit[1], Reward)) { LOGWARNING("furnace.txt: line %d: Cannot parse reward \"%s\".", a_LineNum, OutputSplit[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; Recipe.Reward = Reward; 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(SplitAmount[1], a_Item.m_ItemCount)) { return false; } } if (SplitMeta.size() > 1) { if (!StringToInteger(SplitMeta[1], 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 = nullptr; 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 if ((Recipe.In->m_ItemDamage == -1) || (Recipe.In->m_ItemDamage == a_Ingredient.m_ItemDamage)) { 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; }