1
0
Fork 0

Added the new recipe parser, parsing the crafting.txt file. Included are a few recipes. The old parser still works, but will be replaced soon.

git-svn-id: http://mc-server.googlecode.com/svn/trunk@549 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
madmaxoft@gmail.com 2012-06-04 12:08:20 +00:00
parent ce5d97d65b
commit b355bdecce
21 changed files with 20029 additions and 18948 deletions

View File

@ -2,6 +2,7 @@ MCServer.exe
MCServer*debug.cmd
Plugins
webadmin
crafting.ini
banned.example.ini
furnace.txt
groups.example.ini

View File

@ -458,6 +458,14 @@
RelativePath="..\source\cPiston.h"
>
</File>
<File
RelativePath="..\source\CraftingRecipes.cpp"
>
</File>
<File
RelativePath="..\source\CraftingRecipes.h"
>
</File>
<File
RelativePath="..\source\cRecipeChecker.cpp"
>
@ -1383,10 +1391,10 @@
RelativePath="..\source\cStairs.h"
>
</File>
<File
RelativePath="..\source\cStep.h"
>
</File>
<File
RelativePath="..\source\cStep.h"
>
</File>
<File
RelativePath="..\source\cTorch.h"
>

View File

@ -356,6 +356,7 @@
<ClCompile Include="..\source\cPiston.cpp" />
<ClCompile Include="..\Source\cPluginManager.cpp" />
<ClCompile Include="..\source\cPlugin_NewLua.cpp" />
<ClCompile Include="..\source\CraftingRecipes.cpp" />
<ClCompile Include="..\Source\cRecipeChecker.cpp" />
<ClCompile Include="..\source\cRedstone.cpp" />
<ClCompile Include="..\source\cRedstoneSimulator.cpp" />
@ -537,6 +538,7 @@
<ClInclude Include="..\source\cPiston.h" />
<ClInclude Include="..\Source\cPluginManager.h" />
<ClInclude Include="..\source\cPlugin_NewLua.h" />
<ClInclude Include="..\source\CraftingRecipes.h" />
<ClInclude Include="..\Source\cRecipeChecker.h" />
<ClInclude Include="..\source\cRedstone.h" />
<ClInclude Include="..\source\cRedstoneSimulator.h" />
@ -705,4 +707,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -909,6 +909,7 @@
<ClCompile Include="..\source\FastNBT.cpp">
<Filter>Storage</Filter>
</ClCompile>
<ClCompile Include="..\source\CraftingRecipes.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\source\cServer.h">
@ -1417,6 +1418,8 @@
<ClInclude Include="..\source\FastNBT.h">
<Filter>Storage</Filter>
</ClInclude>
<ClInclude Include="..\source\cStep.h" />
<ClInclude Include="..\source\CraftingRecipes.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\source\AllToLua.pkg">

117
crafting.txt Normal file
View File

