Prioritise craft recipes
[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         std::string dump() const;
84 };
85
86 /*
87         Output: Result of crafting operation
88 */
89 struct CraftOutput
90 {
91         // Used for normal crafting and cooking, itemstring
92         std::string item = "";
93         // Used for cooking (cook time) and fuel (burn time), seconds
94         float time = 0.0f;
95
96         CraftOutput() = default;
97
98         CraftOutput(const std::string &item_, float time_):
99                 item(item_), time(time_)
100         {}
101         std::string dump() const;
102 };
103
104 /*
105         A list of replacements. A replacement indicates that a specific
106         input item should not be deleted (when crafting) but replaced with
107         a different item. Each replacements is a pair (itemstring to remove,
108         itemstring to replace with)
109
110         Example: If ("bucket:bucket_water", "bucket:bucket_empty") is a
111         replacement pair, the crafting input slot that contained a water
112         bucket will contain an empty bucket after crafting.
113
114         Note: replacements only work correctly when stack_max of the item
115         to be replaced is 1. It is up to the mod writer to ensure this.
116 */
117 struct CraftReplacements
118 {
119         // List of replacements
120         std::vector<std::pair<std::string, std::string> > pairs;
121
122         CraftReplacements() = default;
123         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                 NO_RECIPE,
145                 TOOLREPAIR,
146                 SHAPELESS_AND_GROUPS,
147                 SHAPELESS,
148                 SHAPED_AND_GROUPS,
149                 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;
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
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
200         CraftDefinitionShaped(
201                         const std::string &output_,
202                         unsigned int width_,
203                         const std::vector<std::string> &recipe_,
204                         const CraftReplacements &replacements_):
205                 output(output_), width(width_), recipe(recipe_),
206                 replacements(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                 output(output_), recipe(recipe_), replacements(replacements_)
252         {}
253         virtual ~CraftDefinitionShapeless() = default;
254
255         virtual std::string getName() const;
256         virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
257         virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
258         virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
259         virtual void decrementInput(CraftInput &input,
260                 std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
261
262         virtual u64 getHash(CraftHashType type) const;
263
264         virtual void initHash(IGameDef *gamedef);
265
266         virtual std::string dump() const;
267
268 private:
269         // Output itemstring
270         std::string output;
271         // Recipe list (itemstrings)
272         std::vector<std::string> recipe;
273         // Recipe list (item names)
274         std::vector<std::string> recipe_names;
275         // bool indicating if initHash has been called already
276         bool hash_inited = false;
277         // Replacement items for decrementInput()
278         CraftReplacements replacements;
279 };
280
281 /*
282         Tool repair crafting definition
283         Supported crafting method: CRAFT_METHOD_NORMAL.
284         Put two damaged tools into the crafting grid, get one tool back.
285         There should only be one crafting definition of this type.
286 */
287 class CraftDefinitionToolRepair: public CraftDefinition
288 {
289 public:
290         CraftDefinitionToolRepair() = delete;
291         CraftDefinitionToolRepair(float additional_wear_):
292                 additional_wear(additional_wear_)
293         {}
294         virtual ~CraftDefinitionToolRepair() = default;
295
296         virtual std::string getName() const;
297         virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
298         virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
299         virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
300         virtual void decrementInput(CraftInput &input,
301                 std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
302
303         virtual u64 getHash(CraftHashType type) const { return 2; }
304
305         virtual void initHash(IGameDef *gamedef)
306         {
307                 hash_type = CRAFT_HASH_TYPE_COUNT;
308                 priority = TOOLREPAIR;
309         }
310
311         virtual std::string dump() const;
312
313 private:
314         // This is a constant that is added to the wear of the result.
315         // May be positive or negative, allowed range [-1,1].
316         // 1 = new tool is completely broken
317         // 0 = simply add remaining uses of both input tools
318         // -1 = new tool is completely pristine
319         float additional_wear = 0.0f;
320 };
321
322 /*
323         A cooking (in furnace) definition
324         Supported crafting method: CRAFT_METHOD_COOKING.
325 */
326 class CraftDefinitionCooking: public CraftDefinition
327 {
328 public:
329         CraftDefinitionCooking() = delete;
330         CraftDefinitionCooking(
331                         const std::string &output_,
332                         const std::string &recipe_,
333                         float cooktime_,
334                         const CraftReplacements &replacements_):
335                 output(output_), recipe(recipe_), cooktime(cooktime_), replacements(replacements_)
336         {}
337         virtual ~CraftDefinitionCooking() = default;
338
339         virtual std::string getName() const;
340         virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
341         virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
342         virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
343         virtual void decrementInput(CraftInput &input,
344                 std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
345
346         virtual u64 getHash(CraftHashType type) const;
347
348         virtual void initHash(IGameDef *gamedef);
349
350         virtual std::string dump() const;
351
352 private:
353         // Output itemstring
354         std::string output;
355         // Recipe itemstring
356         std::string recipe;
357         // Recipe item name
358         std::string recipe_name;
359         // bool indicating if initHash has been called already
360         bool hash_inited = false;
361         // Time in seconds
362         float cooktime;
363         // Replacement items for decrementInput()
364         CraftReplacements replacements;
365 };
366
367 /*
368         A fuel (for furnace) definition
369         Supported crafting method: CRAFT_METHOD_FUEL.
370 */
371 class CraftDefinitionFuel: public CraftDefinition
372 {
373 public:
374         CraftDefinitionFuel() = delete;
375         CraftDefinitionFuel(const std::string &recipe_,
376                         float burntime_,
377                         const CraftReplacements &replacements_):
378                 recipe(recipe_), burntime(burntime_), replacements(replacements_)
379         {}
380         virtual ~CraftDefinitionFuel() = default;
381
382         virtual std::string getName() const;
383         virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
384         virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
385         virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
386         virtual void decrementInput(CraftInput &input,
387                 std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
388
389         virtual u64 getHash(CraftHashType type) const;
390
391         virtual void initHash(IGameDef *gamedef);
392
393         virtual std::string dump() const;
394
395 private:
396         // Recipe itemstring
397         std::string recipe;
398         // Recipe item name
399         std::string recipe_name;
400         // bool indicating if initHash has been called already
401         bool hash_inited = false;
402         // Time in seconds
403         float burntime;
404         // Replacement items for decrementInput()
405         CraftReplacements replacements;
406 };
407
408 /*
409         Crafting definition manager
410 */
411 class ICraftDefManager
412 {
413 public:
414         ICraftDefManager() = default;
415         virtual ~ICraftDefManager() = default;
416
417         // The main crafting function
418         virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
419                         std::vector<ItemStack> &output_replacements,
420                         bool decrementInput, IGameDef *gamedef) const=0;
421         virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
422                         IGameDef *gamedef, unsigned limit=0) const=0;
423
424         // Print crafting recipes for debugging
425         virtual std::string dump() const=0;
426 };
427
428 class IWritableCraftDefManager : public ICraftDefManager
429 {
430 public:
431         IWritableCraftDefManager() = default;
432         virtual ~IWritableCraftDefManager() = default;
433
434         // The main crafting function
435         virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
436                         std::vector<ItemStack> &output_replacements,
437                         bool decrementInput, IGameDef *gamedef) const=0;
438         virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
439                         IGameDef *gamedef, unsigned limit=0) const=0;
440
441         virtual bool clearCraftRecipesByOutput(const CraftOutput &output, IGameDef *gamedef) = 0;
442         virtual bool clearCraftRecipesByInput(CraftMethod craft_method,
443                         unsigned int craft_grid_width, const std::vector<std::string> &recipe, IGameDef *gamedef) = 0;
444
445         // Print crafting recipes for debugging
446         virtual std::string dump() const=0;
447
448         // Add a crafting definition.
449         // After calling this, the pointer belongs to the manager.
450         virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef) = 0;
451
452         // Delete all crafting definitions
453         virtual void clear()=0;
454
455         // To be called after all mods are loaded, so that we catch all aliases
456         virtual void initHashes(IGameDef *gamedef) = 0;
457 };
458
459 IWritableCraftDefManager* createCraftDefManager();