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