@ -0,0 +1,117 @@
# This file describes the crafting recipes that MCServer knows.
# The syntax is as follows:
# <Line> = <Recipe>#<Comment>
# <Recipe> = <Result> = <Ingredient1> | <Ingredient2> | ... | <IngredientN>
# <IngredientN> = <ItemID>, <X1> : <Y1>, <X2> : <Y2>, ..., <Xn> : <Yn>
# <ItemID> = <ItemType> [^<DamageValue>]
# <Xn>, <Yn> = "1" .. "3", or "*" for any value. "*:*" can be replaced by a single "*".
# <Result> = <ItemType> [^<DamageValue>] [, <Count>]
#
# The Xn, Yn coordinates are a reference to the crafting grid:
# 1:1 | 2:1 | 3:1
# 1:2 | 2:2 | 3:2
# 1:3 | 2:3 | 3:3
#
# <ItemType> can be either a number, or an item name (checked against items.ini)
#
# ^<DamageValue> is optional, if not present, any damage value is matched for ingredients and zero is produced for the result
#
# Ingredients with an asterisk for a coord will not match already matched crafting grid items. This enables simplifying some of the recipes,
# e. g. hoe: "Iron, 2:1, *:1"
# -- this means "one iron at 2:1, and another one at either 1:1 or 3:1"
#
# To require multiple items of the same type in a slot, specify the slot number several times:
# "Iron, 1:1 2:2, 2:2"
# -- this means "take one iron from slot 1:1 and two irons from slot 2:2"
# Note that asterisked items cannot require multiple items in a single slot.
#
# Note that due to technical problems, it is NOT advised to use asterisked ingredients in crossing directions, such as "*:1, "2:*".
# The parser may be unable to match such a recipe to the crafting grid!
#
# Whitespace is optional. Use it reasonably. Please do NOT use Tabs in the middle of lines!
#******************************************************#
# Basic Crafts
#
# Need to list each of the four log types, otherwise all logs would get converted into apple planks (^0)
Planks ^0, 4 = Log ^0, *
Planks ^1, 4 = Log ^1, *
Planks ^2, 4 = Log ^2, *
Planks ^3, 4 = Log ^3, *
Stick, 4 = Planks, 2:2, 2:3
Workbench = Planks, 1:1, 1:2, 2:1, 2:2
Chest = Planks, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3
Furnace = Cobblestone, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3
Torch, 4 = Stick, 1:2 | Coal, 1:1
#******************************************************#
# Food
#
Bread = Wheat, 1:1, 2:1, 3:1
Bowl = Planks, 1:1, 2:2, 3:1
MushroomStew = Bowl, * | BrownMushroom, * | RedMushroom, *
GoldenApple = RedApple, 2:2 | GoldNugget, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3
Cake = MilkBucket, 1:1, 2:1, 3:1 | Sugar, 1:2, 3:2 | Egg, 2:2 | Wheat, 1:3, 2:3, 3:3
Cookie = Wheat, *, * | CocoaBeans, *
#******************************************************#
# Hoe:
#
WoodenHoe = Stick, 2:2, 2:3 | Planks, 2:1, *:1
StoneHoe = Stick, 2:2, 2:3 | Cobblestone, 2:1, *:1
GoldenHoe = Stick, 2:2, 2:3 | GoldIngot, 2:1, *:1
IronHoe = Stick, 2:2, 2:3 | IronIngot, 2:1, *:1
DiamondHoe = Stick, 2:2, 2:3 | Diamond, 2:1, *:1
#******************************************************#
# Color mixing:
#
OrangeDye, 2 = YellowDye, * | RedDye, *
CyanDye, 2 = GreenDye, * | BlueDye, *
PurpleDye, 2 = RedDye, * | BlueDye, *
GrayDye, 2 = BlackDye, * | WhiteDye, *
LtBlueDye, 2 = BlueDye, * | WhiteDye, *
PinkDye, 2 = RedDye, * | WhiteDye, *
LimeDye, 2 = GreenDye, * | WhiteDye, *
MagentaDye, 2 = PurpleDye, * | PinkDye, *
LtGrayDye, 2 = GrayDye, * | WhiteDye, *
#******************************************************#
# Colored wool:
#
WhiteWool = Wool, * | BoneMeal, *
OrangeWool = Wool, * | OrangeDye, *
MagentaWool = Wool, * | MagentaDye, *
LightBlueWool = Wool, * | LightBlueDye, *
YellowWool = Wool, * | YellowDye, *
LimeWool = Wool, * | LimeDye, *
PinkWool = Wool, * | PinkDye, *
GrayWool = Wool, * | GrayDye, *
LightGrayWool = Wool, * | LightGrayDye, *
CyanWool = Wool, * | CyanDye, *
VioletWool = Wool, * | VioletDye, *
BlueWool = Wool, * | BlueDye, *
BrownWool = Wool, * | BrownDye, *
GreenWool = Wool, * | GreenDye, *
RedWool = Wool, * | RedDye, *
BlackWool = Wool, * | BlackDye, *

View File

