Fix some issues with minetest.clear_craft (#8712)
[oweals/minetest.git] / src / craftdef.h
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
78         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
101         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         Note: replacements only work correctly when stack_max of the item
118         to be replaced is 1. It is up to the mod writer to ensure this.
119 */
120 struct CraftReplacements
121 {
122         // List of replacements
123         std::vector<std::pair<std::string, std::string> > pairs;
124
125         CraftReplacements() = default;
126         CraftReplacements(const std::vector<std::pair<std::string, std::string> > &pairs_):
127                 pairs(pairs_)
128         {}
129         std::string dump() const;
130 };
131
132 /*
133         Crafting definition base class
134 */
135 class CraftDefinition
136 {
137 public:
138         /*
139                 Craft recipe priorities, from low to high
140
141                 Recipes are searched from latest to first.
142                 If a recipe with higher priority than a previous found one is
143                 encountered, it is selected instead.
144         */
145         enum RecipePriority
146         {
147                 PRIORITY_NO_RECIPE,
148                 PRIORITY_TOOLREPAIR,
149                 PRIORITY_SHAPELESS_AND_GROUPS,
150                 PRIORITY_SHAPELESS,
151                 PRIORITY_SHAPED_AND_GROUPS,
152                 PRIORITY_SHAPED,
153         };
154
155         CraftDefinition() = default;
156         virtual ~CraftDefinition() = default;
157
158         // Returns type of crafting definition
159         virtual std::string getName() const=0;
160
161         // Checks whether the recipe is applicable
162         virtual bool check(const CraftInput &input, IGameDef *gamedef) const=0;
163         RecipePriority getPriority() const
164         {
165                 return priority;
166         }
167         // Returns the output structure, meaning depends on crafting method
168         // The implementation can assume that check(input) returns true
169         virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const=0;
170         // the inverse of the above
171         virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const=0;
172         // Decreases count of every input item
173         virtual void decrementInput(CraftInput &input,
174                 std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const=0;
175
176         CraftHashType getHashType() const
177         {
178                 return hash_type;
179         }
180         virtual u64 getHash(CraftHashType type) const = 0;
181
182         // to be called after all mods are loaded, so that we catch all aliases
183         virtual void initHash(IGameDef *gamedef) = 0;
184
185         virtual std::string dump() const=0;
186
187 protected:
188         CraftHashType hash_type;
189         RecipePriority priority;
190 };
191
192 /*
193         A plain-jane (shaped) crafting definition
194
195         Supported crafting method: CRAFT_METHOD_NORMAL.
196         Requires the input items to be arranged exactly like in the recipe.
197 */
198 class CraftDefinitionShaped: public CraftDefinition
199 {
200 public:
201         CraftDefinitionShaped() = delete;
202         CraftDefinitionShaped(
203                 const std::string &output_,
204                 unsigned int width_,
205                 const std::vector<std::string> &recipe_,
206                 const CraftReplacements &replacements_);
207
208         virtual ~CraftDefinitionShaped() = default;
209
210         virtual std::string getName() const;
211         virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
212         virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
213         virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
214         virtual void decrementInput(CraftInput &input,
215                 std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
216
217         virtual u64 getHash(CraftHashType type) const;
218
219         virtual void initHash(IGameDef *gamedef);
220
221         virtual std::string dump() const;
222
223 private:
224         // Output itemstring
225         std::string output = "";
226         // Width of recipe
227         unsigned int width = 1;
228         // Recipe matrix (itemstrings)
229         std::vector<std::string> recipe;
230         // Recipe matrix (item names)
231         std::vector<std::string> recipe_names;
232         // bool indicating if initHash has been called already
233         bool hash_inited = false;
234         // Replacement items for decrementInput()
235         CraftReplacements replacements;
236 };
237
238 /*
239         A shapeless crafting definition
240         Supported crafting method: CRAFT_METHOD_NORMAL.
241         Input items can arranged in any way.
242 */
243 class CraftDefinitionShapeless: public CraftDefinition
244 {
245 public:
246         CraftDefinitionShapeless() = delete;
247         CraftDefinitionShapeless(
248                 const std::string &output_,
249                 const std::vector<std::string> &recipe_,
250                 const CraftReplacements &replacements_);
251
252         virtual ~CraftDefinitionShapeless() = default;
253
254         virtual std::string getName() const;
255         virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
256         virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
257         virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
258         virtual void decrementInput(CraftInput &input,
259                 std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
260
261         virtual u64 getHash(CraftHashType type) const;
262
263         virtual void initHash(IGameDef *gamedef);
264
265         virtual std::string dump() const;
266
267 private:
268         // Output itemstring
269         std::string output;
270         // Recipe list (itemstrings)
271         std::vector<std::string> recipe;
272         // Recipe list (item names)
273         std::vector<std::string> recipe_names;
274         // bool indicating if initHash has been called already
275         bool hash_inited = false;
276         // Replacement items for decrementInput()
277         CraftReplacements replacements;
278 };
279
280 /*
281         Tool repair crafting definition
282         Supported crafting method: CRAFT_METHOD_NORMAL.
283         Put two damaged tools into the crafting grid, get one tool back.
284         There should only be one crafting definition of this type.
285 */
286 class CraftDefinitionToolRepair: public CraftDefinition
287 {
288 public:
289         CraftDefinitionToolRepair() = delete;
290         CraftDefinitionToolRepair(float additional_wear_);
291
292         virtual ~CraftDefinitionToolRepair() = default;
293
294         virtual std::string getName() const;
295         virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
296         virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
297         virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
298         virtual void decrementInput(CraftInput &input,
299                 std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
300
301         virtual u64 getHash(CraftHashType type) const { return 2; }
302
303         virtual void initHash(IGameDef *gamedef)
304         {
305                 hash_type = CRAFT_HASH_TYPE_COUNT;
306         }
307
308         virtual std::string dump() const;
309
310 private:
311         // This is a constant that is added to the wear of the result.
312         // May be positive or negative, allowed range [-1,1].
313         // 1 = new tool is completely broken
314         // 0 = simply add remaining uses of both input tools
315         // -1 = new tool is completely pristine
316         float additional_wear = 0.0f;
317 };
318
319 /*
320         A cooking (in furnace) definition
321         Supported crafting method: CRAFT_METHOD_COOKING.
322 */
323 class CraftDefinitionCooking: public CraftDefinition
324 {
325 public:
326         CraftDefinitionCooking() = delete;
327         CraftDefinitionCooking(
328                 const std::string &output_,
329                 const std::string &recipe_,
330                 float cooktime_,
331                 const CraftReplacements &replacements_);
332
333         virtual ~CraftDefinitionCooking() = default;
334
335         virtual std::string getName() const;
336         virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
337         virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
338         virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
339         virtual void decrementInput(CraftInput &input,
340                 std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
341
342         virtual u64 getHash(CraftHashType type) const;
343
344         virtual void initHash(IGameDef *gamedef);
345
346         virtual std::string dump() const;
347
348 private:
349         // Output itemstring
350         std::string output;
351         // Recipe itemstring
352         std::string recipe;
353         // Recipe item name
354         std::string recipe_name;
355         // bool indicating if initHash has been called already
356         bool hash_inited = false;
357         // Time in seconds
358         float cooktime;
359         // Replacement items for decrementInput()
360         CraftReplacements replacements;
361 };
362
363 /*
364         A fuel (for furnace) definition
365         Supported crafting method: CRAFT_METHOD_FUEL.
366 */
367 class CraftDefinitionFuel: public CraftDefinition
368 {
369 public:
370         CraftDefinitionFuel() = delete;
371         CraftDefinitionFuel(
372                 const std::string &recipe_,
373                 float burntime_,
374                 const CraftReplacements &replacements_);
375
376         virtual ~CraftDefinitionFuel() = default;
377
378         virtual std::string getName() const;
379         virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
380         virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
381         virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
382         virtual void decrementInput(CraftInput &input,
383                 std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
384
385         virtual u64 getHash(CraftHashType type) const;
386
387         virtual void initHash(IGameDef *gamedef);
388
389         virtual std::string dump() const;
390
391 private:
392         // Recipe itemstring
393         std::string recipe;
394         // Recipe item name
395         std::string recipe_name;
396         // bool indicating if initHash has been called already
397         bool hash_inited = false;
398         // Time in seconds
399         float burntime;
400         // Replacement items for decrementInput()
401         CraftReplacements replacements;
402 };
403
404 /*
405         Crafting definition manager
406 */
407 class ICraftDefManager
408 {
409 public:
410         ICraftDefManager() = default;
411         virtual ~ICraftDefManager() = default;
412
413         // The main crafting function
414         virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
415                         std::vector<ItemStack> &output_replacements,
416                         bool decrementInput, IGameDef *gamedef) const=0;
417         virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
418                         IGameDef *gamedef, unsigned limit=0) const=0;
419
420         // Print crafting recipes for debugging
421         virtual std::string dump() const=0;
422 };
423
424 class IWritableCraftDefManager : public ICraftDefManager
425 {
426 public:
427         IWritableCraftDefManager() = default;
428         virtual ~IWritableCraftDefManager() = default;
429
430         // The main crafting function
431         virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
432                         std::vector<ItemStack> &output_replacements,
433                         bool decrementInput, IGameDef *gamedef) const=0;
434         virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
435                         IGameDef *gamedef, unsigned limit=0) const=0;
436
437         virtual bool clearCraftsByOutput(const CraftOutput &output, IGameDef *gamedef) = 0;
438         virtual bool clearCraftsByInput(const CraftInput &input, IGameDef *gamedef) = 0;
439
440         // Print crafting recipes for debugging
441         virtual std::string dump() const=0;
442
443         // Add a crafting definition.
444         // After calling this, the pointer belongs to the manager.
445         virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef) = 0;
446
447         // Delete all crafting definitions
448         virtual void clear()=0;
449
450         // To be called after all mods are loaded, so that we catch all aliases
451         virtual void initHashes(IGameDef *gamedef) = 0;
452 };
453
454 IWritableCraftDefManager* createCraftDefManager();