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