summaryrefslogtreecommitdiffstats
path: root/src/CraftingRecipes.h
blob: 38936636be27e7f06da9b91639601deec9f12fc7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229

// CraftingRecipes.h

// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes




#pragma once

#include "Item.h"

// fwd: cPlayer.h
class cPlayer;





class cCraftingGrid  // tolua_export
{  // tolua_export
  public:
	cCraftingGrid(const cCraftingGrid & a_Original);
	cCraftingGrid(int a_Width, int a_Height);  // tolua_export
	cCraftingGrid(const cItem * a_Items, int a_Width, int a_Height);
	~cCraftingGrid();

	// tolua_begin
	int GetWidth(void) const { return m_Width; }
	int GetHeight(void) const { return m_Height; }
	cItem & GetItem(int x, int y) const;
	void SetItem(int x, int y, ENUM_ITEM_TYPE a_ItemType, char a_ItemCount, short a_ItemHealth);
	void SetItem(int x, int y, const cItem & a_Item);
	void Clear(void);

	/** Removes items in a_Grid from m_Items[] (used by cCraftingRecipe::ConsumeIngredients()) */
	void ConsumeGrid(const cCraftingGrid & a_Grid);

	/** Dumps the entire crafting grid using LOGD() */
	void Dump(void);

	// tolua_end

	cItem * GetItems(void) const { return m_Items; }

	/** Copies internal contents into the item array specified. Assumes that the array has the same dimensions as self
	 */
	void CopyToItems(cItem * a_Items) const;

  protected:
	int m_Width;
	int m_Height;
	cItem * m_Items;
};  // tolua_export





class cCraftingRecipe  // tolua_export
{  // tolua_export
  public:
	cCraftingRecipe(const cCraftingGrid & a_CraftingGrid);

	// tolua_begin
	void Clear(void);
	int GetIngredientsWidth(void) const { return m_Ingredients.GetWidth(); }
	int GetIngredientsHeight(void) const { return m_Ingredients.GetHeight(); }
	cItem & GetIngredient(int x, int y) const { return m_Ingredients.GetItem(x, y); }
	const cItem & GetResult(void) const { return m_Result; }
	void SetResult(ENUM_ITEM_TYPE a_ItemType, char a_ItemCount, short a_ItemHealth);
	void SetResult(const cItem & a_Item) { m_Result = a_Item; }

	void SetIngredient(int x, int y, ENUM_ITEM_TYPE a_ItemType, char a_ItemCount, short a_ItemHealth)
	{
		m_Ingredients.SetItem(x, y, a_ItemType, a_ItemCount, a_ItemHealth);
	}

	void SetIngredient(int x, int y, const cItem & a_Item) { m_Ingredients.SetItem(x, y, a_Item); }

	/** Consumes ingredients from the crafting grid specified */
	void ConsumeIngredients(cCraftingGrid & a_CraftingGrid);

	/** Dumps the entire recipe using LOGD() */
	void Dump(void);
	// tolua_end

  protected:
	cCraftingGrid m_Ingredients;  // Adjusted to correspond to the input crafting grid!
	cItem m_Result;
};  // tolua_export




/**
The crafting recipes are the configurations to build a result item out of a set
of ingredient items.

The recipes are configured in the `crafting.txt`. When populating the crafting
grid in game (inventory or crafting table), the items are compared to the
ingredients to find a matching recipe and show and craft the result.

Each recipe is defined via the result, the ingredients and the minecraft recipe
name.

To handle the crafting recipes internally efficient the vector index of the
`cRecipes` is used as `RecipeId`.
*/
class cCraftingRecipes
{
  public:
	static const int MAX_GRID_WIDTH = 3;
	static const int MAX_GRID_HEIGHT = 3;

	cCraftingRecipes(void);
	~cCraftingRecipes();

	/** Returns the recipe for current crafting grid. Doesn't modify the grid. Clears a_Recipe if no recipe found. */
	void GetRecipe(cPlayer & a_Player, cCraftingGrid & a_CraftingGrid, cCraftingRecipe & a_Recipe);

	/** Find recipes and returns the RecipeIds which contain the new item and all ingredients are in the known items */
	std::vector<UInt32> FindNewRecipesForItem(
		const cItem & a_Item,
		const std::set<cItem, cItem::sItemCompare> & a_KnownItems
	);

	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())
	A normalized recipe starts at (0, 0) */
	struct cRecipe
	{
		cRecipeSlots m_Ingredients;
		cItem m_Result;
		AString m_RecipeName;

		// Size of the regular items in the recipe; "anywhere" items are excluded:
		int m_Width;
		int m_Height;
	};

	/** Returns the recipe by id */
	cRecipe * GetRecipeById(UInt32 a_RecipeId);

	/** Gets a map of all recipes with name and recipe id */
	const std::map<AString, UInt32> & GetRecipeNameMap();

  protected:
	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 nullptr
	 * 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 nullptr 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
	);

	/** Searches for anything firework related, and does the data setting if appropriate */
	void HandleFireworks(
		const cItem * a_CraftingGrid,
		cCraftingRecipes::cRecipe * a_Recipe,
		int a_GridStride,
		int a_OffsetX,
		int a_OffsetY
	);

	/** Searches for anything dye related for leather, calculates the appropriate color value, and sets the resulting
	 * value. */
	void HandleDyedLeather(
		const cItem * a_CraftingGrid,
		cCraftingRecipes::cRecipe * a_Recipe,
		int a_GridStride,
		int a_GridWidth,
		int a_GridHeight
	);

  private:
	/** Mapping the minecraft recipe names to the internal cuberite recipe Ids */
	std::map<AString, UInt32> m_RecipeNameMap;

	/**
	Checks if all ingredients of the a_Recipe are within the a_KnownItems list and
	if the a_NewItem is part of the ingredients.
	This makes sure to only find 'newly discovered' recipes.
	*/
	bool IsNewCraftableRecipe(
		const cRecipe * a_Recipe,
		const cItem & a_NewItem,
		const std::set<cItem, cItem::sItemCompare> & a_KnownItems
	);

	/** Populates the RecipeNameMap */
	void PopulateRecipeNameMap(void);
};