summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Entities/Pickup.cpp2
-rw-r--r--src/FurnaceRecipe.cpp268
-rw-r--r--src/FurnaceRecipe.h30
-rw-r--r--src/Mobs/Pig.cpp20
-rw-r--r--src/Mobs/Pig.h1
5 files changed, 226 insertions, 95 deletions
diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp
index 969461a6a..10b6bbd5c 100644
--- a/src/Entities/Pickup.cpp
+++ b/src/Entities/Pickup.cpp
@@ -52,7 +52,7 @@ public:
return false;
}
- m_Pickup->GetItem().AddCount(CombineCount);
+ m_Pickup->GetItem().AddCount((char)CombineCount);
Item.m_ItemCount -= CombineCount;
if (Item.m_ItemCount <= 0)
diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp
index 2cb204ccf..8add9610c 100644
--- a/src/FurnaceRecipe.cpp
+++ b/src/FurnaceRecipe.cpp
@@ -5,7 +5,8 @@
#include "Item.h"
#include <fstream>
-#include <sstream>
+
+#define FURNACE_RECIPE_FILE "furnace.txt"
@@ -54,128 +55,207 @@ void cFurnaceRecipe::ReloadRecipes(void)
ClearRecipes();
LOGD("Loading furnace recipes...");
- std::ifstream f;
- char a_File[] = "furnace.txt";
- f.open(a_File, std::ios::in);
-
+ std::ifstream f(FURNACE_RECIPE_FILE, std::ios::in);
if (!f.good())
{
- f.close();
- LOG("Could not open the furnace recipes file \"%s\"", a_File);
+ LOG("Could not open the furnace recipes file \"%s\". No furnace recipes are available.", FURNACE_RECIPE_FILE);
return;
}
+
+ unsigned int LineNum = 0;
+ AString ParsingLine;
- // TODO: Replace this messy parse with a high-level-structured one (ReadLine / ProcessLine)
- bool bSyntaxError = false;
- while (f.good())
+ while (std::getline(f, ParsingLine))
{
- char c;
+ LineNum++;
+ ParsingLine.erase(std::remove_if(ParsingLine.begin(), ParsingLine.end(), isspace), ParsingLine.end()); // Remove ALL whitespace from the line
+ if (ParsingLine.empty())
+ {
+ continue;
+ }
- //////////////////////////////////////////////////////////////////////////
- // comments
- f >> c;
- f.unget();
- if( c == '#' )
+ switch (ParsingLine[0])
{
- while( f.good() && c != '\n' )
+ case '#':
{
- f.get( c );
+ // Comment
+ break;
}
- continue;
- }
+
+ 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, int a_LineNum)
+{
+ // Fuel
+ int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0;
+ AString::size_type BeginPos = 1; // Begin at one after exclamation mark (bang)
+
+ if (
+ !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID
+ !ReadOptionalNumbers(BeginPos, ":", "=", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health)
+ !ReadMandatoryNumber(BeginPos, "0123456789", a_Line, a_LineNum, IBurnTime, true) // Read item burn time - last value
+ )
+ {
+ return;
+ }
+
+ // 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);
+}
+
+
+
+
+
+void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, int a_LineNum)
+{
+ 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, ":", a_Line, a_LineNum, IItemID) || // Read item ID
+ !ReadOptionalNumbers(BeginPos, ":", "@", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health)
+ !ReadMandatoryNumber(BeginPos, "=", a_Line, a_LineNum, IBurnTime) || // Read item burn time
+ !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, OItemID) || // Read result ID
+ !ReadOptionalNumbers(BeginPos, ":", "012456789", a_Line, a_LineNum, OItemCount, OItemHealth, true) // Read result count (and optionally health) - last value
+ )
+ {
+ return;
+ }
+
+ // 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);
+}
+
+
+
- //////////////////////////////////////////////////////////////////////////
- // Line breaks
- f.get( c );
- while( f.good() && ( c == '\n' || c == '\r' ) ) { f.get( c ); }
- if (f.eof())
+void cFurnaceRecipe::PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing)
+{
+ LOGWARN("Error parsing furnace recipes at line %i pos " SIZE_T_FMT ": 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, unsigned int a_Line, int & a_Value, bool a_IsLastValue)
+{
+ // TODO: replace atoi with std::stoi
+ 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)
{
- break;
+ PrintParseError(a_Line, a_Begin, a_Delimiter);
+ return false;
}
- f.unget();
+ }
+
+ // 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)))
+ {
+ PrintParseError(a_Line, a_Begin, "number");
+ return false;
+ }
+ a_Value = atoi(a_Text.substr(a_Begin, End - a_Begin).c_str());
+
+ a_Begin = End + 1; // Jump over delimiter
+ return true;
+}
+
+
+
+
- //////////////////////////////////////////////////////////////////////////
- // Check for fuel
- f >> c;
- if( c == '!' ) // It's fuel :)
+bool cFurnaceRecipe::ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue)
+{
+ // TODO: replace atoi with std::stoi
+ AString::size_type 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)))
{
- // Read item
- int IItemID = 0, IItemCount = 0, IItemHealth = 0;
- f >> IItemID;
- f >> c; if( c != ':' ) { bSyntaxError = true; break; }
- f >> IItemCount;
-
- // Optional health
- f >> c;
- if( c != ':' )
- f.unget();
+ a_ValueOne = std::atoi(a_Text.substr(Begin, End - Begin).c_str());
+ Begin = End + 1;
+
+ if (a_IsLastValue)
+ {
+ End = a_Text.find_first_not_of(a_DelimiterTwo, Begin);
+ }
else
{
- f >> IItemHealth;
+ End = a_Text.find_first_of(a_DelimiterTwo, Begin);
+ if (End == AString::npos)
+ {
+ PrintParseError(a_Line, Begin, a_DelimiterTwo);
+ return false;
+ }
}
- // Burn time
- int BurnTime;
- f >> c; if( c != '=' ) { bSyntaxError = true; break; }
- f >> BurnTime;
+ // stoi won't throw an exception if the string is alphanumeric, we should check for this
+ if (!DoesStringContainOnlyNumbers(a_Text.substr(Begin, End - Begin)))
+ {
+ PrintParseError(a_Line, Begin, "number");
+ return false;
+ }
+ a_ValueTwo = atoi(a_Text.substr(Begin, End - Begin).c_str());
- // Add to fuel list
- Fuel F;
- F.In = new cItem( (ENUM_ITEM_ID) IItemID, (char)IItemCount, (short)IItemHealth );
- F.BurnTime = BurnTime;
- m_pState->Fuel.push_back( F );
- continue;
+ a_Begin = End + 1; // Jump over delimiter
+ return true;
}
- f.unget();
-
- //////////////////////////////////////////////////////////////////////////
- // Read items
- int IItemID = 0, IItemCount = 0, IItemHealth = 0;
- f >> IItemID;
- f >> c; if( c != ':' ) { bSyntaxError = true; break; }
- f >> IItemCount;
-
- // Optional health
- f >> c;
- if( c != ':' )
- f.unget();
else
{
- f >> IItemHealth;
+ return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue);
}
+ }
+
+ return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue);
+}
- int CookTime;
- f >> c; if( c != '@' ) { bSyntaxError = true; break; }
- f >> CookTime;
- int OItemID = 0, OItemCount = 0, OItemHealth = 0;
- f >> c; if( c != '=' ) { bSyntaxError = true; break; }
- f >> OItemID;
- f >> c; if( c != ':' ) { bSyntaxError = true; break; }
- f >> OItemCount;
- // Optional health
- f >> c;
- if( c != ':' )
- f.unget();
- else
- {
- f >> OItemHealth;
- }
- // 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 = CookTime;
- m_pState->Recipes.push_back( R );
- }
- if (bSyntaxError)
- {
- LOGERROR("ERROR: FurnaceRecipe, syntax error" );
- }
- LOG("Loaded " SIZE_T_FMT " furnace recipes and " SIZE_T_FMT " fuels", m_pState->Recipes.size(), m_pState->Fuel.size());
+
+bool cFurnaceRecipe::DoesStringContainOnlyNumbers(const AString & a_String)
+{
+ // TODO: replace this with std::all_of(a_String.begin(), a_String.end(), isdigit)
+ return (a_String.find_first_not_of("0123456789") == AString::npos);
}
diff --git a/src/FurnaceRecipe.h b/src/FurnaceRecipe.h
index 2f91e9bcb..77ed35a57 100644
--- a/src/FurnaceRecipe.h
+++ b/src/FurnaceRecipe.h
@@ -41,6 +41,36 @@ public:
private:
void ClearRecipes(void);
+ /** Parses the fuel contained in the line, adds it to m_pState's fuels.
+ Logs a warning to the console on input error. */
+ void AddFuelFromLine(const AString & a_Line, int a_LineNum);
+
+ /** Parses the recipe contained in the line, adds it to m_pState's recipes.
+ Logs a warning to the console on input error. */
+ void AddRecipeFromLine(const AString & a_Line, int a_LineNum);
+
+ /** Calls LOGWARN with the line, position, and error */
+ static void PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing);
+
+ /** Reads a number from a string given, starting at a given position and ending at a delimiter given
+ Updates beginning position to the delimiter found + 1, and updates the value to the one read
+ If it encounters a substring that is not fully numeric, it will call SetParseError() and return false; the caller should abort processing
+ Otherwise, the function will return true
+ */
+ static bool ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue = false);
+
+ /** Reads two numbers from a string given, starting at a given position and ending at the first delimiter given, then again (with an updated position) until the second delimiter given
+ Updates beginning position to the second delimiter found + 1, and updates the values to the ones read
+ If it encounters a substring that is not fully numeric whilst reading the second value, it will call SetParseError() and return false; the caller should abort processing
+ If this happens whilst reading the first value, it will call ReadMandatoryNumber() with the appropriate position, as this may legitimately occur with the optional value and AString::find_first_of finding the incorrect delimiter. It will return the result of ReadMandatoryNumber()
+ True will be returned definitively for an optional value that is valid
+ */
+ static bool ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue = false);
+
+ /** Uses std::all_of to determine if a string contains only digits */
+ static bool DoesStringContainOnlyNumbers(const AString & a_String);
+
+
struct sFurnaceRecipeState;
sFurnaceRecipeState * m_pState;
};
diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp
index e862f5aaa..1f77cf613 100644
--- a/src/Mobs/Pig.cpp
+++ b/src/Mobs/Pig.cpp
@@ -78,3 +78,23 @@ void cPig::OnRightClicked(cPlayer & a_Player)
+
+
+void cPig::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ // If the attachee player is holding a carrot-on-stick, let them drive this pig:
+ if (m_bIsSaddled && (m_Attachee != NULL))
+ {
+ if (m_Attachee->IsPlayer() && (m_Attachee->GetEquippedWeapon().m_ItemType == E_ITEM_CARROT_ON_STICK))
+ {
+ MoveToPosition((m_Attachee->GetPosition()) + (m_Attachee->GetLookVector()*10));
+ m_bMovingToDestination = true;
+ }
+ }
+}
+
+
+
+
diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h
index d434324c1..313af2f44 100644
--- a/src/Mobs/Pig.h
+++ b/src/Mobs/Pig.h
@@ -19,6 +19,7 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_CARROT); }