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