@ -42,6 +42,36 @@ piston=33
pistonextension=34
cloth=35
wool=35
whitewool=35:0
orangewool=35:1
magentawool=35:2
lightbluewool=35:3
yellowwool=35:4
limewool=35:5
lightgreenwool=35:5
ltgreenwool=35:5
pinkwool=35:6
graywool=35:7
greywool=35:7
darkgraywool=35:7
darkgreywool=35:7
dkgraywool=35:7
dkgreywool=35:7
lightgraywool=35:8
lightgreywool=35:8
ltgraywool=35:8
ltgreywool=35:8
cyanwool=35:9
purplewool=35:10
violetwool=35:10
bluewool=35:11
darkbluewool=35:11
brownwool=35:12
greenwool=35:13
darkgreenwool=35:13
dkgreenwool=35:13
redwool=35:14
blackwool=35:15
flower=37
rose=38
brownmushroom=39
@ -156,18 +186,29 @@ ironaxe=258
flintandsteel=259
lighter=259
apple=260
redapple=260
bow=261
arrow=262
coal=263
diamond=264
ironingot=265
ironbar=265
goldingot=266
goldeningot=266
goldbar=266
goldenbar=266
ironsword=267
woodensword=268
woodsword=268
woodenshovel=269
woodshovel=269
woodenspade=269
woodspade=269
woodenpickaxe=270
woodpickaxe=270
woodenpick=270
woodpick=270
woodenaxe=271
woodaxe=271
stonesword=272
stoneshovel=273
@ -183,23 +224,32 @@ diamondpick=278
diamondaxe=279
stick=280
bowl=281
mushroomstew=282
bowlwithsoup=282
soupbowl=282
soup=282
goldensword=283
goldsword=283
goldenshovel=284
goldshovel=284
goldenspade=284
goldspade=284
goldenpickaxe=285
goldpickaxe=285
goldenpick=285
goldpick=285
goldenaxe=286
goldaxe=286
string=287
feather=288
gunpowder=289
woodhoe=290
woodenhoe=290
stonehoe=291
ironhoe=292
diamondhoe=293
goldhoe=294
goldenhoe=294
seeds=295
wheat=296
bread=297
@ -219,9 +269,13 @@ diamondhelmet=310
diamondchestplate=311
diamondpants=312
diamondboots=313
goldenhelmet=314
goldhelmet=314
goldenchestplate=315
goldchestplate=315
goldenpants=316
goldpants=316
goldenboots=317
goldboots=317
flint=318
meat=319
@ -231,8 +285,10 @@ cookedpork=320
painting=321
paintings=321
goldenapple=322
goldapple=322
sign=323
wooddoor=324
woodendoor=324
bucket=325
waterbucket=326
lavabucket=327
@ -247,6 +303,7 @@ milkbucket=335
brick=336
clay=337
reed=338
sugarcane=338
paper=339
book=340
slimeorb=341
@ -265,7 +322,42 @@ fish=349
cookedfish=350
dye=351
inksac=351:0
blackdye=351:0
reddye=351:1
rosered=351:1
greendye=351:2
cactusgreen=351:2
cocoabeans=351:3
browndye=351:3
lapislazuli=351:4
bluedye=351:4
darkbluedye=351:4
dkbluedye=351:4
purpledye=351:5
violetdye=351:5
cyandye=351:6
lightgreydye=351:7
lightgraydye=351:7
ltgreydye=351:7
ltgraydye=351:7
greydye=351:8
graydye=351:8
darkgreydye=351:8
darkgraydye=351:8
dkgreydye=351:8
dkgraydye=351:8
pinkdye=351:9
limedye=351:10
lightgreendye=351:10
ltgreendye=351:10
dandellionyellow=351:11
yellowdye=351:11
lightbluedye=351:12
ltbluedye=351:12
magentadye=351:13
orangedye=351:14
bonemeal=351:15
whitedye=351:15
bone=352
sugar=353
cake=354

View File

@ -86,32 +86,6 @@
#******************************************************#
# Basic Crafts
#
1x1,1:1:17:1@5:4 # -> 4 Planks
1x2,*:*:5:1@280:4 # -> 4 Sticks
2x2,*:*:5:1@58:1 # -> Crafting bench
3x3,2:2:5:-1@54:1 # -> Chest
3x3,2:2:4:-1@61:1 # -> Furnace
1x2,1:1:263:1,1:2:280:1@50:4 # -> Torch
#******************************************************#
# Food
#
3x1,*:3:296:1@297:1 # -> Bread
3x2,1:2:5:1,3:2:5:1,2:3:5:1@281:4 # -> Bowl
1x3,1:1:49:1,1:2:48:1,1:3:281:1@282:1 # -> Mushroom Stew
3x3,2:2:41:-1,2:2:260:1@322:1 # -> Golden Apple
#******************************************************#
# Utilities
#

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
/*
** Lua binding: AllToLua
** Generated automatically by tolua++-1.0.92 on Mon Jun 4 01:27:12 2012.
*/
/* Exported function */
TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S);
/*
** Lua binding: AllToLua
** Generated automatically by tolua++-1.0.92 on 06/04/12 10:48:42.
*/
/* Exported function */
TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S);

View File

