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("Crafting definition is missing an output");
156 std::vector<std::string> recipe;
157 lua_getfield(L, table, "recipe");
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 + "\")");
165 CraftReplacements replacements;
166 lua_getfield(L, table, "replacements");
167 if(!lua_isnil(L, -1))
169 if(!readCraftReplacements(L, -1, replacements))
170 throw LuaError("Invalid replacements"
171 " (output=\"" + output + "\")");
174 CraftDefinition *def = new CraftDefinitionShaped(
175 output, width, recipe, replacements);
176 craftdef->registerCraft(def, getServer(L));
179 CraftDefinitionShapeless
181 else if(type == "shapeless"){
182 std::string output = getstringfield_default(L, table, "output", "");
184 throw LuaError("Crafting definition (shapeless)"
185 " is missing an output");
187 std::vector<std::string> recipe;
188 lua_getfield(L, table, "recipe");
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 + "\")");
197 CraftReplacements replacements;
198 lua_getfield(L, table, "replacements");
199 if(!lua_isnil(L, -1))
201 if(!readCraftReplacements(L, -1, replacements))
202 throw LuaError("Invalid replacements"
203 " (output=\"" + output + "\")");
206 CraftDefinition *def = new CraftDefinitionShapeless(
207 output, recipe, replacements);
208 craftdef->registerCraft(def, getServer(L));
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, getServer(L));
222 CraftDefinitionCooking
224 else if(type == "cooking"){
225 std::string output = getstringfield_default(L, table, "output", "");
227 throw LuaError("Crafting definition (cooking)"
228 " is missing an output");
230 std::string recipe = getstringfield_default(L, table, "recipe", "");
232 throw LuaError("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("Invalid replacements"
244 " (cooking output=\"" + output + "\")");
247 CraftDefinition *def = new CraftDefinitionCooking(
248 output, recipe, cooktime, replacements);
249 craftdef->registerCraft(def, getServer(L));
254 else if(type == "fuel"){
255 std::string recipe = getstringfield_default(L, table, "recipe", "");
257 throw LuaError("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("Invalid replacements"
268 " (fuel recipe=\"" + recipe + "\")");
271 CraftDefinition *def = new CraftDefinitionFuel(
272 recipe, burntime, replacements);
273 craftdef->registerCraft(def, getServer(L));
277 throw LuaError("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 std::vector<ItemStack> output_replacements;
307 bool got = cdef->getCraftResult(input, output, output_replacements, true, gdef);
308 lua_newtable(L); // output table
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");
318 LuaItemStack::create(L, ItemStack());
319 lua_setfield(L, -2, "item");
320 setintfield(L, -1, "time", 0);
322 lua_setfield(L, -2, "replacements");
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");
335 static void push_craft_recipe(lua_State *L, IGameDef *gdef,
336 const CraftDefinition *recipe,
337 const CraftOutput &tmpout)
339 CraftInput input = recipe->getInput(tmpout, gdef);
340 CraftOutput output = recipe->getOutput(input, gdef);
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++) {
347 lua_pushstring(L, iter->name.c_str());
348 lua_rawseti(L, -2, j);
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");
356 case CRAFT_METHOD_COOKING:
357 lua_pushstring(L, "cooking");
359 case CRAFT_METHOD_FUEL:
360 lua_pushstring(L, "fuel");
363 lua_pushstring(L, "unknown");
365 lua_setfield(L, -2, "type");
366 lua_pushstring(L, output.item.c_str());
367 lua_setfield(L, -2, "output");
370 static void push_craft_recipes(lua_State *L, IGameDef *gdef,
371 const std::vector<CraftDefinition*> &recipes,
372 const CraftOutput &output)
374 lua_createtable(L, recipes.size(), 0);
376 if (recipes.empty()) {
381 std::vector<CraftDefinition*>::const_iterator it = recipes.begin();
382 for (unsigned i = 0; it != recipes.end(); ++it) {
384 push_craft_recipe(L, gdef, *it, output);
385 lua_rawseti(L, -2, ++i);
390 // get_craft_recipe(result item)
391 int ModApiCraft::l_get_craft_recipe(lua_State *L)
393 NO_MAP_LOCK_REQUIRED;
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);
401 lua_createtable(L, 1, 0);
403 if (recipes.empty()) {
405 lua_setfield(L, -2, "items");
406 setintfield(L, -1, "width", 0);
409 push_craft_recipe(L, server, recipes[0], output);
413 // get_all_craft_recipes(result item)
414 int ModApiCraft::l_get_all_craft_recipes(lua_State *L)
416 NO_MAP_LOCK_REQUIRED;
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);
424 push_craft_recipes(L, server, recipes, output);
428 void ModApiCraft::Initialize(lua_State *L, int top)
430 API_FCT(get_all_craft_recipes);
431 API_FCT(get_craft_recipe);
432 API_FCT(get_craft_result);
433 API_FCT(register_craft);