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