@ -6,6 +6,7 @@
#include "Globals.h"
#include "BlockID.h"
#include "../iniFile/iniFile.h"
#include "cItem.h"
@ -35,6 +36,11 @@ public:
return m_Ini.GetValueI("Items", a_ItemName, -1);
}
AString ResolveString(AString & a_ItemName)
{
return m_Ini.GetValue("Items", a_ItemName, "");
}
protected:
cIniFile m_Ini;
} ;
@ -58,12 +64,47 @@ int BlockStringToType(const AString & a_BlockTypeString)
return res;
}
return gsBlockIDMap.Resolve(a_BlockTypeString);
return gsBlockIDMap.Resolve(TrimString(a_BlockTypeString));
}
bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item)
{
AString Resolved = TrimString(gsBlockIDMap.ResolveString(TrimString(a_ItemTypeString)));
AString txt = (!Resolved.empty()) ? Resolved : a_ItemTypeString;
AStringVector Split = StringSplit(txt, ":");
if (Split.size() == 1)
{
Split = StringSplit(txt, "^");
}
if (Split.empty())
{
return false;
}
a_Item.m_ItemID = (ENUM_ITEM_ID)atoi(Split[0].c_str());
if ((a_Item.m_ItemID == 0) && (Split[0] != "0"))
{
// Parsing the number failed
return false;
}
if (Split.size() > 1)
{
a_Item.m_ItemHealth = atoi(Split[1].c_str());
if ((a_Item.m_ItemHealth == 0) && (Split[1] != "0"))
{
// Parsing the number failed
return false;
}
}
return true;
}
EMCSBiome StringToBiome(const AString & a_BiomeString)
{
// If it is a number, return it:

View File

@ -145,7 +145,8 @@ enum ENUM_ITEM_ID
E_ITEM_GRASS = 2,
E_ITEM_DIRT = 3,
E_ITEM_COBBLESTONE = 4,
E_ITEM_WOOD = 5,
E_ITEM_PLANKS = 5,
E_ITEM_WOOD = 5, // obsolete, use E_ITEM_PLANKS instead
E_ITEM_SAPLING = 6,
E_ITEM_BEDROCK = 7,
E_ITEM_WATER = 8,
@ -443,8 +444,18 @@ enum
// fwd: cItem.h:
class cItem;
/// Translates a blocktype string into blocktype. Takes either a number or an items.ini alias as input. Returns -1 on failure.
extern int BlockStringToType(const AString & a_BlockTypeString);
extern int BlockStringToType(const AString & a_BlockTypeString); // tolua_export
/// Translates an itemtype string into an item. Takes either a number, number^number, number:number or an items.ini alias as input. Returns true if successful.
extern bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item); // tolua_export
/// Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns -1 on failure.
extern EMCSBiome StringToBiome(const AString & a_BiomeString);

520
source/CraftingRecipes.cpp Normal file
View File

@ -0,0 +1,520 @@
// CraftingRecipes.cpp
// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes
#include "Globals.h"
#include "CraftingRecipes.h"
cCraftingRecipes::cCraftingRecipes(void)
{
LoadRecipes();
}
cCraftingRecipes::~cCraftingRecipes()
{
ClearRecipes();
}
/// Offers an item resulting from the crafting grid setup. Doesn't modify the grid
cItem cCraftingRecipes::Offer(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight)
{
std::auto_ptr<cRecipe> Recipe(FindRecipe(a_CraftingGrid, a_GridWidth, a_GridHeight));
if (Recipe.get() == NULL)
{
return cItem();
}
return Recipe->m_Result;
}
/// Crafts the item resulting from the crafting grid setup. Modifies the grid, returns the crafted item
cItem cCraftingRecipes::Craft(cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight)
{
std::auto_ptr<cRecipe> Recipe(FindRecipe(a_CraftingGrid, a_GridWidth, a_GridHeight));
if (Recipe.get() == NULL)
{
return cItem();
}
// Consume the ingredients from the grid:
for (cRecipeSlots::const_iterator itr = Recipe->m_Ingredients.begin(); itr != Recipe->m_Ingredients.end(); ++itr)
{
int GridIdx = itr->x + a_GridWidth * itr->y;
a_CraftingGrid[GridIdx].m_ItemCount -= itr->m_Item.m_ItemCount;
if (a_CraftingGrid[GridIdx].m_ItemCount <= 0)
{
a_CraftingGrid[GridIdx].Empty();
}
}
return Recipe->m_Result;
}
void cCraftingRecipes::LoadRecipes(void)
{
LOG("-- Loading crafting recipes from crafting.txt --");
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[]
LOG("-- %d crafting recipes loaded from crafting.txt --", m_Recipes.size());
}
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);
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);
return;
}
if (!ParseItem(ResultSplit[0], Recipe->m_Result))
{
LOGWARNING("crafting.txt: line %d: Cannot parse result item, ignoring the recipe.", a_LineNum);
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);
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);
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]);
a_Item.m_ItemHealth = atoi(Damage.c_str());
if ((a_Item.m_ItemHealth == 0) && (Damage.compare("0") != 0))
{
// 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())
{
GridRight = MAX(x, GridRight);
GridBottom = MAX(y, GridBottom);
GridLeft = MIN(x, GridLeft);
GridTop = MIN(y, GridTop);
}
}
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) ||
(itrS->m_Item.m_ItemID != a_CraftingGrid[GridID].m_ItemID) || // same item type?
(itrS->m_Item.m_ItemCount > a_CraftingGrid[GridID].m_ItemCount) || // not enough items
(
(itrS->m_Item.m_ItemHealth > 0) && // should compare damage values?
(itrS->m_Item.m_ItemHealth != a_CraftingGrid[GridID].m_ItemHealth)
)
)
{
// 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 (
(a_CraftingGrid[GridIdx].m_ItemID == itrS->m_Item.m_ItemID) &&
(
(itrS->m_Item.m_ItemHealth < 0) || // doesn't want damage comparison
(itrS->m_Item.m_ItemHealth == a_CraftingGrid[GridIdx].m_ItemHealth) // the damage matches
)
)
{
HasMatched[x][y] = true;
Found = true;
MatchedSlots.push_back(*itrS);
MatchedSlots.back().x = x;
MatchedSlots.back().y = y;
break;
}
} // for y, for x - "anywhere"
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();
}

