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"},
37 // helper for register_craft
38 bool ModApiCraft::readCraftRecipeShaped(lua_State *L, int index,
39 int &width, std::vector<std::string> &recipe)
42 index = lua_gettop(L) + 1 + index;
44 if(!lua_istable(L, index))
49 while(lua_next(L, index) != 0){
51 // key at index -2 and value at index -1
52 if(!lua_istable(L, -1))
54 int table2 = lua_gettop(L);
56 while(lua_next(L, table2) != 0){
57 // key at index -2 and value at index -1
58 if(!lua_isstring(L, -1))
60 recipe.emplace_back(lua_tostring(L, -1));
61 // removes value, keeps key for next iteration
71 // removes value, keeps key for next iteration
78 // helper for register_craft
79 bool ModApiCraft::readCraftRecipeShapeless(lua_State *L, int index,
80 std::vector<std::string> &recipe)
83 index = lua_gettop(L) + 1 + index;
85 if(!lua_istable(L, index))
89 while(lua_next(L, index) != 0){
90 // key at index -2 and value at index -1
91 if(!lua_isstring(L, -1))
93 recipe.emplace_back(lua_tostring(L, -1));
94 // removes value, keeps key for next iteration
100 // helper for register_craft
101 bool ModApiCraft::readCraftReplacements(lua_State *L, int index,
102 CraftReplacements &replacements)
105 index = lua_gettop(L) + 1 + index;
107 if(!lua_istable(L, index))
111 while(lua_next(L, index) != 0){
112 // key at index -2 and value at index -1
113 if(!lua_istable(L, -1))
115 lua_rawgeti(L, -1, 1);
116 if(!lua_isstring(L, -1))
118 std::string replace_from = lua_tostring(L, -1);
120 lua_rawgeti(L, -1, 2);
121 if(!lua_isstring(L, -1))
123 std::string replace_to = lua_tostring(L, -1);
125 replacements.pairs.emplace_back(replace_from, replace_to);
126 // removes value, keeps key for next iteration
131 // register_craft({output=item, recipe={{item00,item10},{item01,item11}})
132 int ModApiCraft::l_register_craft(lua_State *L)
134 NO_MAP_LOCK_REQUIRED;
135 //infostream<<"register_craft"<<std::endl;
136 luaL_checktype(L, 1, LUA_TTABLE);
139 // Get the writable craft definition manager from the server
140 IWritableCraftDefManager *craftdef =
141 getServer(L)->getWritableCraftDefManager();
143 std::string type = getstringfield_default(L, table, "type", "shaped");
146 CraftDefinitionShaped
148 if(type == "shaped"){
149 std::string output = getstringfield_default(L, table, "output", "");
151 throw LuaError("Crafting definition is missing an output");
154 std::vector<std::string> recipe;
155 lua_getfield(L, table, "recipe");
157 throw LuaError("Crafting definition is missing a recipe"
158 " (output=\"" + output + "\")");
159 if(!readCraftRecipeShaped(L, -1, width, recipe))
160 throw LuaError("Invalid crafting recipe"
161 " (output=\"" + output + "\")");
163 CraftReplacements replacements;
164 lua_getfield(L, table, "replacements");
165 if(!lua_isnil(L, -1))
167 if(!readCraftReplacements(L, -1, replacements))
168 throw LuaError("Invalid replacements"
169 " (output=\"" + output + "\")");
172 CraftDefinition *def = new CraftDefinitionShaped(
173 output, width, recipe, replacements);
174 craftdef->registerCraft(def, getServer(L));
177 CraftDefinitionShapeless
179 else if(type == "shapeless"){
180 std::string output = getstringfield_default(L, table, "output", "");
182 throw LuaError("Crafting definition (shapeless)"
183 " is missing an output");
185 std::vector<std::string> recipe;
186 lua_getfield(L, table, "recipe");
188 throw LuaError("Crafting definition (shapeless)"
189 " is missing a recipe"
190 " (output=\"" + output + "\")");
191 if(!readCraftRecipeShapeless(L, -1, recipe))
192 throw LuaError("Invalid crafting recipe"
193 " (output=\"" + output + "\")");
195 CraftReplacements replacements;
196 lua_getfield(L, table, "replacements");
197 if(!lua_isnil(L, -1))
199 if(!readCraftReplacements(L, -1, replacements))
200 throw LuaError("Invalid replacements"
201 " (output=\"" + output + "\")");
204 CraftDefinition *def = new CraftDefinitionShapeless(
205 output, recipe, replacements);
206 craftdef->registerCraft(def, getServer(L));
209 CraftDefinitionToolRepair
211 else if(type == "toolrepair"){
212 float additional_wear = getfloatfield_default(L, table,
213 "additional_wear", 0.0);
215 CraftDefinition *def = new CraftDefinitionToolRepair(
217 craftdef->registerCraft(def, getServer(L));
220 CraftDefinitionCooking
222 else if(type == "cooking"){
223 std::string output = getstringfield_default(L, table, "output", "");
225 throw LuaError("Crafting definition (cooking)"
226 " is missing an output");
228 std::string recipe = getstringfield_default(L, table, "recipe", "");
230 throw LuaError("Crafting definition (cooking)"
231 " is missing a recipe"
232 " (output=\"" + output + "\")");
234 float cooktime = getfloatfield_default(L, table, "cooktime", 3.0);
236 CraftReplacements replacements;
237 lua_getfield(L, table, "replacements");
238 if(!lua_isnil(L, -1))
240 if(!readCraftReplacements(L, -1, replacements))
241 throw LuaError("Invalid replacements"
242 " (cooking output=\"" + output + "\")");
245 CraftDefinition *def = new CraftDefinitionCooking(
246 output, recipe, cooktime, replacements);
247 craftdef->registerCraft(def, getServer(L));
252 else if(type == "fuel"){
253 std::string recipe = getstringfield_default(L, table, "recipe", "");
255 throw LuaError("Crafting definition (fuel)"
256 " is missing a recipe");
258 float burntime = getfloatfield_default(L, table, "burntime", 1.0);
260 CraftReplacements replacements;
261 lua_getfield(L, table, "replacements");
262 if(!lua_isnil(L, -1))
264 if(!readCraftReplacements(L, -1, replacements))
265 throw LuaError("Invalid replacements"
266 " (fuel recipe=\"" + recipe + "\")");
269 CraftDefinition *def = new CraftDefinitionFuel(
270 recipe, burntime, replacements);
271 craftdef->registerCraft(def, getServer(L));
275 throw LuaError("Unknown crafting definition type: \"" + type + "\"");
279 return 0; /* number of results */
282 // clear_craft({[output=item], [recipe={{item00,item10},{item01,item11}}])
283 int ModApiCraft::l_clear_craft(lua_State *L)
285 NO_MAP_LOCK_REQUIRED;
286 luaL_checktype(L, 1, LUA_TTABLE);
289 // Get the writable craft definition manager from the server
290 IWritableCraftDefManager *craftdef =
291 getServer(L)->getWritableCraftDefManager();
293 std::string output = getstringfield_default(L, table, "output", "");
294 std::string type = getstringfield_default(L, table, "type", "shaped");
295 CraftOutput c_output(output, 0);
296 if (!output.empty()) {
297 if (craftdef->clearCraftRecipesByOutput(c_output, getServer(L)))
300 throw LuaError("No craft recipe known for output"
301 " (output=\"" + output + "\")");
303 std::vector<std::string> recipe;
305 CraftMethod method = CRAFT_METHOD_NORMAL;
307 CraftDefinitionShaped
309 if (type == "shaped") {
310 lua_getfield(L, table, "recipe");
311 if (lua_isnil(L, -1))
312 throw LuaError("Either output or recipe has to be defined");
313 if (!readCraftRecipeShaped(L, -1, width, recipe))
314 throw LuaError("Invalid crafting recipe");
317 CraftDefinitionShapeless
319 else if (type == "shapeless") {
320 lua_getfield(L, table, "recipe");
321 if (lua_isnil(L, -1))
322 throw LuaError("Either output or recipe has to be defined");
323 if (!readCraftRecipeShapeless(L, -1, recipe))
324 throw LuaError("Invalid crafting recipe");
327 CraftDefinitionCooking
329 else if (type == "cooking") {
330 method = CRAFT_METHOD_COOKING;
331 std::string rec = getstringfield_default(L, table, "recipe", "");
333 throw LuaError("Crafting definition (cooking)"
334 " is missing a recipe");
335 recipe.push_back(rec);
340 else if (type == "fuel") {
341 method = CRAFT_METHOD_FUEL;
342 std::string rec = getstringfield_default(L, table, "recipe", "");
344 throw LuaError("Crafting definition (fuel)"
345 " is missing a recipe");
346 recipe.push_back(rec);
348 throw LuaError("Unknown crafting definition type: \"" + type + "\"");
350 if (!craftdef->clearCraftRecipesByInput(method, width, recipe, getServer(L)))
351 throw LuaError("No crafting specified for input");
356 // get_craft_result(input)
357 int ModApiCraft::l_get_craft_result(lua_State *L)
359 NO_MAP_LOCK_REQUIRED;
362 std::string method_s = getstringfield_default(L, input_i, "method", "normal");
363 enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method",
364 es_CraftMethod, CRAFT_METHOD_NORMAL);
366 lua_getfield(L, input_i, "width");
367 if(lua_isnumber(L, -1))
368 width = luaL_checkinteger(L, -1);
370 lua_getfield(L, input_i, "items");
371 std::vector<ItemStack> items = read_items(L, -1,getServer(L));
372 lua_pop(L, 1); // items
374 IGameDef *gdef = getServer(L);
375 ICraftDefManager *cdef = gdef->cdef();
376 CraftInput input(method, width, items);
378 std::vector<ItemStack> output_replacements;
379 bool got = cdef->getCraftResult(input, output, output_replacements, true, gdef);
380 lua_newtable(L); // output table
383 item.deSerialize(output.item, gdef->idef());
384 LuaItemStack::create(L, item);
385 lua_setfield(L, -2, "item");
386 setintfield(L, -1, "time", output.time);
387 push_items(L, output_replacements);
388 lua_setfield(L, -2, "replacements");
390 LuaItemStack::create(L, ItemStack());
391 lua_setfield(L, -2, "item");
392 setintfield(L, -1, "time", 0);
394 lua_setfield(L, -2, "replacements");
396 lua_newtable(L); // decremented input table
397 lua_pushstring(L, method_s.c_str());
398 lua_setfield(L, -2, "method");
399 lua_pushinteger(L, width);
400 lua_setfield(L, -2, "width");
401 push_items(L, input.items);
402 lua_setfield(L, -2, "items");
407 static void push_craft_recipe(lua_State *L, IGameDef *gdef,
408 const CraftDefinition *recipe,
409 const CraftOutput &tmpout)
411 CraftInput input = recipe->getInput(tmpout, gdef);
412 CraftOutput output = recipe->getOutput(input, gdef);
414 lua_newtable(L); // items
415 std::vector<ItemStack>::const_iterator iter = input.items.begin();
416 for (u16 j = 1; iter != input.items.end(); ++iter, j++) {
419 lua_pushstring(L, iter->name.c_str());
420 lua_rawseti(L, -2, j);
422 lua_setfield(L, -2, "items");
423 setintfield(L, -1, "width", input.width);
425 std::string method_s;
426 switch (input.method) {
427 case CRAFT_METHOD_NORMAL:
430 case CRAFT_METHOD_COOKING:
431 method_s = "cooking";
433 case CRAFT_METHOD_FUEL:
437 method_s = "unknown";
439 lua_pushstring(L, method_s.c_str());
440 lua_setfield(L, -2, "method");
442 // Deprecated, only for compatibility's sake
443 lua_pushstring(L, method_s.c_str());
444 lua_setfield(L, -2, "type");
446 lua_pushstring(L, output.item.c_str());
447 lua_setfield(L, -2, "output");
450 static void push_craft_recipes(lua_State *L, IGameDef *gdef,
451 const std::vector<CraftDefinition*> &recipes,
452 const CraftOutput &output)
454 lua_createtable(L, recipes.size(), 0);
456 if (recipes.empty()) {
461 std::vector<CraftDefinition*>::const_iterator it = recipes.begin();
462 for (unsigned i = 0; it != recipes.end(); ++it) {
464 push_craft_recipe(L, gdef, *it, output);
465 lua_rawseti(L, -2, ++i);
470 // get_craft_recipe(result item)
471 int ModApiCraft::l_get_craft_recipe(lua_State *L)
473 NO_MAP_LOCK_REQUIRED;
475 std::string item = luaL_checkstring(L, 1);
476 Server *server = getServer(L);
477 CraftOutput output(item, 0);
478 std::vector<CraftDefinition*> recipes = server->cdef()
479 ->getCraftRecipes(output, server, 1);
481 lua_createtable(L, 1, 0);
483 if (recipes.empty()) {
485 lua_setfield(L, -2, "items");
486 setintfield(L, -1, "width", 0);
489 push_craft_recipe(L, server, recipes[0], output);
493 // get_all_craft_recipes(result item)
494 int ModApiCraft::l_get_all_craft_recipes(lua_State *L)
496 NO_MAP_LOCK_REQUIRED;
498 std::string item = luaL_checkstring(L, 1);
499 Server *server = getServer(L);
500 CraftOutput output(item, 0);
501 std::vector<CraftDefinition*> recipes = server->cdef()
502 ->getCraftRecipes(output, server);
504 push_craft_recipes(L, server, recipes, output);
508 void ModApiCraft::Initialize(lua_State *L, int top)
510 API_FCT(get_all_craft_recipes);
511 API_FCT(get_craft_recipe);
512 API_FCT(get_craft_result);
513 API_FCT(register_craft);
514 API_FCT(clear_craft);