3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
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"
29 struct EnumString ModApiCraft::es_CraftMethod[] =
31 {CRAFT_METHOD_NORMAL, "normal"},
32 {CRAFT_METHOD_COOKING, "cooking"},
33 {CRAFT_METHOD_FUEL, "fuel"},
38 // helper for register_craft
39 bool ModApiCraft::readCraftRecipeShaped(lua_State *L, int index,
40 int &width, std::vector<std::string> &recipe)
43 index = lua_gettop(L) + 1 + index;
45 if(!lua_istable(L, index))
50 while(lua_next(L, index) != 0){
52 // key at index -2 and value at index -1
53 if(!lua_istable(L, -1))
55 int table2 = lua_gettop(L);
57 while(lua_next(L, table2) != 0){
58 // key at index -2 and value at index -1
59 if(!lua_isstring(L, -1))
61 recipe.push_back(lua_tostring(L, -1));
62 // removes value, keeps key for next iteration
72 // removes value, keeps key for next iteration
79 // helper for register_craft
80 bool ModApiCraft::readCraftRecipeShapeless(lua_State *L, int index,
81 std::vector<std::string> &recipe)
84 index = lua_gettop(L) + 1 + index;
86 if(!lua_istable(L, index))
90 while(lua_next(L, index) != 0){
91 // key at index -2 and value at index -1
92 if(!lua_isstring(L, -1))
94 recipe.push_back(lua_tostring(L, -1));
95 // removes value, keeps key for next iteration
101 // helper for register_craft
102 bool ModApiCraft::readCraftReplacements(lua_State *L, int index,
103 CraftReplacements &replacements)
106 index = lua_gettop(L) + 1 + index;
108 if(!lua_istable(L, index))
112 while(lua_next(L, index) != 0){
113 // key at index -2 and value at index -1
114 if(!lua_istable(L, -1))
116 lua_rawgeti(L, -1, 1);
117 if(!lua_isstring(L, -1))
119 std::string replace_from = lua_tostring(L, -1);
121 lua_rawgeti(L, -1, 2);
122 if(!lua_isstring(L, -1))
124 std::string replace_to = lua_tostring(L, -1);
126 replacements.pairs.push_back(
127 std::make_pair(replace_from, replace_to));
128 // removes value, keeps key for next iteration
133 // register_craft({output=item, recipe={{item00,item10},{item01,item11}})
134 int ModApiCraft::l_register_craft(lua_State *L)
136 NO_MAP_LOCK_REQUIRED;
137 //infostream<<"register_craft"<<std::endl;
138 luaL_checktype(L, 1, LUA_TTABLE);
141 // Get the writable craft definition manager from the server
142 IWritableCraftDefManager *craftdef =
143 getServer(L)->getWritableCraftDefManager();
145 std::string type = getstringfield_default(L, table, "type", "shaped");
148 CraftDefinitionShaped
150 if(type == "shaped"){
151 std::string output = getstringfield_default(L, table, "output", "");
153 throw LuaError(L, "Crafting definition is missing an output");
156 std::vector<std::string> recipe;
157 lua_getfield(L, table, "recipe");
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 + "\")");
165 CraftReplacements replacements;
166 lua_getfield(L, table, "replacements");
167 if(!lua_isnil(L, -1))
169 if(!readCraftReplacements(L, -1, replacements))
170 throw LuaError(L, "Invalid replacements"
171 " (output=\"" + output + "\")");
174 CraftDefinition *def = new CraftDefinitionShaped(
175 output, width, recipe, replacements);
176 craftdef->registerCraft(def);
179 CraftDefinitionShapeless
181 else if(type == "shapeless"){
182 std::string output = getstringfield_default(L, table, "output", "");
184 throw LuaError(L, "Crafting definition (shapeless)"
185 " is missing an output");
187 std::vector<std::string> recipe;
188 lua_getfield(L, table, "recipe");
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 + "\")");
197 CraftReplacements replacements;
198 lua_getfield(L, table, "replacements");
199 if(!lua_isnil(L, -1))
201 if(!readCraftReplacements(L, -1, replacements))
202 throw LuaError(L, "Invalid replacements"
203 " (output=\"" + output + "\")");
206 CraftDefinition *def = new CraftDefinitionShapeless(
207 output, recipe, replacements);
208 craftdef->registerCraft(def);
211 CraftDefinitionToolRepair
213 else if(type == "toolrepair"){
214 float additional_wear = getfloatfield_default(L, table,
215 "additional_wear", 0.0);
217 CraftDefinition *def = new CraftDefinitionToolRepair(
219 craftdef->registerCraft(def);
222 CraftDefinitionCooking
224 else if(type == "cooking"){
225 std::string output = getstringfield_default(L, table, "output", "");
227 throw LuaError(L, "Crafting definition (cooking)"
228 " is missing an output");
230 std::string recipe = getstringfield_default(L, table, "recipe", "");
232 throw LuaError(L, "Crafting definition (cooking)"
233 " is missing a recipe"
234 " (output=\"" + output + "\")");
236 float cooktime = getfloatfield_default(L, table, "cooktime", 3.0);
238 CraftReplacements replacements;
239 lua_getfield(L, table, "replacements");
240 if(!lua_isnil(L, -1))
242 if(!readCraftReplacements(L, -1, replacements))
243 throw LuaError(L, "Invalid replacements"
244 " (cooking output=\"" + output + "\")");
247 CraftDefinition *def = new CraftDefinitionCooking(
248 output, recipe, cooktime, replacements);
249 craftdef->registerCraft(def);
254 else if(type == "fuel"){
255 std::string recipe = getstringfield_default(L, table, "recipe", "");
257 throw LuaError(L, "Crafting definition (fuel)"
258 " is missing a recipe");
260 float burntime = getfloatfield_default(L, table, "burntime", 1.0);
262 CraftReplacements replacements;
263 lua_getfield(L, table, "replacements");
264 if(!lua_isnil(L, -1))
266 if(!readCraftReplacements(L, -1, replacements))
267 throw LuaError(L, "Invalid replacements"
268 " (fuel recipe=\"" + recipe + "\")");
271 CraftDefinition *def = new CraftDefinitionFuel(
272 recipe, burntime, replacements);
273 craftdef->registerCraft(def);
277 throw LuaError(L, "Unknown crafting definition type: \"" + type + "\"");
281 return 0; /* number of results */
284 // get_craft_result(input)
285 int ModApiCraft::l_get_craft_result(lua_State *L)
287 NO_MAP_LOCK_REQUIRED;
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);
294 lua_getfield(L, input_i, "width");
295 if(lua_isnumber(L, -1))
296 width = luaL_checkinteger(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
302 IGameDef *gdef = getServer(L);
303 ICraftDefManager *cdef = gdef->cdef();
304 CraftInput input(method, width, items);
306 bool got = cdef->getCraftResult(input, output, true, gdef);
307 lua_newtable(L); // output table
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);
315 LuaItemStack::create(L, ItemStack());
316 lua_setfield(L, -2, "item");
317 setintfield(L, -1, "time", 0);
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");
329 // get_craft_recipe(result item)
330 int ModApiCraft::l_get_craft_recipe(lua_State *L)
332 NO_MAP_LOCK_REQUIRED;
336 std::string o_item = luaL_checkstring(L,input_i);
338 IGameDef *gdef = getServer(L);
339 ICraftDefManager *cdef = gdef->cdef();
341 CraftOutput output(o_item,0);
342 bool got = cdef->getCraftRecipe(input, output, gdef);
343 lua_newtable(L); // output table
346 for(std::vector<ItemStack>::const_iterator
347 i = input.items.begin();
348 i != input.items.end(); i++, k++)
354 lua_pushinteger(L,k);
355 lua_pushstring(L,i->name.c_str());
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");
364 case CRAFT_METHOD_COOKING:
365 lua_pushstring(L,"cooking");
367 case CRAFT_METHOD_FUEL:
368 lua_pushstring(L,"fuel");
371 lua_pushstring(L,"unknown");
373 lua_setfield(L, -2, "type");
376 lua_setfield(L, -2, "items");
377 setintfield(L, -1, "width", 0);
382 // get_all_craft_recipes(result item)
383 int ModApiCraft::l_get_all_craft_recipes(lua_State *L)
385 NO_MAP_LOCK_REQUIRED;
387 std::string o_item = luaL_checkstring(L,1);
388 IGameDef *gdef = getServer(L);
389 ICraftDefManager *cdef = gdef->cdef();
391 CraftOutput output(o_item,0);
392 std::vector<CraftDefinition*> recipes_list = cdef->getCraftRecipes(output, gdef);
393 if (recipes_list.empty())
398 // Get the table insert function
399 lua_getglobal(L, "table");
400 lua_getfield(L, -1, "insert");
401 int table_insert = lua_gettop(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++)
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)
417 input = def->getInput(output, gdef);
418 lua_pushvalue(L, table_insert);
419 lua_pushvalue(L, table);
423 for(std::vector<ItemStack>::const_iterator
424 i = input.items.begin();
425 i != input.items.end(); i++, k++)
429 lua_pushinteger(L,k);
430 lua_pushstring(L,i->name.c_str());
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");
439 case CRAFT_METHOD_COOKING:
440 lua_pushstring(L,"cooking");
442 case CRAFT_METHOD_FUEL:
443 lua_pushstring(L,"fuel");
446 lua_pushstring(L,"unknown");
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));
458 void ModApiCraft::Initialize(lua_State *L, int top)
460 API_FCT(get_all_craft_recipes);
461 API_FCT(get_craft_recipe);
462 API_FCT(get_craft_result);
463 API_FCT(register_craft);