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