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