82
source/CraftingRecipes.h Normal file
View File

@ -0,0 +1,82 @@
// CraftingRecipes.h
// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes
#pragma once
#include "cItem.h"
class cCraftingRecipes
{
public:
static const int MAX_GRID_WIDTH = 3;
static const int MAX_GRID_HEIGHT = 3;
cCraftingRecipes(void);
~cCraftingRecipes();
/// Offers an item resulting from the crafting grid setup. Doesn't modify the grid
cItem Offer(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight);
/// Crafts the item resulting from the crafting grid setup. Modifies the grid, returns the crafted item
cItem Craft(cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight);
protected:
struct cRecipeSlot
{
cItem m_Item;
int x, y; // 1..3, or -1 for "any"
} ;
typedef std::vector<cRecipeSlot> cRecipeSlots;
// A single recipe, stored. Each recipe is normalized right after parsing (NormalizeIngredients())
struct cRecipe
{
cRecipeSlots m_Ingredients;
cItem m_Result;
// Size of the regular items in the recipe; "anywhere" items are excluded:
int m_Width;
int m_Height;
} ;
typedef std::vector<cRecipe *> cRecipes;
cRecipes m_Recipes;
void LoadRecipes(void);
void ClearRecipes(void);
/// Parses the recipe line and adds it into m_Recipes. a_LineNum is used for diagnostic warnings only
void AddRecipeLine(int a_LineNum, const AString & a_RecipeLine);
/// Parses an item string in the format "<ItemType>[^<Damage>]", returns true if successful.
bool ParseItem(const AString & a_String, cItem & a_Item);
/// Parses one ingredient and adds it to the specified recipe. Returns true if successful.
bool ParseIngredient(const AString & a_String, cRecipe * a_Recipe);
/// Moves the recipe to top-left corner, sets its MinWidth / MinHeight
void NormalizeIngredients(cRecipe * a_Recipe);
/// Finds a recipe matching the crafting grid. Returns a newly allocated recipe (with all its coords set) or NULL if not found. Caller must delete return value!
cRecipe * FindRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight);
/// Same as FindRecipe, but the grid is guaranteed to be of minimal dimensions needed
cRecipe * FindRecipeCropped(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride);
/// Checks if the grid matches the specified recipe, offset by the specified offsets. Returns a matched cRecipe * if so, or NULL if not matching. Caller must delete the return value!
cRecipe * MatchRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe * a_Recipe, int a_OffsetX, int a_OffsetY);
} ;

View File

