From 386d58b5862d8b76925c6523721594887606e82a Mon Sep 17 00:00:00 2001 From: faketruth Date: Mon, 3 Oct 2011 18:41:19 +0000 Subject: MCServer c++ source files git-svn-id: http://mc-server.googlecode.com/svn/trunk@3 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/cRecipeChecker.cpp | 458 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 458 insertions(+) create mode 100644 source/cRecipeChecker.cpp (limited to 'source/cRecipeChecker.cpp') diff --git a/source/cRecipeChecker.cpp b/source/cRecipeChecker.cpp new file mode 100644 index 000000000..d85cb3c31 --- /dev/null +++ b/source/cRecipeChecker.cpp @@ -0,0 +1,458 @@ +#include "cRecipeChecker.h" + +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "Defines.h" +#include "cMCLogger.h" +#include "cRoot.h" + +typedef std::list< cRecipeChecker::Recipe* > RecipeList; +struct cRecipeChecker::sRecipeCheckerState +{ + RecipeList Recipes; +}; + +cRecipeChecker* cRecipeChecker::GetRecipeChecker() +{ + LOGWARN("WARNING: Using deprecated function cRecipeChecker::GetRecipeChecker() use cRoot::Get()->GetRecipeChecker() instead!"); + return cRoot::Get()->GetRecipeChecker(); +} + +cRecipeChecker::Recipe::~Recipe() +{ + delete [] Slots; + Slots = 0; +} + +cRecipeChecker::~cRecipeChecker() +{ + ClearRecipes(); + delete m_pState; +} + +cRecipeChecker::cRecipeChecker() + : m_pState( new sRecipeCheckerState ) +{ + ReloadRecipes(); +} + +void cRecipeChecker::ClearRecipes() +{ + while( m_pState->Recipes.size() > 0 ) + { + delete *m_pState->Recipes.begin(); + m_pState->Recipes.remove( *m_pState->Recipes.begin() ); + } +} + +void PrintRecipe( std::vector< cRecipeChecker::RecipeSlot > & RecipeSlots ) +{ + LOG("Recipe:"); + for(unsigned int i = 0; i < RecipeSlots.size(); i++) + { + cRecipeChecker::RecipeSlot Slot = RecipeSlots[i]; + LOG("x:%i y:%i id:%i #%i", Slot.x, Slot.y, Slot.Item.m_ItemID, Slot.Item.m_ItemCount ); + } +} + +void PrintNear( std::ifstream & f, int a_History = 64 ) +{ + f.clear(); + + // Calculate how far we can move pointer back + std::streamoff Position = f.tellg(); + f.seekg( 0, std::ios_base::beg ); + std::streamoff Difference = Position - f.tellg(); + if( Difference > a_History ) Difference = a_History; + f.seekg( Position - Difference, std::ios_base::beg ); + + std::string Near; + if( f.good() ) + { + while( f.good() && Near.size() < (unsigned int)Difference ) + { + char c; + f.get(c); + Near.push_back( c ); + } + } + LOGERROR("Error near: \"%s\"", Near.c_str() ); +} + +void cRecipeChecker::ReloadRecipes() +{ + LOG("--Loading recipes--"); + ClearRecipes(); + + /* + char a_File[] = "recipes.txt"; + + FILE* f = 0; + #ifdef _WIN32 + if( fopen_s(&f, a_File, "rb" ) == 0 ) // no error + #else + if( (f = fopen(a_File, "rb" )) != 0 ) // no error + #endif + { + char c; + while( fread( &c, sizeof(char), 1, f) == 1 ) + { + + } + } + else + { + LOG("Could not open file for recipes: %s", a_File); + return; + } + */ + + + std::ifstream f; + + char a_File[] = "recipes.txt"; + f.open(a_File, std::ios::in); + std::string input; + + if( !f.good() ) + { + f.close(); + LOG("Could not open file for recipes: %s", a_File); + return; + } + + std::vector< RecipeSlot > RecipeSlots; + + bool bError = false; + while( f.good() ) + { + bool bLoadSlot = false; + bool bLoadResult = false; + + char c; + f >> c; + f.unget(); + if( c == '#' ) + { + //LOG("Ignoring comment"); + while( f.good() && c != '\n' ) + { + f.get( c ); + } + continue; + } + + f.get( c ); + while( f.good() && ( c == '\n' || c == '\r' ) ) { f.get( c ); } + if( f.eof() ) break; + f.unget(); + + int width, height; + f >> width; + f >> c; if( c != 'x' ) { bError=true; break; } + f >> height; + f >> c; + if( c == ',' ) bLoadSlot = true; + + while( f.good() && bLoadSlot ) + { + bool bDontAddRecipe = false; + int x, y, ItemID, Amount; + if( f.peek() == '*' ) + { + f >> c; + x = -1; + } + else + { + f >> x; + } + f >> c; if( c != ':' ) { bError=true; break; } + if( f.peek() == '*' ) + { + f >> c; + y = -1; + } + else + { + f >> y; + } + f >> c; if( c != ':' ) { bError=true; break; } + f >> ItemID; + f >> c; if( c != ':' ) { bError=true; break; } + f >> Amount; + + f >> c; + if( c == '@' ) bLoadResult = true; + if( c != ',' ) bLoadSlot = false; + + if( !isValidItem( ItemID ) ) + { + LOGERROR("Error in recipes file (%s): Invalid Item ID %i", a_File, ItemID ); + bDontAddRecipe = true; + } + if( x < 0 && y < 0 ) + { + if( Amount < 0 ) + { + LOGERROR("Error in recipes file (%s): Invalid use of negative amount on wildcard coordinates", a_File ); + bDontAddRecipe = true; + } + for(int x = 0; x < width; ++x) for(int y = 0; y < height; ++y ) + { + cItem Item( (ENUM_ITEM_ID)ItemID, (char)Amount ); + RecipeSlot Slot; + Slot.Item = Item; + Slot.x = x; + Slot.y = y; + RecipeSlots.push_back( Slot ); + } + } + else if( x < 0 ) + { + if( Amount < 0 ) + { + for(int x = 0; x < width; ++x) for(int yy = 0; yy < height; ++yy ) + { + if( yy == y-1 ) continue; + cItem Item( (ENUM_ITEM_ID)ItemID, (char)abs(Amount) ); + RecipeSlot Slot; + Slot.Item = Item; + Slot.x = x; + Slot.y = yy; + RecipeSlots.push_back( Slot ); + } + } + else + { + for(int x = 0; x < width; ++x) + { + cItem Item( (ENUM_ITEM_ID)ItemID, (char)Amount ); + RecipeSlot Slot; + Slot.Item = Item; + Slot.x = x; + Slot.y = y-1; + RecipeSlots.push_back( Slot ); + } + } + } + else if( y < 0 ) + { + if( Amount < 0 ) + { + for(int xx = 0; xx < width; ++xx) for(int y = 0; y < height; ++y ) + { + if( xx == x-1 ) continue; + cItem Item( (ENUM_ITEM_ID)ItemID, (char)abs(Amount) ); + RecipeSlot Slot; + Slot.Item = Item; + Slot.x = xx; + Slot.y = y; + RecipeSlots.push_back( Slot ); + } + } + else + { + for(int y = 0; y < height; ++y) + { + cItem Item( (ENUM_ITEM_ID)ItemID, (char)Amount ); + RecipeSlot Slot; + Slot.Item = Item; + Slot.x = x-1; + Slot.y = y; + RecipeSlots.push_back( Slot ); + } + } + } + else + { + if( Amount < 0 ) + { + for(int xx = 0; xx < width; ++xx) for(int yy = 0; yy < height; ++yy ) + { + if( xx == x-1 && yy == y-1 ) continue; + cItem Item( (ENUM_ITEM_ID)ItemID, (char)abs(Amount) ); + RecipeSlot Slot; + Slot.Item = Item; + Slot.x = xx; + Slot.y = yy; + RecipeSlots.push_back( Slot ); + } + } + else + { + cItem Item( (ENUM_ITEM_ID)ItemID, (char)Amount ); + RecipeSlot Slot; + Slot.Item = Item; + Slot.x = x-1; + Slot.y = y-1; + RecipeSlots.push_back( Slot ); + } + } + //LOG("%i %i %i %i", x, y, ItemID, Amount ); + + if( bLoadResult ) + { + bLoadResult = false; + f >> ItemID; + f >> c; if( c != ':' ) { bError=true; break; } + f >> Amount; + //LOG("%i %i", ItemID, Amount ); + if( !isValidItem( ItemID ) ) + { + LOGERROR("Error in recipes file (%s): Invalid Item ID %i", a_File, ItemID ); + bDontAddRecipe = true; + } + + // Do a sanity check - Handshake algorithm :) + bool bDuplicateEntries = false; + for(unsigned int i = 0; i < RecipeSlots.size(); i++) + { + for(unsigned int j = i+1; j < RecipeSlots.size(); j++) + { + if( RecipeSlots[i].x == RecipeSlots[j].x && RecipeSlots[i].y == RecipeSlots[j].y ) + { + LOGERROR("Error in recipes file (%s): Duplicate item on coordinates %i:%i", a_File, RecipeSlots[i].x+1, RecipeSlots[i].y+1 ); + bDontAddRecipe = true; + bDuplicateEntries = true; + break; + } + } + } + if( bDuplicateEntries ) + { + PrintNear( f, 64 ); + PrintRecipe( RecipeSlots ); + } + + if( bDontAddRecipe == false ) + { + cItem Item( (ENUM_ITEM_ID)ItemID, (char)Amount ); + Recipe* recipe = new Recipe; + recipe->Result = Item; + recipe->NumItems = RecipeSlots.size(); + recipe->Slots = new RecipeSlot[ recipe->NumItems ]; + memcpy( recipe->Slots, &RecipeSlots[0], sizeof(RecipeSlot)*recipe->NumItems ); + m_pState->Recipes.push_back( recipe ); + //LOG("Loaded recipe for %i times %i", Amount, ItemID ); + } + + RecipeSlots.clear(); + } + } + if( bError ) break; + } + if( bError || ( !f.eof() && !f.good() ) ) + { + LOGERROR("Error: Wrong format"); + PrintNear( f, 128 ); + } + f.close(); + + LOG("Found %i recipes", m_pState->Recipes.size() ); +// for(RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr ) +// { +// LOG("Recipe for %i times %i", (*itr)->Result.m_ItemCount, (*itr)->Result.m_ItemID ); +// for(unsigned int j = 0; j < (*itr)->NumItems; j++) +// { +// RecipeSlot Slot = (*itr)->Slots[j]; +// LOG("%i %i %i %i", Slot.x, Slot.y, Slot.Item.m_ItemID, Slot.Item.m_ItemCount ); +// } +// } + LOG("--Done loading recipes--"); +} + +cItem cRecipeChecker::CookIngredients( cItem* a_Items, int a_Width, int a_Height, bool a_bConsumeIngredients /* = false */ ) +{ + int iLeft = 999, iTop = 999; + int iRight = 0, iBottom = 0; + for(int y = 0; y < a_Height; y++ ) for(int x = 0; x < a_Width; x++) + { + cItem Item = a_Items[x + y*a_Width]; + if( Item.m_ItemID != E_ITEM_EMPTY && Item.m_ItemCount > 0 ) + { + iRight = MAX(x, iRight); + iBottom = MAX(y, iBottom); + iLeft = MIN(x, iLeft); + iTop = MIN(y, iTop); + } + } + + for(RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr ) + { + Recipe* recipe = (*itr); + int Left = 999, Top = 999; + int Right = 0, Bottom = 0; + for(unsigned int i = 0; i < recipe->NumItems; i++) + { + Right = MAX(recipe->Slots[i].x, Right); + Bottom = MAX(recipe->Slots[i].y, Bottom); + Left = MIN(recipe->Slots[i].x, Left); + Top = MIN(recipe->Slots[i].y, Top); + } + if( Right-Left != iRight-iLeft || Bottom-Top != iBottom-iTop ) continue; + // it has the right dimensions + + // Check for empty spaces + unsigned int Hash = 0; + for(unsigned int i = 0; i < recipe->NumItems; i++) + { + int x = recipe->Slots[i].x - Left + iLeft +1; + int y = recipe->Slots[i].y - Top + iTop +1; + Hash += x + y * a_Width; + } + for(int y = 0; y < a_Height; y++ ) for(int x = 0; x < a_Width; x++) + { + cItem & Item = a_Items[x + y*a_Width]; + if( Item.m_ItemID != E_ITEM_EMPTY && Item.m_ItemCount > 0 ) + { + Hash -= (x+1) + (y+1)*a_Width; + } + } + if( Hash != 0 ) continue; // Empty spaces not in right place + + bool bWrong = false; + for(unsigned int i = 0; i < recipe->NumItems; i++) + { + int x = recipe->Slots[i].x - Left + iLeft; + int y = recipe->Slots[i].y - Top + iTop; + cItem Item = a_Items[x + y*a_Width]; + if( Item.m_ItemID != recipe->Slots[i].Item.m_ItemID + || Item.m_ItemCount < recipe->Slots[i].Item.m_ItemCount ) + { + bWrong = true; + break; + } + } + if( bWrong ) continue; + + cItem Dish = recipe->Result; + + // else + if( a_bConsumeIngredients ) + { + // Consume! nomnom~ + for(unsigned int i = 0; i < recipe->NumItems; i++) + { + int x = recipe->Slots[i].x - Left + iLeft; + int y = recipe->Slots[i].y - Top + iTop; + a_Items[x + y*a_Width].m_ItemCount -= recipe->Slots[i].Item.m_ItemCount; + if( a_Items[x + y*a_Width].m_ItemCount <= 0 ) a_Items[x + y*a_Width].Empty(); + } + Dish = CookIngredients( a_Items, a_Width, a_Height, false ); + } + + // Return the resulting dish! + return Dish; + } + return cItem(); +} -- cgit v1.2.3