1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14 
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 #pragma once
21 
22 #include <string>
23 #include <iostream>
24 #include <vector>
25 #include <utility>
26 #include "gamedef.h"
27 #include "inventory.h"
28 
29 /*
30 	Crafting methods.
31 
32 	The crafting method depends on the inventory list
33 	that the crafting input comes from.
34 */
35 enum CraftMethod
36 {
37 	// Crafting grid
38 	CRAFT_METHOD_NORMAL,
39 	// Cooking something in a furnace
40 	CRAFT_METHOD_COOKING,
41 	// Using something as fuel for a furnace
42 	CRAFT_METHOD_FUEL,
43 };
44 
45 /*
46 	The type a hash can be. The earlier a type is mentioned in this enum,
47 	the earlier it is tried at crafting, and the less likely is a collision.
48 	Changing order causes changes in behaviour, so know what you do.
49  */
50 enum CraftHashType
51 {
52 	// Hashes the normalized names of the recipe's elements.
53 	// Only recipes without group usage can be found here,
54 	// because groups can't be guessed efficiently.
55 	CRAFT_HASH_TYPE_ITEM_NAMES,
56 
57 	// Counts the non-empty slots.
58 	CRAFT_HASH_TYPE_COUNT,
59 
60 	// This layer both spares an extra variable, and helps to retain (albeit rarely used) functionality. Maps to 0.
61 	// Before hashes are "initialized", all hashes reside here, after initialisation, none are.
62 	CRAFT_HASH_TYPE_UNHASHED
63 
64 };
65 const int craft_hash_type_max = (int) CRAFT_HASH_TYPE_UNHASHED;
66 
67 /*
68 	Input: The contents of the crafting slots, arranged in matrix form
69 */
70 struct CraftInput
71 {
72 	CraftMethod method = CRAFT_METHOD_NORMAL;
73 	unsigned int width = 0;
74 	std::vector<ItemStack> items;
75 
76 	CraftInput() = default;
77 
CraftInputCraftInput78 	CraftInput(CraftMethod method_, unsigned int width_,
79 			const std::vector<ItemStack> &items_):
80 		method(method_), width(width_), items(items_)
81 	{}
82 
83 	// Returns true if all items are empty.
84 	bool empty() const;
85 
86 	std::string dump() const;
87 };
88 
89 /*
90 	Output: Result of crafting operation
91 */
92 struct CraftOutput
93 {
94 	// Used for normal crafting and cooking, itemstring
95 	std::string item = "";
96 	// Used for cooking (cook time) and fuel (burn time), seconds
97 	float time = 0.0f;
98 
99 	CraftOutput() = default;
100 
CraftOutputCraftOutput101 	CraftOutput(const std::string &item_, float time_):
102 		item(item_), time(time_)
103 	{}
104 	std::string dump() const;
105 };
106 
107 /*
108 	A list of replacements. A replacement indicates that a specific
109 	input item should not be deleted (when crafting) but replaced with
110 	a different item. Each replacements is a pair (itemstring to remove,
111 	itemstring to replace with)
112 
113 	Example: If ("bucket:bucket_water", "bucket:bucket_empty") is a
114 	replacement pair, the crafting input slot that contained a water
115 	bucket will contain an empty bucket after crafting.
116 */
117 struct CraftReplacements
118 {
119 	// List of replacements
120 	std::vector<std::pair<std::string, std::string> > pairs;
121 
122 	CraftReplacements() = default;
CraftReplacementsCraftReplacements123 	CraftReplacements(const std::vector<std::pair<std::string, std::string> > &pairs_):
124 		pairs(pairs_)
125 	{}
126 	std::string dump() const;
127 };
128 
129 /*
130 	Crafting definition base class
131 */
132 class CraftDefinition
133 {
134 public:
135 	/*
136 		Craft recipe priorities, from low to high
137 
138 		Recipes are searched from latest to first.
139 		If a recipe with higher priority than a previous found one is
140 		encountered, it is selected instead.
141 	*/
142 	enum RecipePriority
143 	{
144 		PRIORITY_NO_RECIPE,
145 		PRIORITY_TOOLREPAIR,
146 		PRIORITY_SHAPELESS_AND_GROUPS,
147 		PRIORITY_SHAPELESS,
148 		PRIORITY_SHAPED_AND_GROUPS,
149 		PRIORITY_SHAPED,
150 	};
151 
152 	CraftDefinition() = default;
153 	virtual ~CraftDefinition() = default;
154 
155 	// Returns type of crafting definition
156 	virtual std::string getName() const=0;
157 
158 	// Checks whether the recipe is applicable
159 	virtual bool check(const CraftInput &input, IGameDef *gamedef) const=0;
getPriority()160 	RecipePriority getPriority() const
161 	{
162 		return priority;
163 	}
164 	// Returns the output structure, meaning depends on crafting method
165 	// The implementation can assume that check(input) returns true
166 	virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const=0;
167 	// the inverse of the above
168 	virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const=0;
169 	// Decreases count of every input item
170 	virtual void decrementInput(CraftInput &input,
171 		std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const=0;
172 
getHashType()173 	CraftHashType getHashType() const
174 	{
175 		return hash_type;
176 	}
177 	virtual u64 getHash(CraftHashType type) const = 0;
178 
179 	// to be called after all mods are loaded, so that we catch all aliases
180 	virtual void initHash(IGameDef *gamedef) = 0;
181 
182 	virtual std::string dump() const=0;
183 
184 protected:
185 	CraftHashType hash_type;
186 	RecipePriority priority;
187 };
188 
189 /*
190 	A plain-jane (shaped) crafting definition
191 
192 	Supported crafting method: CRAFT_METHOD_NORMAL.
193 	Requires the input items to be arranged exactly like in the recipe.
194 */
195 class CraftDefinitionShaped: public CraftDefinition
196 {
197 public:
198 	CraftDefinitionShaped() = delete;
199 	CraftDefinitionShaped(
200 		const std::string &output_,
201 		unsigned int width_,
202 		const std::vector<std::string> &recipe_,
203 		const CraftReplacements &replacements_);
204 
205 	virtual ~CraftDefinitionShaped() = default;
206 
207 	virtual std::string getName() const;
208 	virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
209 	virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
210 	virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
211 	virtual void decrementInput(CraftInput &input,
212 		std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
213 
214 	virtual u64 getHash(CraftHashType type) const;
215 
216 	virtual void initHash(IGameDef *gamedef);
217 
218 	virtual std::string dump() const;
219 
220 private:
221 	// Output itemstring
222 	std::string output = "";
223 	// Width of recipe
224 	unsigned int width = 1;
225 	// Recipe matrix (itemstrings)
226 	std::vector<std::string> recipe;
227 	// Recipe matrix (item names)
228 	std::vector<std::string> recipe_names;
229 	// bool indicating if initHash has been called already
230 	bool hash_inited = false;
231 	// Replacement items for decrementInput()
232 	CraftReplacements replacements;
233 };
234 
235 /*
236 	A shapeless crafting definition
237 	Supported crafting method: CRAFT_METHOD_NORMAL.
238 	Input items can arranged in any way.
239 */
240 class CraftDefinitionShapeless: public CraftDefinition
241 {
242 public:
243 	CraftDefinitionShapeless() = delete;
244 	CraftDefinitionShapeless(
245 		const std::string &output_,
246 		const std::vector<std::string> &recipe_,
247 		const CraftReplacements &replacements_);
248 
249 	virtual ~CraftDefinitionShapeless() = default;
250 
251 	virtual std::string getName() const;
252 	virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
253 	virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
254 	virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
255 	virtual void decrementInput(CraftInput &input,
256 		std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
257 
258 	virtual u64 getHash(CraftHashType type) const;
259 
260 	virtual void initHash(IGameDef *gamedef);
261 
262 	virtual std::string dump() const;
263 
264 private:
265 	// Output itemstring
266 	std::string output;
267 	// Recipe list (itemstrings)
268 	std::vector<std::string> recipe;
269 	// Recipe list (item names)
270 	std::vector<std::string> recipe_names;
271 	// bool indicating if initHash has been called already
272 	bool hash_inited = false;
273 	// Replacement items for decrementInput()
274 	CraftReplacements replacements;
275 };
276 
277 /*
278 	Tool repair crafting definition
279 	Supported crafting method: CRAFT_METHOD_NORMAL.
280 	Put two damaged tools into the crafting grid, get one tool back.
281 	There should only be one crafting definition of this type.
282 */
283 class CraftDefinitionToolRepair: public CraftDefinition
284 {
285 public:
286 	CraftDefinitionToolRepair() = delete;
287 	CraftDefinitionToolRepair(float additional_wear_);
288 
289 	virtual ~CraftDefinitionToolRepair() = default;
290 
291 	virtual std::string getName() const;
292 	virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
293 	virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
294 	virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
295 	virtual void decrementInput(CraftInput &input,
296 		std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
297 
getHash(CraftHashType type)298 	virtual u64 getHash(CraftHashType type) const { return 2; }
299 
initHash(IGameDef * gamedef)300 	virtual void initHash(IGameDef *gamedef)
301 	{
302 		hash_type = CRAFT_HASH_TYPE_COUNT;
303 	}
304 
305 	virtual std::string dump() const;
306 
307 private:
308 	// This is a constant that is added to the wear of the result.
309 	// May be positive or negative, allowed range [-1,1].
310 	// 1 = new tool is completely broken
311 	// 0 = simply add remaining uses of both input tools
312 	// -1 = new tool is completely pristine
313 	float additional_wear = 0.0f;
314 };
315 
316 /*
317 	A cooking (in furnace) definition
318 	Supported crafting method: CRAFT_METHOD_COOKING.
319 */
320 class CraftDefinitionCooking: public CraftDefinition
321 {
322 public:
323 	CraftDefinitionCooking() = delete;
324 	CraftDefinitionCooking(
325 		const std::string &output_,
326 		const std::string &recipe_,
327 		float cooktime_,
328 		const CraftReplacements &replacements_);
329 
330 	virtual ~CraftDefinitionCooking() = default;
331 
332 	virtual std::string getName() const;
333 	virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
334 	virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
335 	virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
336 	virtual void decrementInput(CraftInput &input,
337 		std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
338 
339 	virtual u64 getHash(CraftHashType type) const;
340 
341 	virtual void initHash(IGameDef *gamedef);
342 
343 	virtual std::string dump() const;
344 
345 private:
346 	// Output itemstring
347 	std::string output;
348 	// Recipe itemstring
349 	std::string recipe;
350 	// Recipe item name
351 	std::string recipe_name;
352 	// bool indicating if initHash has been called already
353 	bool hash_inited = false;
354 	// Time in seconds
355 	float cooktime;
356 	// Replacement items for decrementInput()
357 	CraftReplacements replacements;
358 };
359 
360 /*
361 	A fuel (for furnace) definition
362 	Supported crafting method: CRAFT_METHOD_FUEL.
363 */
364 class CraftDefinitionFuel: public CraftDefinition
365 {
366 public:
367 	CraftDefinitionFuel() = delete;
368 	CraftDefinitionFuel(
369 		const std::string &recipe_,
370 		float burntime_,
371 		const CraftReplacements &replacements_);
372 
373 	virtual ~CraftDefinitionFuel() = default;
374 
375 	virtual std::string getName() const;
376 	virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
377 	virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
378 	virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
379 	virtual void decrementInput(CraftInput &input,
380 		std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
381 
382 	virtual u64 getHash(CraftHashType type) const;
383 
384 	virtual void initHash(IGameDef *gamedef);
385 
386 	virtual std::string dump() const;
387 
388 private:
389 	// Recipe itemstring
390 	std::string recipe;
391 	// Recipe item name
392 	std::string recipe_name;
393 	// bool indicating if initHash has been called already
394 	bool hash_inited = false;
395 	// Time in seconds
396 	float burntime;
397 	// Replacement items for decrementInput()
398 	CraftReplacements replacements;
399 };
400 
401 /*
402 	Crafting definition manager
403 */
404 class ICraftDefManager
405 {
406 public:
407 	ICraftDefManager() = default;
408 	virtual ~ICraftDefManager() = default;
409 
410 	/**
411 	 * The main crafting function.
412 	 *
413 	 * @param input The input grid.
414 	 * @param output CraftOutput where the result is placed.
415 	 * @param output_replacements A vector of ItemStacks where replacements are
416 	 * placed if they cannot be placed in the input. Replacements can be placed
417 	 * in the input if the stack of the replaced item has a count of 1.
418 	 * @param decrementInput If true, consume or replace input items.
419 	 * @param gamedef
420 	 * @return true if a result was found, otherwise false.
421 	 */
422 	virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
423 			std::vector<ItemStack> &output_replacements,
424 			bool decrementInput, IGameDef *gamedef) const=0;
425 
426 	virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
427 			IGameDef *gamedef, unsigned limit=0) const=0;
428 
429 	// Print crafting recipes for debugging
430 	virtual std::string dump() const=0;
431 };
432 
433 class IWritableCraftDefManager : public ICraftDefManager
434 {
435 public:
436 	IWritableCraftDefManager() = default;
437 	virtual ~IWritableCraftDefManager() = default;
438 
439 	// The main crafting function
440 	virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
441 			std::vector<ItemStack> &output_replacements,
442 			bool decrementInput, IGameDef *gamedef) const=0;
443 	virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
444 			IGameDef *gamedef, unsigned limit=0) const=0;
445 
446 	virtual bool clearCraftsByOutput(const CraftOutput &output, IGameDef *gamedef) = 0;
447 	virtual bool clearCraftsByInput(const CraftInput &input, IGameDef *gamedef) = 0;
448 
449 	// Print crafting recipes for debugging
450 	virtual std::string dump() const=0;
451 
452 	// Add a crafting definition.
453 	// After calling this, the pointer belongs to the manager.
454 	virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef) = 0;
455 
456 	// Delete all crafting definitions
457 	virtual void clear()=0;
458 
459 	// To be called after all mods are loaded, so that we catch all aliases
460 	virtual void initHashes(IGameDef *gamedef) = 0;
461 };
462 
463 IWritableCraftDefManager* createCraftDefManager();
464