@ -88,10 +88,7 @@ AStringVector StringSplit(const AString & str, const AString & delim)
size_t Prev = 0;
while ((cutAt = str.find_first_of(delim, Prev)) != str.npos)
{
if (cutAt > 0)
{
results.push_back(str.substr(Prev, cutAt - Prev));
}
results.push_back(str.substr(Prev, cutAt - Prev));
Prev = cutAt + delim.length();
}
if (Prev < str.length())
@ -104,6 +101,40 @@ AStringVector StringSplit(const AString & str, const AString & delim)
AString TrimString(const AString & str)
{
size_t len = str.length();
size_t start = 0;
while (start < len)
{
if (str[start] > 32)
{
break;
}
++start;
}
if (start == len)
{
return "";
}
size_t end = len;
while (end >= start)
{
if (str[end] > 32)
{
break;
}
--end;
}
return str.substr(start, end - start + 1);
}
AString & StrToUpper(AString & s)
{
AString::iterator i = s.begin();

View File

@ -33,6 +33,9 @@ extern AString & AppendPrintf (AString & str, const char * format, ...);
/// Split the string at delimiters, return as a stringvector
extern AStringVector StringSplit(const AString & str, const AString & delim);
/// Trime whitespace at both ends of the string
extern AString TrimString(const AString & str);
/// In-place string conversion to uppercase; returns the same string
extern AString & StrToUpper(AString & s);

View File

@ -4,6 +4,7 @@
#include "cCraftingWindow.h"
#include "cItem.h"
#include "cRecipeChecker.h"
#include "CraftingRecipes.h"
#include "cPlayer.h"
#include "cClientHandle.h"
#include "cInventory.h"
@ -13,6 +14,10 @@
#include "packets/cPacket_WindowClick.h"
#include "packets/cPacket_InventorySlot.h"
cCraftingWindow::cCraftingWindow( cWindowOwner* a_Owner, bool a_bInventoryVisible )
: cWindow( a_Owner, a_bInventoryVisible )
{
@ -23,6 +28,10 @@ cCraftingWindow::cCraftingWindow( cWindowOwner* a_Owner, bool a_bInventoryVisibl
SetSlots( Slots, 10 );
}
void cCraftingWindow::Clicked( cPacket_WindowClick* a_ClickPacket, cPlayer & a_Player )
{
bool bDontCook = false;
@ -59,25 +68,41 @@ void cCraftingWindow::Clicked( cPacket_WindowClick* a_ClickPacket, cPlayer & a_P
cWindow::Clicked( a_ClickPacket, a_Player );
}
if( a_ClickPacket->m_SlotNum >= 0 && a_ClickPacket->m_SlotNum < 10 )
if ((a_ClickPacket->m_SlotNum >= 0) && (a_ClickPacket->m_SlotNum < 10))
{
cItem CookedItem;
if( a_ClickPacket->m_SlotNum == 0 && !bDontCook )
if ((a_ClickPacket->m_SlotNum == 0) && !bDontCook)
{
CookedItem = cRoot::Get()->GetRecipeChecker()->CookIngredients( GetSlots()+1, 3, 3, true );
// Consume the ingredients from the crafting grid:
CookedItem = cRoot::Get()->GetCraftingRecipes()->Craft(GetSlots() + 1, 3, 3);
LOGD("New recipes crafted: %i x %i", CookedItem.m_ItemID, CookedItem.m_ItemCount);
// Upgrade the crafting result from the new crafting grid contents:
CookedItem = cRoot::Get()->GetCraftingRecipes()->Offer(GetSlots() + 1, 3, 3);
if (CookedItem.IsEmpty())
{
// Fallback to the old recipes:
CookedItem = cRoot::Get()->GetRecipeChecker()->CookIngredients( GetSlots()+1, 3, 3, true );
LOGD("Old recipes crafted: %i x %i", CookedItem.m_ItemID, CookedItem.m_ItemCount );
}
}
else
{
CookedItem = cRoot::Get()->GetRecipeChecker()->CookIngredients( GetSlots()+1, 3, 3 );
CookedItem = cRoot::Get()->GetCraftingRecipes()->Offer(GetSlots() + 1, 3, 3);
LOGD("New recipes offer: %i x %i", CookedItem.m_ItemID, CookedItem.m_ItemCount );
if (CookedItem.IsEmpty())
{
// Fallback to the old recipes
CookedItem = cRoot::Get()->GetRecipeChecker()->CookIngredients( GetSlots()+1, 3, 3 );
LOGD("Old recipes offer: %i x %i", CookedItem.m_ItemID, CookedItem.m_ItemCount );
}
}
*GetSlot(0) = CookedItem;
LOG("You cooked: %i x %i !!", GetSlot(0)->m_ItemID, GetSlot(0)->m_ItemCount );
}
SendWholeWindow( a_Player.GetClientHandle() );
a_Player.GetInventory().SendWholeInventory( a_Player.GetClientHandle() );
//Separate packet for result =/ Don't know why
// Separate packet for result =/ Don't know why
cPacket_InventorySlot Packet;
Packet.m_WindowID = (char)GetWindowID();
Packet.m_SlotNum = 0;
@ -85,9 +110,12 @@ void cCraftingWindow::Clicked( cPacket_WindowClick* a_ClickPacket, cPlayer & a_P
Packet.m_ItemCount = GetSlot(0)->m_ItemCount;
Packet.m_ItemUses = (char)GetSlot(0)->m_ItemHealth;
a_Player.GetClientHandle()->Send( Packet );
}
void cCraftingWindow::Close( cPlayer & a_Player )
{
// Start from slot 1, don't drop what's in the result slot
@ -105,4 +133,8 @@ void cCraftingWindow::Close( cPlayer & a_Player )
Item->Empty();
}
cWindow::Close( a_Player );
}
}

View File

@ -7,30 +7,51 @@
#include <fstream>
#include <sstream>
typedef std::list< cFurnaceRecipe::Recipe > RecipeList;
typedef std::list< cFurnaceRecipe::Fuel > FuelList;
struct cFurnaceRecipe::sFurnaceRecipeState
{
RecipeList Recipes;
FuelList Fuel;
};
cFurnaceRecipe::cFurnaceRecipe()
: m_pState( new sFurnaceRecipeState )
{
ReloadRecipes();
}
cFurnaceRecipe::~cFurnaceRecipe()
{
ClearRecipes();
delete m_pState;
}
void cFurnaceRecipe::ReloadRecipes()
{
ClearRecipes();
LOG("--Loading furnace recipes--");
LOG("-- Loading furnace recipes --");
std::ifstream f;
char a_File[] = "furnace.txt";
@ -152,9 +173,13 @@ void cFurnaceRecipe::ReloadRecipes()
}
LOG("Got %i furnace recipes, and %i fuels.", m_pState->Recipes.size(), m_pState->Fuel.size() );
LOG("--Done loading furnace recipes--");
LOG("-- Done loading furnace recipes --");
}
void cFurnaceRecipe::ClearRecipes()
{
for( RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr )

View File

@ -125,7 +125,7 @@ void PrintNear( std::ifstream & f, int a_History = 64 )
void cRecipeChecker::ReloadRecipes()
{
LOG("--Loading recipes--");
LOG("-- Loading recipes from recipes.txt --");
ClearRecipes();
std::ifstream f;
@ -369,7 +369,7 @@ void cRecipeChecker::ReloadRecipes()
}
f.close();
LOG("--Done loading recipes, found %i recipes", m_pState->Recipes.size() );
LOG("-- Done loading recipes, found %i recipes --", m_pState->Recipes.size() );
}

