]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/lua_api/l_craft.cpp
Fix various points reported by cppcheck (#5656)
[dragonfireclient.git] / src / script / lua_api / l_craft.cpp
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
21 #include "lua_api/l_craft.h"
22 #include "lua_api/l_internal.h"
23 #include "lua_api/l_item.h"
24 #include "common/c_converter.h"
25 #include "common/c_content.h"
26 #include "server.h"
27 #include "craftdef.h"
28
29 struct EnumString ModApiCraft::es_CraftMethod[] =
30 {
31         {CRAFT_METHOD_NORMAL, "normal"},
32         {CRAFT_METHOD_COOKING, "cooking"},
33         {CRAFT_METHOD_FUEL, "fuel"},
34         {0, NULL},
35 };
36
37 // helper for register_craft
38 bool ModApiCraft::readCraftRecipeShaped(lua_State *L, int index,
39                 int &width, std::vector<std::string> &recipe)
40 {
41         if(index < 0)
42                 index = lua_gettop(L) + 1 + index;
43
44         if(!lua_istable(L, index))
45                 return false;
46
47         lua_pushnil(L);
48         int rowcount = 0;
49         while(lua_next(L, index) != 0){
50                 int colcount = 0;
51                 // key at index -2 and value at index -1
52                 if(!lua_istable(L, -1))
53                         return false;
54                 int table2 = lua_gettop(L);
55                 lua_pushnil(L);
56                 while(lua_next(L, table2) != 0){
57                         // key at index -2 and value at index -1
58                         if(!lua_isstring(L, -1))
59                                 return false;
60                         recipe.push_back(lua_tostring(L, -1));
61                         // removes value, keeps key for next iteration
62                         lua_pop(L, 1);
63                         colcount++;
64                 }
65                 if(rowcount == 0){
66                         width = colcount;
67                 } else {
68                         if(colcount != width)
69                                 return false;
70                 }
71                 // removes value, keeps key for next iteration
72                 lua_pop(L, 1);
73                 rowcount++;
74         }
75         return width != 0;
76 }
77
78 // helper for register_craft
79 bool ModApiCraft::readCraftRecipeShapeless(lua_State *L, int index,
80                 std::vector<std::string> &recipe)
81 {
82         if(index < 0)
83                 index = lua_gettop(L) + 1 + index;
84
85         if(!lua_istable(L, index))
86                 return false;
87
88         lua_pushnil(L);
89         while(lua_next(L, index) != 0){
90                 // key at index -2 and value at index -1
91                 if(!lua_isstring(L, -1))
92                         return false;
93                 recipe.push_back(lua_tostring(L, -1));
94                 // removes value, keeps key for next iteration
95                 lua_pop(L, 1);
96         }
97         return true;
98 }
99
100 // helper for register_craft
101 bool ModApiCraft::readCraftReplacements(lua_State *L, int index,
102                 CraftReplacements &replacements)
103 {
104         if(index < 0)
105                 index = lua_gettop(L) + 1 + index;
106
107         if(!lua_istable(L, index))
108                 return false;
109
110         lua_pushnil(L);
111         while(lua_next(L, index) != 0){
112                 // key at index -2 and value at index -1
113                 if(!lua_istable(L, -1))
114                         return false;
115                 lua_rawgeti(L, -1, 1);
116                 if(!lua_isstring(L, -1))
117                         return false;
118                 std::string replace_from = lua_tostring(L, -1);
119                 lua_pop(L, 1);
120                 lua_rawgeti(L, -1, 2);
121                 if(!lua_isstring(L, -1))
122                         return false;
123                 std::string replace_to = lua_tostring(L, -1);
124                 lua_pop(L, 1);
125                 replacements.pairs.push_back(
126                                 std::make_pair(replace_from, replace_to));
127                 // removes value, keeps key for next iteration
128                 lua_pop(L, 1);
129         }
130         return true;
131 }
132 // register_craft({output=item, recipe={{item00,item10},{item01,item11}})
133 int ModApiCraft::l_register_craft(lua_State *L)
134 {
135         NO_MAP_LOCK_REQUIRED;
136         //infostream<<"register_craft"<<std::endl;
137         luaL_checktype(L, 1, LUA_TTABLE);
138         int table = 1;
139
140         // Get the writable craft definition manager from the server
141         IWritableCraftDefManager *craftdef =
142                         getServer(L)->getWritableCraftDefManager();
143
144         std::string type = getstringfield_default(L, table, "type", "shaped");
145
146         /*
147                 CraftDefinitionShaped
148         */
149         if(type == "shaped"){
150                 std::string output = getstringfield_default(L, table, "output", "");
151                 if(output == "")
152                         throw LuaError("Crafting definition is missing an output");
153
154                 int width = 0;
155                 std::vector<std::string> recipe;
156                 lua_getfield(L, table, "recipe");
157                 if(lua_isnil(L, -1))
158                         throw LuaError("Crafting definition is missing a recipe"
159                                         " (output=\"" + output + "\")");
160                 if(!readCraftRecipeShaped(L, -1, width, recipe))
161                         throw LuaError("Invalid crafting recipe"
162                                         " (output=\"" + output + "\")");
163
164                 CraftReplacements replacements;
165                 lua_getfield(L, table, "replacements");
166                 if(!lua_isnil(L, -1))
167                 {
168                         if(!readCraftReplacements(L, -1, replacements))
169                                 throw LuaError("Invalid replacements"
170                                                 " (output=\"" + output + "\")");
171                 }
172
173                 CraftDefinition *def = new CraftDefinitionShaped(
174                                 output, width, recipe, replacements);
175                 craftdef->registerCraft(def, getServer(L));
176         }
177         /*
178                 CraftDefinitionShapeless
179         */
180         else if(type == "shapeless"){
181                 std::string output = getstringfield_default(L, table, "output", "");
182                 if(output == "")
183                         throw LuaError("Crafting definition (shapeless)"
184                                         " is missing an output");
185
186                 std::vector<std::string> recipe;
187                 lua_getfield(L, table, "recipe");
188                 if(lua_isnil(L, -1))
189                         throw LuaError("Crafting definition (shapeless)"
190                                         " is missing a recipe"
191                                         " (output=\"" + output + "\")");
192                 if(!readCraftRecipeShapeless(L, -1, recipe))
193                         throw LuaError("Invalid crafting recipe"
194                                         " (output=\"" + output + "\")");
195
196                 CraftReplacements replacements;
197                 lua_getfield(L, table, "replacements");
198                 if(!lua_isnil(L, -1))
199                 {
200                         if(!readCraftReplacements(L, -1, replacements))
201                                 throw LuaError("Invalid replacements"
202                                                 " (output=\"" + output + "\")");
203                 }
204
205                 CraftDefinition *def = new CraftDefinitionShapeless(
206                                 output, recipe, replacements);
207                 craftdef->registerCraft(def, getServer(L));
208         }
209         /*
210                 CraftDefinitionToolRepair
211         */
212         else if(type == "toolrepair"){
213                 float additional_wear = getfloatfield_default(L, table,
214                                 "additional_wear", 0.0);
215
216                 CraftDefinition *def = new CraftDefinitionToolRepair(
217                                 additional_wear);
218                 craftdef->registerCraft(def, getServer(L));
219         }
220         /*
221                 CraftDefinitionCooking
222         */
223         else if(type == "cooking"){
224                 std::string output = getstringfield_default(L, table, "output", "");
225                 if(output == "")
226                         throw LuaError("Crafting definition (cooking)"
227                                         " is missing an output");
228
229                 std::string recipe = getstringfield_default(L, table, "recipe", "");
230                 if(recipe == "")
231                         throw LuaError("Crafting definition (cooking)"
232                                         " is missing a recipe"
233                                         " (output=\"" + output + "\")");
234
235                 float cooktime = getfloatfield_default(L, table, "cooktime", 3.0);
236
237                 CraftReplacements replacements;
238                 lua_getfield(L, table, "replacements");
239                 if(!lua_isnil(L, -1))
240                 {
241                         if(!readCraftReplacements(L, -1, replacements))
242                                 throw LuaError("Invalid replacements"
243                                                 " (cooking output=\"" + output + "\")");
244                 }
245
246                 CraftDefinition *def = new CraftDefinitionCooking(
247                                 output, recipe, cooktime, replacements);
248                 craftdef->registerCraft(def, getServer(L));
249         }
250         /*
251                 CraftDefinitionFuel
252         */
253         else if(type == "fuel"){
254                 std::string recipe = getstringfield_default(L, table, "recipe", "");
255                 if(recipe == "")
256                         throw LuaError("Crafting definition (fuel)"
257                                         " is missing a recipe");
258
259                 float burntime = getfloatfield_default(L, table, "burntime", 1.0);
260
261                 CraftReplacements replacements;
262                 lua_getfield(L, table, "replacements");
263                 if(!lua_isnil(L, -1))
264                 {
265                         if(!readCraftReplacements(L, -1, replacements))
266                                 throw LuaError("Invalid replacements"
267                                                 " (fuel recipe=\"" + recipe + "\")");
268                 }
269
270                 CraftDefinition *def = new CraftDefinitionFuel(
271                                 recipe, burntime, replacements);
272                 craftdef->registerCraft(def, getServer(L));
273         }
274         else
275         {
276                 throw LuaError("Unknown crafting definition type: \"" + type + "\"");
277         }
278
279         lua_pop(L, 1);
280         return 0; /* number of results */
281 }
282
283 // clear_craft({[output=item], [recipe={{item00,item10},{item01,item11}}])
284 int ModApiCraft::l_clear_craft(lua_State *L)
285 {
286         NO_MAP_LOCK_REQUIRED;
287         luaL_checktype(L, 1, LUA_TTABLE);
288         int table = 1;
289
290         // Get the writable craft definition manager from the server
291         IWritableCraftDefManager *craftdef =
292                         getServer(L)->getWritableCraftDefManager();
293
294         std::string output = getstringfield_default(L, table, "output", "");
295         std::string type = getstringfield_default(L, table, "type", "shaped");
296         CraftOutput c_output(output, 0);
297         if (output != "") {
298                 if (craftdef->clearCraftRecipesByOutput(c_output, getServer(L)))
299                         return 0;
300                 else
301                         throw LuaError("No craft recipe known for output"
302                                         " (output=\"" + output + "\")");
303         }
304         std::vector<std::string> recipe;
305         int width = 0;
306         CraftMethod method = CRAFT_METHOD_NORMAL;
307         /*
308                 CraftDefinitionShaped
309         */
310         if (type == "shaped") {
311                 lua_getfield(L, table, "recipe");
312                 if (lua_isnil(L, -1))
313                         throw LuaError("Either output or recipe has to be defined");
314                 if (!readCraftRecipeShaped(L, -1, width, recipe))
315                         throw LuaError("Invalid crafting recipe");
316         }
317         /*
318                 CraftDefinitionShapeless
319         */
320         else if (type == "shapeless") {
321                 lua_getfield(L, table, "recipe");
322                 if (lua_isnil(L, -1))
323                         throw LuaError("Either output or recipe has to be defined");
324                 if (!readCraftRecipeShapeless(L, -1, recipe))
325                         throw LuaError("Invalid crafting recipe");
326         }
327         /*
328                 CraftDefinitionCooking
329         */
330         else if (type == "cooking") {
331                 method = CRAFT_METHOD_COOKING;
332                 std::string rec = getstringfield_default(L, table, "recipe", "");
333                 if (rec == "")
334                         throw LuaError("Crafting definition (cooking)"
335                                         " is missing a recipe");
336                 recipe.push_back(rec);
337         }
338         /*
339                 CraftDefinitionFuel
340         */
341         else if (type == "fuel") {
342                 method = CRAFT_METHOD_FUEL;
343                 std::string rec = getstringfield_default(L, table, "recipe", "");
344                 if (rec == "")
345                         throw LuaError("Crafting definition (fuel)"
346                                         " is missing a recipe");
347                 recipe.push_back(rec);
348         } else {
349                 throw LuaError("Unknown crafting definition type: \"" + type + "\"");
350         }
351         if (!craftdef->clearCraftRecipesByInput(method, width, recipe, getServer(L)))
352                 throw LuaError("No crafting specified for input");
353         lua_pop(L, 1);
354         return 0;
355 }
356
357 // get_craft_result(input)
358 int ModApiCraft::l_get_craft_result(lua_State *L)
359 {
360         NO_MAP_LOCK_REQUIRED;
361
362         int input_i = 1;
363         std::string method_s = getstringfield_default(L, input_i, "method", "normal");
364         enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method",
365                                 es_CraftMethod, CRAFT_METHOD_NORMAL);
366         int width = 1;
367         lua_getfield(L, input_i, "width");
368         if(lua_isnumber(L, -1))
369                 width = luaL_checkinteger(L, -1);
370         lua_pop(L, 1);
371         lua_getfield(L, input_i, "items");
372         std::vector<ItemStack> items = read_items(L, -1,getServer(L));
373         lua_pop(L, 1); // items
374
375         IGameDef *gdef = getServer(L);
376         ICraftDefManager *cdef = gdef->cdef();
377         CraftInput input(method, width, items);
378         CraftOutput output;
379         std::vector<ItemStack> output_replacements;
380         bool got = cdef->getCraftResult(input, output, output_replacements, true, gdef);
381         lua_newtable(L); // output table
382         if (got) {
383                 ItemStack item;
384                 item.deSerialize(output.item, gdef->idef());
385                 LuaItemStack::create(L, item);
386                 lua_setfield(L, -2, "item");
387                 setintfield(L, -1, "time", output.time);
388                 push_items(L, output_replacements);
389                 lua_setfield(L, -2, "replacements");
390         } else {
391                 LuaItemStack::create(L, ItemStack());
392                 lua_setfield(L, -2, "item");
393                 setintfield(L, -1, "time", 0);
394                 lua_newtable(L);
395                 lua_setfield(L, -2, "replacements");
396         }
397         lua_newtable(L); // decremented input table
398         lua_pushstring(L, method_s.c_str());
399         lua_setfield(L, -2, "method");
400         lua_pushinteger(L, width);
401         lua_setfield(L, -2, "width");
402         push_items(L, input.items);
403         lua_setfield(L, -2, "items");
404         return 2;
405 }
406
407
408 static void push_craft_recipe(lua_State *L, IGameDef *gdef,
409                 const CraftDefinition *recipe,
410                 const CraftOutput &tmpout)
411 {
412         CraftInput input = recipe->getInput(tmpout, gdef);
413         CraftOutput output = recipe->getOutput(input, gdef);
414
415         lua_newtable(L); // items
416         std::vector<ItemStack>::const_iterator iter = input.items.begin();
417         for (u16 j = 1; iter != input.items.end(); ++iter, j++) {
418                 if (iter->empty())
419                         continue;
420                 lua_pushstring(L, iter->name.c_str());
421                 lua_rawseti(L, -2, j);
422         }
423         lua_setfield(L, -2, "items");
424         setintfield(L, -1, "width", input.width);
425
426         std::string method_s;
427         switch (input.method) {
428         case CRAFT_METHOD_NORMAL:
429                 method_s = "normal";
430                 break;
431         case CRAFT_METHOD_COOKING:
432                 method_s = "cooking";
433                 break;
434         case CRAFT_METHOD_FUEL:
435                 method_s = "fuel";
436                 break;
437         default:
438                 method_s = "unknown";
439         }
440         lua_pushstring(L, method_s.c_str());
441         lua_setfield(L, -2, "method");
442
443         // Deprecated, only for compatibility's sake
444         lua_pushstring(L, method_s.c_str());
445         lua_setfield(L, -2, "type");
446
447         lua_pushstring(L, output.item.c_str());
448         lua_setfield(L, -2, "output");
449 }
450
451 static void push_craft_recipes(lua_State *L, IGameDef *gdef,
452                 const std::vector<CraftDefinition*> &recipes,
453                 const CraftOutput &output)
454 {
455         lua_createtable(L, recipes.size(), 0);
456
457         if (recipes.empty()) {
458                 lua_pushnil(L);
459                 return;
460         }
461
462         std::vector<CraftDefinition*>::const_iterator it = recipes.begin();
463         for (unsigned i = 0; it != recipes.end(); ++it) {
464                 lua_newtable(L);
465                 push_craft_recipe(L, gdef, *it, output);
466                 lua_rawseti(L, -2, ++i);
467         }
468 }
469
470
471 // get_craft_recipe(result item)
472 int ModApiCraft::l_get_craft_recipe(lua_State *L)
473 {
474         NO_MAP_LOCK_REQUIRED;
475
476         std::string item = luaL_checkstring(L, 1);
477         Server *server = getServer(L);
478         CraftOutput output(item, 0);
479         std::vector<CraftDefinition*> recipes = server->cdef()
480                         ->getCraftRecipes(output, server, 1);
481
482         lua_createtable(L, 1, 0);
483
484         if (recipes.empty()) {
485                 lua_pushnil(L);
486                 lua_setfield(L, -2, "items");
487                 setintfield(L, -1, "width", 0);
488                 return 1;
489         }
490         push_craft_recipe(L, server, recipes[0], output);
491         return 1;
492 }
493
494 // get_all_craft_recipes(result item)
495 int ModApiCraft::l_get_all_craft_recipes(lua_State *L)
496 {
497         NO_MAP_LOCK_REQUIRED;
498
499         std::string item = luaL_checkstring(L, 1);
500         Server *server = getServer(L);
501         CraftOutput output(item, 0);
502         std::vector<CraftDefinition*> recipes = server->cdef()
503                         ->getCraftRecipes(output, server);
504
505         push_craft_recipes(L, server, recipes, output);
506         return 1;
507 }
508
509 void ModApiCraft::Initialize(lua_State *L, int top)
510 {
511         API_FCT(get_all_craft_recipes);
512         API_FCT(get_craft_recipe);
513         API_FCT(get_craft_result);
514         API_FCT(register_craft);
515         API_FCT(clear_craft);
516 }