2012-06-14 13:06:06 +00:00
// CraftingRecipes.cpp
// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes
#include "Globals.h"
#include "CraftingRecipes.h"
2012-09-23 22:09:57 +00:00
#include "Root.h"
#include "PluginManager.h"
2012-06-14 13:06:06 +00:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCraftingGrid:
cCraftingGrid :: cCraftingGrid ( int a_Width , int a_Height ) :
m_Width ( a_Width ),
m_Height ( a_Height ),
m_Items ( new cItem [ a_Width * a_Height ])
{
}
2012-09-20 13:25:54 +00:00
cCraftingGrid :: cCraftingGrid ( const cItem * a_Items , int a_Width , int a_Height ) :
2012-06-14 13:06:06 +00:00
m_Width ( a_Width ),
m_Height ( a_Height ),
m_Items ( new cItem [ a_Width * a_Height ])
{
for ( int i = a_Width * a_Height - 1 ; i >= 0 ; i -- )
{
m_Items [ i ] = a_Items [ i ];
}
}
cCraftingGrid :: cCraftingGrid ( const cCraftingGrid & a_Original ) :
m_Width ( a_Original . m_Width ),
m_Height ( a_Original . m_Height ),
m_Items ( new cItem [ a_Original . m_Width * a_Original . m_Height ])
{
for ( int i = m_Width * m_Height - 1 ; i >= 0 ; i -- )
{
m_Items [ i ] = a_Original . m_Items [ i ];
}
}
cCraftingGrid ::~ cCraftingGrid ()
{
delete [] m_Items ;
}
cItem & cCraftingGrid :: GetItem ( int x , int y ) const
{
// Accessible through scripting, must verify parameters:
if (( x < 0 ) || ( x >= m_Width ) || ( y < 0 ) || ( y >= m_Height ))
{
LOGERROR ( "Attempted to get an invalid item from a crafting grid: (%d, %d), grid dimensions: (%d, %d)." ,
x , y , m_Width , m_Height
);
return m_Items [ 0 ];
}
return m_Items [ x + m_Width * y ];
}
void cCraftingGrid :: SetItem ( int x , int y , ENUM_ITEM_ID a_ItemType , int a_ItemCount , short a_ItemHealth )
{
// Accessible through scripting, must verify parameters:
if (( x < 0 ) || ( x >= m_Width ) || ( y < 0 ) || ( y >= m_Height ))
{
LOGERROR ( "Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d)." ,
x , y , m_Width , m_Height
);
return ;
}
m_Items [ x + m_Width * y ] = cItem ( a_ItemType , a_ItemCount , a_ItemHealth );
}
void cCraftingGrid :: SetItem ( int x , int y , const cItem & a_Item )
{
// Accessible through scripting, must verify parameters:
if (( x < 0 ) || ( x >= m_Width ) || ( y < 0 ) || ( y >= m_Height ))
{
LOGERROR ( "Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d)." ,
x , y , m_Width , m_Height
);
return ;
}
m_Items [ x + m_Width * y ] = a_Item ;
}
void cCraftingGrid :: Clear ( void )
{
for ( int y = 0 ; y < m_Height ; y ++ ) for ( int x = 0 ; x < m_Width ; x ++ )
{
m_Items [ x + m_Width * y ]. Empty ();
}
}
void cCraftingGrid :: ConsumeGrid ( const cCraftingGrid & a_Grid )
{
if (( a_Grid . m_Width != m_Width ) || ( a_Grid . m_Height != m_Height ))
{
LOGWARNING ( "Consuming a grid of different dimensions: (%d, %d) vs (%d, %d)" ,
a_Grid . m_Width , a_Grid . m_Height , m_Width , m_Height
);
}
int MinX = std :: min ( a_Grid . m_Width , m_Width );
int MinY = std :: min ( a_Grid . m_Height , m_Height );
for ( int y = 0 ; y < MinY ; y ++ ) for ( int x = 0 ; x < MinX ; x ++ )
{
int ThatIdx = x + a_Grid . m_Width * y ;
if ( a_Grid . m_Items [ ThatIdx ]. IsEmpty ())
{
continue ;
}
int ThisIdx = x + m_Width * y ;
2013-01-12 04:46:01 +00:00
if ( a_Grid . m_Items [ ThatIdx ]. m_ItemType != m_Items [ ThisIdx ]. m_ItemType )
2012-06-14 13:06:06 +00:00
{
LOGWARNING ( "Consuming incompatible grids: item at (%d, %d) is %d in grid and %d in ingredients. Item not consumed." ,
2013-01-12 04:46:01 +00:00
x , y , m_Items [ ThisIdx ]. m_ItemType , a_Grid . m_Items [ ThatIdx ]. m_ItemType
2012-06-14 13:06:06 +00:00
);
continue ;
}
char NumWantedItems = a_Grid . m_Items [ ThatIdx ]. m_ItemCount ;
if ( NumWantedItems > m_Items [ ThisIdx ]. m_ItemCount )
{
LOGWARNING ( "Consuming more items than there actually are in slot (%d, %d), item %d (want %d, have %d). Item zeroed out." ,
2013-01-12 04:46:01 +00:00
x , y , m_Items [ ThisIdx ]. m_ItemType ,
2012-06-14 13:06:06 +00:00
NumWantedItems , m_Items [ ThisIdx ]. m_ItemCount
);
NumWantedItems = m_Items [ ThisIdx ]. m_ItemCount ;
}
m_Items [ ThisIdx ]. m_ItemCount -= NumWantedItems ;
if ( m_Items [ ThisIdx ]. m_ItemCount == 0 )
{
m_Items [ ThisIdx ]. Clear ();
}
} // for x, for y
}
void cCraftingGrid :: CopyToItems ( cItem * a_Items ) const
{
for ( int i = m_Height * m_Width - 1 ; i >= 0 ; i -- )
{
a_Items [ i ] = m_Items [ i ];
} // for x, for y
}
void cCraftingGrid :: Dump ( void )
{
for ( int y = 0 ; y < m_Height ; y ++ ) for ( int x = 0 ; x < m_Width ; x ++ )
{
int idx = x + m_Width * y ;
LOGD ( "Slot (%d, %d): Type %d, health %d, count %d" ,
2013-01-12 04:46:01 +00:00
x , y , m_Items [ idx ]. m_ItemType , m_Items [ idx ]. m_ItemDamage , m_Items [ idx ]. m_ItemCount
2012-06-14 13:06:06 +00:00
);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCraftingRecipe:
cCraftingRecipe :: cCraftingRecipe ( const cCraftingGrid & a_CraftingGrid ) :
m_Ingredients ( a_CraftingGrid )
{
}
void cCraftingRecipe :: Clear ( void )
{
m_Ingredients . Clear ();
m_Result . Clear ();
}
void cCraftingRecipe :: SetResult ( ENUM_ITEM_ID a_ItemType , int a_ItemCount , short a_ItemHealth )
{
m_Result = cItem ( a_ItemType , a_ItemCount , a_ItemHealth );
}
void cCraftingRecipe :: ConsumeIngredients ( cCraftingGrid & a_CraftingGrid )
{
a_CraftingGrid . ConsumeGrid ( m_Ingredients );
}
void cCraftingRecipe :: Dump ( void )
{
LOGD ( "Recipe ingredients:" );
m_Ingredients . Dump ();
LOGD ( "Result: Type %d, health %d, count %d" ,
2013-01-12 04:46:01 +00:00
m_Result . m_ItemType , m_Result . m_ItemDamage , m_Result . m_ItemCount
2012-06-14 13:06:06 +00:00
);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCraftingRecipes:
cCraftingRecipes :: cCraftingRecipes ( void )
{
LoadRecipes ();
}
cCraftingRecipes ::~ cCraftingRecipes ()
{
ClearRecipes ();
}
void cCraftingRecipes :: GetRecipe ( const cPlayer * a_Player , const cCraftingGrid & a_CraftingGrid , cCraftingRecipe & a_Recipe )
{
// Allow plugins to intercept recipes using a pre-craft hook:
if ( cRoot :: Get () -> GetPluginManager () -> CallHookPreCrafting ( a_Player , & a_CraftingGrid , & a_Recipe ))
{
return ;
}
// Built-in recipes:
std :: auto_ptr < cRecipe > Recipe ( FindRecipe ( a_CraftingGrid . GetItems (), a_CraftingGrid . GetWidth (), a_CraftingGrid . GetHeight ()));
a_Recipe . Clear ();
if ( Recipe . get () == NULL )
{
// Allow plugins to intercept a no-recipe-found situation:
cRoot :: Get () -> GetPluginManager () -> CallHookCraftingNoRecipe ( a_Player , & a_CraftingGrid , & a_Recipe );
return ;
}
for ( cRecipeSlots :: const_iterator itr = Recipe -> m_Ingredients . begin (); itr != Recipe -> m_Ingredients . end (); ++ itr )
{
a_Recipe . SetIngredient ( itr -> x , itr -> y , itr -> m_Item );
} // for itr
a_Recipe . SetResult ( Recipe -> m_Result );
// Allow plugins to intercept recipes after they are processed:
cRoot :: Get () -> GetPluginManager () -> CallHookPostCrafting ( a_Player , & a_CraftingGrid , & a_Recipe );
}
void cCraftingRecipes :: LoadRecipes ( void )
{
2013-09-28 20:36:01 +01:00
LOGD ( "Loading crafting recipes from crafting.txt..." );
2012-06-14 13:06:06 +00:00
ClearRecipes ();
// Load the crafting.txt file:
cFile f ;
if ( ! f . Open ( "crafting.txt" , cFile :: fmRead ))
{
LOGWARNING ( "Cannot open file \" crafting.txt \" , no crafting recipes will be available!" );
return ;
}
AString Everything ;
f . ReadRestOfFile ( Everything );
f . Close ();
// Split it into lines, then process each line as a single recipe:
AStringVector Split = StringSplit ( Everything , " \n " );
int LineNum = 1 ;
for ( AStringVector :: const_iterator itr = Split . begin (); itr != Split . end (); ++ itr , ++ LineNum )
{
// Remove anything after a '#' sign and trim away the whitespace:
AString Recipe = TrimString ( itr -> substr ( 0 , itr -> find ( '#' )));
if ( Recipe . empty ())
{
// Empty recipe
continue ;
}
AddRecipeLine ( LineNum , Recipe );
} // for itr - Split[]
2013-09-28 20:36:01 +01:00
LOG ( "Loaded %d crafting recipes" , m_Recipes . size ());
2012-06-14 13:06:06 +00:00
}
void cCraftingRecipes :: ClearRecipes ( void )
{
for ( cRecipes :: iterator itr = m_Recipes . begin (); itr != m_Recipes . end (); ++ itr )
{
delete * itr ;
}
m_Recipes . clear ();
}
void cCraftingRecipes :: AddRecipeLine ( int a_LineNum , const AString & a_RecipeLine )
{
AStringVector Sides = StringSplit ( a_RecipeLine , "=" );
if ( Sides . size () != 2 )
{
LOGWARNING ( "crafting.txt: line %d: A single '=' was expected, got %d" , a_LineNum , ( int ) Sides . size () - 1 );
2012-11-18 13:09:13 +00:00
LOGINFO ( "Offending line: \" %s \" " , a_RecipeLine . c_str ());
2012-06-14 13:06:06 +00:00
return ;
}
std :: auto_ptr < cCraftingRecipes :: cRecipe > Recipe ( new cCraftingRecipes :: cRecipe );
// Parse the result:
AStringVector ResultSplit = StringSplit ( Sides [ 0 ], "," );
if ( ResultSplit . empty ())
{
LOGWARNING ( "crafting.txt: line %d: Result is empty, ignoring the recipe." , a_LineNum );
2012-11-18 13:09:13 +00:00
LOGINFO ( "Offending line: \" %s \" " , a_RecipeLine . c_str ());
2012-06-14 13:06:06 +00:00
return ;
}
if ( ! ParseItem ( ResultSplit [ 0 ], Recipe -> m_Result ))
{
LOGWARNING ( "crafting.txt: line %d: Cannot parse result item, ignoring the recipe." , a_LineNum );
2012-11-18 13:09:13 +00:00
LOGINFO ( "Offending line: \" %s \" " , a_RecipeLine . c_str ());
2012-06-14 13:06:06 +00:00
return ;
}
if ( ResultSplit . size () > 1 )
{
Recipe -> m_Result . m_ItemCount = atoi ( ResultSplit [ 1 ]. c_str ());
if ( Recipe -> m_Result . m_ItemCount == 0 )
{
LOGWARNING ( "crafting.txt: line %d: Cannot parse result count, ignoring the recipe." , a_LineNum );
2012-11-18 13:09:13 +00:00
LOGINFO ( "Offending line: \" %s \" " , a_RecipeLine . c_str ());
2012-06-14 13:06:06 +00:00
return ;
}
}
else
{
Recipe -> m_Result . m_ItemCount = 1 ;
}
// Parse each ingredient:
AStringVector Ingredients = StringSplit ( Sides [ 1 ], "|" );
int Num = 1 ;
for ( AStringVector :: const_iterator itr = Ingredients . begin (); itr != Ingredients . end (); ++ itr , ++ Num )
{
if ( ! ParseIngredient ( * itr , Recipe . get ()))
{
LOGWARNING ( "crafting.txt: line %d: Cannot parse ingredient #%d, ignoring the recipe." , a_LineNum , Num );
2012-11-18 13:09:13 +00:00
LOGINFO ( "Offending line: \" %s \" " , a_RecipeLine . c_str ());
2012-06-14 13:06:06 +00:00
return ;
}
} // for itr - Ingredients[]
NormalizeIngredients ( Recipe . get ());
m_Recipes . push_back ( Recipe . release ());
}
bool cCraftingRecipes :: ParseItem ( const AString & a_String , cItem & a_Item )
{
// The caller provides error logging
AStringVector Split = StringSplit ( a_String , "^" );
if ( Split . empty ())
{
return false ;
}
if ( ! StringToItem ( Split [ 0 ], a_Item ))
{
return false ;
}
if ( Split . size () > 1 )
{
AString Damage = TrimString ( Split [ 1 ]);
2013-01-12 04:46:01 +00:00
a_Item . m_ItemDamage = atoi ( Damage . c_str ());
if (( a_Item . m_ItemDamage == 0 ) && ( Damage . compare ( "0" ) != 0 ))
2012-06-14 13:06:06 +00:00
{
// Parsing the number failed
return false ;
}
}
// Success
return true ;
}
bool cCraftingRecipes :: ParseIngredient ( const AString & a_String , cRecipe * a_Recipe )
{
// a_String is in this format: "ItemType^damage, X:Y, X:Y, X:Y..."
AStringVector Split = StringSplit ( a_String , "," );
if ( Split . size () < 2 )
{
// Not enough split items
return false ;
}
cItem Item ;
if ( ! ParseItem ( Split [ 0 ], Item ))
{
return false ;
}
Item . m_ItemCount = 1 ;
cCraftingRecipes :: cRecipeSlots TempSlots ;
for ( AStringVector :: const_iterator itr = Split . begin () + 1 ; itr != Split . end (); ++ itr )
{
// Parse the coords in the split item:
AStringVector Coords = StringSplit ( * itr , ":" );
if (( Coords . size () == 1 ) && ( TrimString ( Coords [ 0 ]) == "*" ))
{
cCraftingRecipes :: cRecipeSlot Slot ;
Slot . m_Item = Item ;
Slot . x = - 1 ;
Slot . y = - 1 ;
TempSlots . push_back ( Slot );
continue ;
}
if ( Coords . size () != 2 )
{
return false ;
}
Coords [ 0 ] = TrimString ( Coords [ 0 ]);
Coords [ 1 ] = TrimString ( Coords [ 1 ]);
if ( Coords [ 0 ]. empty () || Coords [ 1 ]. empty ())
{
return false ;
}
cCraftingRecipes :: cRecipeSlot Slot ;
Slot . m_Item = Item ;
switch ( Coords [ 0 ][ 0 ])
{
case '1' : Slot . x = 0 ; break ;
case '2' : Slot . x = 1 ; break ;
case '3' : Slot . x = 2 ; break ;
case '*' : Slot . x = - 1 ; break ;
default :
{
return false ;
}
}
switch ( Coords [ 1 ][ 0 ])
{
case '1' : Slot . y = 0 ; break ;
case '2' : Slot . y = 1 ; break ;
case '3' : Slot . y = 2 ; break ;
case '*' : Slot . y = - 1 ; break ;
default :
{
return false ;
}
}
TempSlots . push_back ( Slot );
} // for itr - Split[]
// Append the ingredients:
a_Recipe -> m_Ingredients . insert ( a_Recipe -> m_Ingredients . end (), TempSlots . begin (), TempSlots . end ());
return true ;
}
void cCraftingRecipes :: NormalizeIngredients ( cCraftingRecipes :: cRecipe * a_Recipe )
{
// Calculate the minimum coords for ingredients, excluding the "anywhere" items:
int MinX = MAX_GRID_WIDTH , MaxX = 0 ;
int MinY = MAX_GRID_HEIGHT , MaxY = 0 ;
for ( cRecipeSlots :: const_iterator itr = a_Recipe -> m_Ingredients . begin (); itr != a_Recipe -> m_Ingredients . end (); ++ itr )
{
if ( itr -> x >= 0 )
{
MinX = std :: min ( itr -> x , MinX );
MaxX = std :: max ( itr -> x , MaxX );
}
if ( itr -> y >= 0 )
{
MinY = std :: min ( itr -> y , MinY );
MaxY = std :: max ( itr -> y , MaxY );
}
} // for itr - a_Recipe->m_Ingredients[]
// Move ingredients so that the minimum coords are 0:0
for ( cRecipeSlots :: iterator itr = a_Recipe -> m_Ingredients . begin (); itr != a_Recipe -> m_Ingredients . end (); ++ itr )
{
if ( itr -> x >= 0 )
{
itr -> x -= MinX ;
}
if ( itr -> y >= 0 )
{
itr -> y -= MinY ;
}
} // for itr - a_Recipe->m_Ingredients[]
a_Recipe -> m_Width = std :: max ( MaxX - MinX + 1 , 1 );
a_Recipe -> m_Height = std :: max ( MaxY - MinY + 1 , 1 );
// TODO: Compress two same ingredients with the same coords into a single ingredient with increased item count
}
cCraftingRecipes :: cRecipe * cCraftingRecipes :: FindRecipe ( const cItem * a_CraftingGrid , int a_GridWidth , int a_GridHeight )
{
ASSERT ( a_GridWidth <= MAX_GRID_WIDTH );
ASSERT ( a_GridHeight <= MAX_GRID_HEIGHT );
// Get the real bounds of the crafting grid:
int GridLeft = MAX_GRID_WIDTH , GridTop = MAX_GRID_HEIGHT ;
int GridRight = 0 , GridBottom = 0 ;
for ( int y = 0 ; y < a_GridHeight ; y ++ ) for ( int x = 0 ; x < a_GridWidth ; x ++ )
{
if ( ! a_CraftingGrid [ x + y * a_GridWidth ]. IsEmpty ())
{
2013-05-07 19:59:17 +00:00
GridRight = std :: max ( x , GridRight );
GridBottom = std :: max ( y , GridBottom );
GridLeft = std :: min ( x , GridLeft );
GridTop = std :: min ( y , GridTop );
2012-06-14 13:06:06 +00:00
}
}
int GridWidth = GridRight - GridLeft + 1 ;
int GridHeight = GridBottom - GridTop + 1 ;
// Search in the possibly minimized grid, but keep the stride:
const cItem * Grid = a_CraftingGrid + GridLeft + ( a_GridWidth * GridTop );
cRecipe * Recipe = FindRecipeCropped ( Grid , GridWidth , GridHeight , a_GridWidth );
if ( Recipe == NULL )
{
return NULL ;
}
// A recipe has been found, move it to correspond to the original crafting grid:
for ( cRecipeSlots :: iterator itrS = Recipe -> m_Ingredients . begin (); itrS != Recipe -> m_Ingredients . end (); ++ itrS )
{
itrS -> x += GridLeft ;
itrS -> y += GridTop ;
} // for itrS - Recipe->m_Ingredients[]
return Recipe ;
}
cCraftingRecipes :: cRecipe * cCraftingRecipes :: FindRecipeCropped ( const cItem * a_CraftingGrid , int a_GridWidth , int a_GridHeight , int a_GridStride )
{
for ( cRecipes :: const_iterator itr = m_Recipes . begin (); itr != m_Recipes . end (); ++ itr )
{
// Both the crafting grid and the recipes are normalized. The only variable possible is the "anywhere" items.
// This still means that the "anywhere" item may be the one that is offsetting the grid contents to the right or downwards, so we need to check all possible positions.
// E. g. recipe "A, * | B, 1:1 | ..." still needs to check grid for B at 2:2 (in case A was in grid's 1:1)
// Calculate the maximum offsets for this recipe relative to the grid size, and iterate through all combinations of offsets.
// Also, this calculation automatically filters out recipes that are too large for the current grid - the loop won't be entered at all.
int MaxOfsX = a_GridWidth - ( * itr ) -> m_Width ;
int MaxOfsY = a_GridHeight - ( * itr ) -> m_Height ;
for ( int x = 0 ; x <= MaxOfsX ; x ++ ) for ( int y = 0 ; y <= MaxOfsY ; y ++ )
{
cRecipe * Recipe = MatchRecipe ( a_CraftingGrid , a_GridWidth , a_GridHeight , a_GridStride , * itr , x , y );
if ( Recipe != NULL )
{
return Recipe ;
}
} // for y, for x
} // for itr - m_Recipes[]
// No matching recipe found
return NULL ;
}
cCraftingRecipes :: cRecipe * cCraftingRecipes :: MatchRecipe ( const cItem * a_CraftingGrid , int a_GridWidth , int a_GridHeight , int a_GridStride , const cRecipe * a_Recipe , int a_OffsetX , int a_OffsetY )
{
// Check the regular items first:
bool HasMatched [ MAX_GRID_WIDTH ][ MAX_GRID_HEIGHT ];
memset ( HasMatched , 0 , sizeof ( HasMatched ));
for ( cRecipeSlots :: const_iterator itrS = a_Recipe -> m_Ingredients . begin (); itrS != a_Recipe -> m_Ingredients . end (); ++ itrS )
{
if (( itrS -> x < 0 ) || ( itrS -> y < 0 ))
{
// "Anywhere" item, process later
continue ;
}
ASSERT ( itrS -> x + a_OffsetX < a_GridWidth );
ASSERT ( itrS -> y + a_OffsetY < a_GridHeight );
int GridID = ( itrS -> x + a_OffsetX ) + a_GridStride * ( itrS -> y + a_OffsetY );
if (
( itrS -> x >= a_GridWidth ) ||
( itrS -> y >= a_GridHeight ) ||
2013-01-12 04:46:01 +00:00
( itrS -> m_Item . m_ItemType != a_CraftingGrid [ GridID ]. m_ItemType ) || // same item type?
2012-06-14 13:06:06 +00:00
( itrS -> m_Item . m_ItemCount > a_CraftingGrid [ GridID ]. m_ItemCount ) || // not enough items
(
2013-01-12 04:46:01 +00:00
( itrS -> m_Item . m_ItemDamage > 0 ) && // should compare damage values?
( itrS -> m_Item . m_ItemDamage != a_CraftingGrid [ GridID ]. m_ItemDamage )
2012-06-14 13:06:06 +00:00
)
)
{
// Doesn't match
return NULL ;
}
HasMatched [ itrS -> x + a_OffsetX ][ itrS -> y + a_OffsetY ] = true ;
} // for itrS - Recipe->m_Ingredients[]
// Process the "Anywhere" items now, and only in the cells that haven't matched yet
// The "anywhere" items are processed on a first-come-first-served basis.
// Do not use a recipe with one horizontal and one vertical "anywhere" ("*:1, 1:*") as it may not match properly!
cRecipeSlots MatchedSlots ; // Stores the slots of "anywhere" items that have matched, with the match coords
for ( cRecipeSlots :: const_iterator itrS = a_Recipe -> m_Ingredients . begin (); itrS != a_Recipe -> m_Ingredients . end (); ++ itrS )
{
if (( itrS -> x >= 0 ) && ( itrS -> y >= 0 ))
{
// Regular item, already processed
continue ;
}
int StartX = 0 , EndX = a_GridWidth - 1 ;
int StartY = 0 , EndY = a_GridHeight - 1 ;
if ( itrS -> x >= 0 )
{
StartX = itrS -> x ;
EndX = itrS -> x ;
}
else if ( itrS -> y >= 0 )
{
StartY = itrS -> y ;
EndY = itrS -> y ;
}
bool Found = false ;
for ( int x = StartX ; x <= EndX ; x ++ )
{
for ( int y = StartY ; y <= EndY ; y ++ )
{
if ( HasMatched [ x ][ y ])
{
// Already matched some other item
continue ;
}
int GridIdx = x + a_GridStride * y ;
if (
2013-01-12 04:46:01 +00:00
( a_CraftingGrid [ GridIdx ]. m_ItemType == itrS -> m_Item . m_ItemType ) &&
2012-06-14 13:06:06 +00:00
(
2013-01-12 04:46:01 +00:00
( itrS -> m_Item . m_ItemDamage < 0 ) || // doesn't want damage comparison
( itrS -> m_Item . m_ItemDamage == a_CraftingGrid [ GridIdx ]. m_ItemDamage ) // the damage matches
2012-06-14 13:06:06 +00:00
)
)
{
HasMatched [ x ][ y ] = true ;
Found = true ;
MatchedSlots . push_back ( * itrS );
MatchedSlots . back (). x = x ;
MatchedSlots . back (). y = y ;
break ;
}
} // for y
if ( Found )
{
break ;
}
} // for x
if ( ! Found )
{
return NULL ;
}
} // for itrS - a_Recipe->m_Ingredients[]
// Check if the whole grid has matched:
for ( int x = 0 ; x < a_GridWidth ; x ++ ) for ( int y = 0 ; y < a_GridHeight ; y ++ )
{
if ( ! HasMatched [ x ][ y ] && ! a_CraftingGrid [ x + a_GridStride * y ]. IsEmpty ())
{
// There's an unmatched item in the grid
return NULL ;
}
} // for y, for x
// The recipe has matched. Create a copy of the recipe and set its coords to match the crafting grid:
std :: auto_ptr < cRecipe > Recipe ( new cRecipe );
Recipe -> m_Result = a_Recipe -> m_Result ;
Recipe -> m_Width = a_Recipe -> m_Width ;
Recipe -> m_Height = a_Recipe -> m_Height ;
for ( cRecipeSlots :: const_iterator itrS = a_Recipe -> m_Ingredients . begin (); itrS != a_Recipe -> m_Ingredients . end (); ++ itrS )
{
if (( itrS -> x < 0 ) || ( itrS -> y < 0 ))
{
// "Anywhere" item, process later
continue ;
}
Recipe -> m_Ingredients . push_back ( * itrS );
}
Recipe -> m_Ingredients . insert ( Recipe -> m_Ingredients . end (), MatchedSlots . begin (), MatchedSlots . end ());
return Recipe . release ();
}