View File

@ -8,6 +8,7 @@
#include "cFurnaceRecipe.h"
#include "cGroupManager.h"
#include "cRecipeChecker.h"
#include "CraftingRecipes.h"
#include "cPluginManager.h"
#include "cMonsterConfig.h"
#include "cSleep.h"
@ -40,7 +41,8 @@ cRoot::cRoot()
: m_Server( 0 )
, m_MonsterConfig( 0 )
, m_GroupManager( 0 )
, m_RecipeChecker( 0 )
, m_RecipeChecker(NULL)
, m_CraftingRecipes(NULL)
, m_FurnaceRecipe( 0 )
, m_WebAdmin( 0 )
, m_PluginManager( 0 )
@ -85,11 +87,11 @@ void cRoot::InputThread(void* a_Params)
void cRoot::Start()
{
if( m_Log ) delete m_Log, m_Log = 0;
delete m_Log;
m_Log = new cMCLogger();
m_bStop = false;
while(!m_bStop)
while (!m_bStop)
{
m_bRestart = false;
@ -122,6 +124,7 @@ void cRoot::Start()
LOG("Loading settings...");
m_GroupManager = new cGroupManager();
m_RecipeChecker = new cRecipeChecker();
m_CraftingRecipes = new cCraftingRecipes;
m_FurnaceRecipe = new cFurnaceRecipe();
LOG("Loading worlds...");
@ -166,8 +169,9 @@ void cRoot::Start()
LOG("Stopping WebAdmin...");
delete m_WebAdmin; m_WebAdmin = 0;
LOG("Unloading recipes...");
delete m_FurnaceRecipe; m_FurnaceRecipe = 0;
delete m_RecipeChecker; m_RecipeChecker = 0;
delete m_FurnaceRecipe; m_FurnaceRecipe = NULL;
delete m_RecipeChecker; m_RecipeChecker = NULL;
delete m_CraftingRecipes; m_CraftingRecipes = NULL;
LOG("Forgetting groups...");
delete m_GroupManager; m_GroupManager = 0;
LOG("Unloading worlds...");

View File

@ -14,6 +14,7 @@ class cThread;
class cMonsterConfig;
class cGroupManager;
class cRecipeChecker;
class cCraftingRecipes;
class cFurnaceRecipe;
class cWebAdmin;
class cPluginManager;
@ -32,26 +33,27 @@ class cRoot //tolua_export
public:
static cRoot* Get() { return s_Root; } //tolua_export
cRoot();
cRoot(void);
~cRoot();
void Start();
void Start(void);
cServer* GetServer(void) { return m_Server; } //tolua_export
cWorld* GetDefaultWorld(void); //tolua_export
cWorld* GetWorld( const AString & a_WorldName ); //tolua_export
cServer * GetServer(void) { return m_Server; } //tolua_export
cWorld * GetDefaultWorld(void); //tolua_export
cWorld * GetWorld(const AString & a_WorldName); //tolua_export
/// Calls the callback for each world; returns true if the callback didn't abort (return true)
bool ForEachWorld(cWorldListCallback & a_Callback); // >> Exported in ManualBindings <<
cMonsterConfig *GetMonsterConfig() { return m_MonsterConfig;}
cMonsterConfig * GetMonsterConfig() { return m_MonsterConfig; }
cGroupManager * GetGroupManager (void) { return m_GroupManager; } // tolua_export
cRecipeChecker * GetRecipeChecker(void) { return m_RecipeChecker; } // tolua_export
cFurnaceRecipe * GetFurnaceRecipe(void) { return m_FurnaceRecipe; } // tolua_export
cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export
cPluginManager * GetPluginManager(void) { return m_PluginManager; } // tolua_export
cAuthenticator & GetAuthenticator(void) { return m_Authenticator; }
cGroupManager * GetGroupManager (void) { return m_GroupManager; } // tolua_export
cRecipeChecker * GetRecipeChecker (void) { return m_RecipeChecker; } // tolua_export
cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export
cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // tolua_export
cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export
cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export
cAuthenticator & GetAuthenticator (void) { return m_Authenticator; }
void ServerCommand(const char* a_Cmd ); //tolua_export
@ -78,12 +80,13 @@ private:
cServer * m_Server;
cMonsterConfig * m_MonsterConfig;
cGroupManager * m_GroupManager;
cRecipeChecker * m_RecipeChecker;
cFurnaceRecipe * m_FurnaceRecipe;
cWebAdmin * m_WebAdmin;
cPluginManager * m_PluginManager;
cAuthenticator m_Authenticator;
cGroupManager * m_GroupManager;
cRecipeChecker * m_RecipeChecker;
cCraftingRecipes * m_CraftingRecipes;
cFurnaceRecipe * m_FurnaceRecipe;
cWebAdmin * m_WebAdmin;
cPluginManager * m_PluginManager;
cAuthenticator m_Authenticator;
cMCLogger * m_Log;

View File

@ -7,6 +7,7 @@
#include "cWindow.h"
#include "cItem.h"
#include "cRecipeChecker.h"
#include "CraftingRecipes.h"
#include "cRoot.h"
#include "packets/cPacket_WindowClick.h"
@ -69,16 +70,29 @@ void cSurvivalInventory::Clicked( cPacket* a_ClickPacket )
LOG("No Inventory window! WTF");
}
if( Packet->m_SlotNum >= (short)c_CraftOffset && Packet->m_SlotNum < (short)(c_CraftOffset+c_CraftSlots+1) )
if ((Packet->m_SlotNum >= (short)c_CraftOffset) && (Packet->m_SlotNum < (short)(c_CraftOffset + c_CraftSlots + 1)))
{
cItem CookedItem;
if( Packet->m_SlotNum == 0 && !bDontCook )
if ((Packet->m_SlotNum == 0) && !bDontCook)
{
CookedItem = cRoot::Get()->GetRecipeChecker()->CookIngredients( m_Slots+c_CraftOffset+1, 2, 2, true );
// Consume the items from the crafting grid:
CookedItem = cRoot::Get()->GetCraftingRecipes()->Craft(m_Slots + c_CraftOffset + 1, 2, 2);
// Upgrade the crafting result from the new crafting grid contents:
CookedItem = cRoot::Get()->GetCraftingRecipes()->Offer(m_Slots + c_CraftOffset + 1, 2, 2);
if (CookedItem.IsEmpty())
{
// Fallback to the old recipes:
CookedItem = cRoot::Get()->GetRecipeChecker()->CookIngredients( m_Slots+c_CraftOffset+1, 2, 2, true );
}
}
else
{
CookedItem = cRoot::Get()->GetRecipeChecker()->CookIngredients( m_Slots+c_CraftOffset+1, 2, 2 );
CookedItem = cRoot::Get()->GetCraftingRecipes()->Offer(m_Slots + c_CraftOffset + 1, 2, 2);
if (CookedItem.IsEmpty())
{
// Fallback to the old recipes:
CookedItem = cRoot::Get()->GetRecipeChecker()->CookIngredients( m_Slots+c_CraftOffset+1, 2, 2 );
}
}
m_Slots[c_CraftOffset] = CookedItem;
LOG("You cooked: %i x %i !!", m_Slots[c_CraftOffset].m_ItemID, m_Slots[c_CraftOffset].m_ItemCount );