]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/lua_api/l_craft.cpp
Pathfinder: Fix style
[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
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("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("Crafting definition is missing a recipe"
160                                         " (output=\"" + output + "\")");
161                 if(!readCraftRecipeShaped(L, -1, width, recipe))
162                         throw LuaError("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("Invalid replacements"
171                                                 " (output=\"" + output + "\")");
172                 }
173
174                 CraftDefinition *def = new CraftDefinitionShaped(
175                                 output, width, recipe, replacements);
176                 craftdef->registerCraft(def, getServer(L));
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("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("Crafting definition (shapeless)"
191                                         " is missing a recipe"
192                                         " (output=\"" + output + "\")");
193                 if(!readCraftRecipeShapeless(L, -1, recipe))
194                         throw LuaError("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("Invalid replacements"
203                                                 " (output=\"" + output + "\")");
204                 }
205
206                 CraftDefinition *def = new CraftDefinitionShapeless(
207                                 output, recipe, replacements);
208                 craftdef->registerCraft(def, getServer(L));
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, getServer(L));
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("Crafting definition (cooking)"
228                                         " is missing an output");
229
230                 std::string recipe = getstringfield_default(L, table, "recipe", "");
231                 if(recipe == "")
232                         throw LuaError("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("Invalid replacements"
244                                                 " (cooking output=\"" + output + "\")");
245                 }
246
247                 CraftDefinition *def = new CraftDefinitionCooking(
248                                 output, recipe, cooktime, replacements);
249                 craftdef->registerCraft(def, getServer(L));
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("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("Invalid replacements"
268                                                 " (fuel recipe=\"" + recipe + "\")");
269                 }
270
271                 CraftDefinition *def = new CraftDefinitionFuel(
272                                 recipe, burntime, replacements);
273                 craftdef->registerCraft(def, getServer(L));
274         }
275         else
276         {
277                 throw LuaError("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         std::vector<ItemStack> output_replacements;
307         bool got = cdef->getCraftResult(input, output, output_replacements, 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                 push_items(L, output_replacements);
316                 lua_setfield(L, -2, "replacements");
317         } else {
318                 LuaItemStack::create(L, ItemStack());
319                 lua_setfield(L, -2, "item");
320                 setintfield(L, -1, "time", 0);
321                 lua_newtable(L);
322                 lua_setfield(L, -2, "replacements");
323         }
324         lua_newtable(L); // decremented input table
325         lua_pushstring(L, method_s.c_str());
326         lua_setfield(L, -2, "method");
327         lua_pushinteger(L, width);
328         lua_setfield(L, -2, "width");
329         push_items(L, input.items);
330         lua_setfield(L, -2, "items");
331         return 2;
332 }
333
334
335 static void push_craft_recipe(lua_State *L, IGameDef *gdef,
336                 const CraftDefinition *recipe,
337                 const CraftOutput &tmpout)
338 {
339         CraftInput input = recipe->getInput(tmpout, gdef);
340         CraftOutput output = recipe->getOutput(input, gdef);
341
342         lua_newtable(L); // items
343         std::vector<ItemStack>::const_iterator iter = input.items.begin();
344         for (u16 j = 1; iter != input.items.end(); iter++, j++) {
345                 if (iter->empty())
346                         continue;
347                 lua_pushstring(L, iter->name.c_str());
348                 lua_rawseti(L, -2, j);
349         }
350         lua_setfield(L, -2, "items");
351         setintfield(L, -1, "width", input.width);
352         switch (input.method) {
353         case CRAFT_METHOD_NORMAL:
354                 lua_pushstring(L, "normal");
355                 break;
356         case CRAFT_METHOD_COOKING:
357                 lua_pushstring(L, "cooking");
358                 break;
359         case CRAFT_METHOD_FUEL:
360                 lua_pushstring(L, "fuel");
361                 break;
362         default:
363                 lua_pushstring(L, "unknown");
364         }
365         lua_setfield(L, -2, "type");
366         lua_pushstring(L, output.item.c_str());
367         lua_setfield(L, -2, "output");
368 }
369
370 static void push_craft_recipes(lua_State *L, IGameDef *gdef,
371                 const std::vector<CraftDefinition*> &recipes,
372                 const CraftOutput &output)
373 {
374         lua_createtable(L, recipes.size(), 0);
375
376         if (recipes.empty()) {
377                 lua_pushnil(L);
378                 return;
379         }
380
381         std::vector<CraftDefinition*>::const_iterator it = recipes.begin();
382         for (unsigned i = 0; it != recipes.end(); ++it) {
383                 lua_newtable(L);
384                 push_craft_recipe(L, gdef, *it, output);
385                 lua_rawseti(L, -2, ++i);
386         }
387 }
388
389
390 // get_craft_recipe(result item)
391 int ModApiCraft::l_get_craft_recipe(lua_State *L)
392 {
393         NO_MAP_LOCK_REQUIRED;
394
395         std::string item = luaL_checkstring(L, 1);
396         Server *server = getServer(L);
397         CraftOutput output(item, 0);
398         std::vector<CraftDefinition*> recipes = server->cdef()
399                         ->getCraftRecipes(output, server, 1);
400
401         lua_createtable(L, 1, 0);
402
403         if (recipes.empty()) {
404                 lua_pushnil(L);
405                 lua_setfield(L, -2, "items");
406                 setintfield(L, -1, "width", 0);
407                 return 1;
408         }
409         push_craft_recipe(L, server, recipes[0], output);
410         return 1;
411 }
412
413 // get_all_craft_recipes(result item)
414 int ModApiCraft::l_get_all_craft_recipes(lua_State *L)
415 {
416         NO_MAP_LOCK_REQUIRED;
417
418         std::string item = luaL_checkstring(L, 1);
419         Server *server = getServer(L);
420         CraftOutput output(item, 0);
421         std::vector<CraftDefinition*> recipes = server->cdef()
422                         ->getCraftRecipes(output, server);
423
424         push_craft_recipes(L, server, recipes, output);
425         return 1;
426 }
427
428 void ModApiCraft::Initialize(lua_State *L, int top)
429 {
430         API_FCT(get_all_craft_recipes);
431         API_FCT(get_craft_recipe);
432         API_FCT(get_craft_result);
433         API_FCT(register_craft);
434 }