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:
parent
ce5d97d65b
commit
b355bdecce
@ -2,6 +2,7 @@ MCServer.exe
|
||||
MCServer*debug.cmd
|
||||
Plugins
|
||||
webadmin
|
||||
crafting.ini
|
||||
banned.example.ini
|
||||
furnace.txt
|
||||
groups.example.ini
|
||||
|
@ -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"
|
||||
>
|
||||
|
@ -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>
|
@ -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
117
crafting.txt
Normal 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, *
|
92
items.ini
92
items.ini
@ -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
|
||||
|
26
recipes.txt
26
recipes.txt
@ -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
|
||||
#
|
||||
|
37842
source/Bindings.cpp
37842
source/Bindings.cpp
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
520
source/CraftingRecipes.cpp
Normal 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
82
source/CraftingRecipes.h
Normal 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);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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 )
|
||||
|
@ -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() );
|
||||
}
|
||||
|
||||
|
||||
|
@ -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...");
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 );
|
||||
|
Loading…
Reference in New Issue
Block a user