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.push_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.push_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.push_back(
126 std::make_pair(replace_from, replace_to));
127 // removes value, keeps key for next iteration
132 // register_craft({output=item, recipe={{item00,item10},{item01,item11}})
133 int ModApiCraft::l_register_craft(lua_State *L)
135 NO_MAP_LOCK_REQUIRED;
136 //infostream<<"register_craft"<<std::endl;
137 luaL_checktype(L, 1, LUA_TTABLE);
140 // Get the writable craft definition manager from the server
141 IWritableCraftDefManager *craftdef =
142 getServer(L)->getWritableCraftDefManager();
144 std::string type = getstringfield_default(L, table, "type", "shaped");
147 CraftDefinitionShaped
149 if(type == "shaped"){
150 std::string output = getstringfield_default(L, table, "output", "");
152 throw LuaError("Crafting definition is missing an output");
155 std::vector<std::string> recipe;
156 lua_getfield(L, table, "recipe");
158 throw LuaError("Crafting definition is missing a recipe"
159 " (output=\"" + output + "\")");
160 if(!readCraftRecipeShaped(L, -1, width, recipe))
161 throw LuaError("Invalid crafting recipe"
162 " (output=\"" + output + "\")");
164 CraftReplacements replacements;
165 lua_getfield(L, table, "replacements");
166 if(!lua_isnil(L, -1))
168 if(!readCraftReplacements(L, -1, replacements))
169 throw LuaError("Invalid replacements"
170 " (output=\"" + output + "\")");
173 CraftDefinition *def = new CraftDefinitionShaped(
174 output, width, recipe, replacements);
175 craftdef->registerCraft(def, getServer(L));
178 CraftDefinitionShapeless
180 else if(type == "shapeless"){
181 std::string output = getstringfield_default(L, table, "output", "");
183 throw LuaError("Crafting definition (shapeless)"
184 " is missing an output");
186 std::vector<std::string> recipe;
187 lua_getfield(L, table, "recipe");
189 throw LuaError("Crafting definition (shapeless)"
190 " is missing a recipe"
191 " (output=\"" + output + "\")");
192 if(!readCraftRecipeShapeless(L, -1, recipe))
193 throw LuaError("Invalid crafting recipe"
194 " (output=\"" + output + "\")");
196 CraftReplacements replacements;
197 lua_getfield(L, table, "replacements");
198 if(!lua_isnil(L, -1))
200 if(!readCraftReplacements(L, -1, replacements))
201 throw LuaError("Invalid replacements"
202 " (output=\"" + output + "\")");
205 CraftDefinition *def = new CraftDefinitionShapeless(
206 output, recipe, replacements);
207 craftdef->registerCraft(def, getServer(L));
210 CraftDefinitionToolRepair
212 else if(type == "toolrepair"){
213 float additional_wear = getfloatfield_default(L, table,
214 "additional_wear", 0.0);
216 CraftDefinition *def = new CraftDefinitionToolRepair(
218 craftdef->registerCraft(def, getServer(L));
221 CraftDefinitionCooking
223 else if(type == "cooking"){
224 std::string output = getstringfield_default(L, table, "output", "");
226 throw LuaError("Crafting definition (cooking)"
227 " is missing an output");
229 std::string recipe = getstringfield_default(L, table, "recipe", "");
231 throw LuaError("Crafting definition (cooking)"
232 " is missing a recipe"
233 " (output=\"" + output + "\")");
235 float cooktime = getfloatfield_default(L, table, "cooktime", 3.0);
237 CraftReplacements replacements;
238 lua_getfield(L, table, "replacements");
239 if(!lua_isnil(L, -1))
241 if(!readCraftReplacements(L, -1, replacements))
242 throw LuaError("Invalid replacements"
243 " (cooking output=\"" + output + "\")");
246 CraftDefinition *def = new CraftDefinitionCooking(
247 output, recipe, cooktime, replacements);
248 craftdef->registerCraft(def, getServer(L));
253 else if(type == "fuel"){
254 std::string recipe = getstringfield_default(L, table, "recipe", "");
256 throw LuaError("Crafting definition (fuel)"
257 " is missing a recipe");
259 float burntime = getfloatfield_default(L, table, "burntime", 1.0);
261 CraftReplacements replacements;
262 lua_getfield(L, table, "replacements");
263 if(!lua_isnil(L, -1))
265 if(!readCraftReplacements(L, -1, replacements))
266 throw LuaError("Invalid replacements"
267 " (fuel recipe=\"" + recipe + "\")");
270 CraftDefinition *def = new CraftDefinitionFuel(
271 recipe, burntime, replacements);
272 craftdef->registerCraft(def, getServer(L));
276 throw LuaError("Unknown crafting definition type: \"" + type + "\"");
280 return 0; /* number of results */
283 // clear_craft({[output=item], [recipe={{item00,item10},{item01,item11}}])
284 int ModApiCraft::l_clear_craft(lua_State *L)
286 NO_MAP_LOCK_REQUIRED;
287 luaL_checktype(L, 1, LUA_TTABLE);
290 // Get the writable craft definition manager from the server
291 IWritableCraftDefManager *craftdef =
292 getServer(L)->getWritableCraftDefManager();
294 std::string output = getstringfield_default(L, table, "output", "");
295 std::string type = getstringfield_default(L, table, "type", "shaped");
296 CraftOutput c_output(output, 0);
298 if (craftdef->clearCraftRecipesByOutput(c_output, getServer(L)))
301 throw LuaError("No craft recipe known for output"
302 " (output=\"" + output + "\")");
304 std::vector<std::string> recipe;
306 CraftMethod method = CRAFT_METHOD_NORMAL;
308 CraftDefinitionShaped
310 if (type == "shaped") {
311 lua_getfield(L, table, "recipe");
312 if (lua_isnil(L, -1))
313 throw LuaError("Either output or recipe has to be defined");
314 if (!readCraftRecipeShaped(L, -1, width, recipe))
315 throw LuaError("Invalid crafting recipe");
318 CraftDefinitionShapeless
320 else if (type == "shapeless") {
321 lua_getfield(L, table, "recipe");
322 if (lua_isnil(L, -1))
323 throw LuaError("Either output or recipe has to be defined");
324 if (!readCraftRecipeShapeless(L, -1, recipe))
325 throw LuaError("Invalid crafting recipe");
328 CraftDefinitionCooking
330 else if (type == "cooking") {
331 method = CRAFT_METHOD_COOKING;
332 std::string rec = getstringfield_default(L, table, "recipe", "");
334 throw LuaError("Crafting definition (cooking)"
335 " is missing a recipe");
336 recipe.push_back(rec);
341 else if (type == "fuel") {
342 method = CRAFT_METHOD_FUEL;
343 std::string rec = getstringfield_default(L, table, "recipe", "");
345 throw LuaError("Crafting definition (fuel)"
346 " is missing a recipe");
347 recipe.push_back(rec);
349 throw LuaError("Unknown crafting definition type: \"" + type + "\"");
351 if (!craftdef->clearCraftRecipesByInput(method, width, recipe, getServer(L)))
352 throw LuaError("No crafting specified for input");
357 // get_craft_result(input)
358 int ModApiCraft::l_get_craft_result(lua_State *L)
360 NO_MAP_LOCK_REQUIRED;
363 std::string method_s = getstringfield_default(L, input_i, "method", "normal");
364 enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method",
365 es_CraftMethod, CRAFT_METHOD_NORMAL);
367 lua_getfield(L, input_i, "width");
368 if(lua_isnumber(L, -1))
369 width = luaL_checkinteger(L, -1);
371 lua_getfield(L, input_i, "items");
372 std::vector<ItemStack> items = read_items(L, -1,getServer(L));
373 lua_pop(L, 1); // items
375 IGameDef *gdef = getServer(L);
376 ICraftDefManager *cdef = gdef->cdef();
377 CraftInput input(method, width, items);
379 std::vector<ItemStack> output_replacements;
380 bool got = cdef->getCraftResult(input, output, output_replacements, true, gdef);
381 lua_newtable(L); // output table
384 item.deSerialize(output.item, gdef->idef());
385 LuaItemStack::create(L, item);
386 lua_setfield(L, -2, "item");
387 setintfield(L, -1, "time", output.time);
388 push_items(L, output_replacements);
389 lua_setfield(L, -2, "replacements");
391 LuaItemStack::create(L, ItemStack());
392 lua_setfield(L, -2, "item");
393 setintfield(L, -1, "time", 0);
395 lua_setfield(L, -2, "replacements");
397 lua_newtable(L); // decremented input table
398 lua_pushstring(L, method_s.c_str());
399 lua_setfield(L, -2, "method");
400 lua_pushinteger(L, width);
401 lua_setfield(L, -2, "width");
402 push_items(L, input.items);
403 lua_setfield(L, -2, "items");
408 static void push_craft_recipe(lua_State *L, IGameDef *gdef,
409 const CraftDefinition *recipe,
410 const CraftOutput &tmpout)
412 CraftInput input = recipe->getInput(tmpout, gdef);
413 CraftOutput output = recipe->getOutput(input, gdef);
415 lua_newtable(L); // items
416 std::vector<ItemStack>::const_iterator iter = input.items.begin();
417 for (u16 j = 1; iter != input.items.end(); iter++, j++) {
420 lua_pushstring(L, iter->name.c_str());
421 lua_rawseti(L, -2, j);
423 lua_setfield(L, -2, "items");
424 setintfield(L, -1, "width", input.width);
425 switch (input.method) {
426 case CRAFT_METHOD_NORMAL:
427 lua_pushstring(L, "normal");
429 case CRAFT_METHOD_COOKING:
430 lua_pushstring(L, "cooking");
432 case CRAFT_METHOD_FUEL:
433 lua_pushstring(L, "fuel");
436 lua_pushstring(L, "unknown");
438 lua_setfield(L, -2, "type");
439 lua_pushstring(L, output.item.c_str());
440 lua_setfield(L, -2, "output");
443 static void push_craft_recipes(lua_State *L, IGameDef *gdef,
444 const std::vector<CraftDefinition*> &recipes,
445 const CraftOutput &output)
447 lua_createtable(L, recipes.size(), 0);
449 if (recipes.empty()) {
454 std::vector<CraftDefinition*>::const_iterator it = recipes.begin();
455 for (unsigned i = 0; it != recipes.end(); ++it) {
457 push_craft_recipe(L, gdef, *it, output);
458 lua_rawseti(L, -2, ++i);
463 // get_craft_recipe(result item)
464 int ModApiCraft::l_get_craft_recipe(lua_State *L)
466 NO_MAP_LOCK_REQUIRED;
468 std::string item = luaL_checkstring(L, 1);
469 Server *server = getServer(L);
470 CraftOutput output(item, 0);
471 std::vector<CraftDefinition*> recipes = server->cdef()
472 ->getCraftRecipes(output, server, 1);
474 lua_createtable(L, 1, 0);
476 if (recipes.empty()) {
478 lua_setfield(L, -2, "items");
479 setintfield(L, -1, "width", 0);
482 push_craft_recipe(L, server, recipes[0], output);
486 // get_all_craft_recipes(result item)
487 int ModApiCraft::l_get_all_craft_recipes(lua_State *L)
489 NO_MAP_LOCK_REQUIRED;
491 std::string item = luaL_checkstring(L, 1);
492 Server *server = getServer(L);
493 CraftOutput output(item, 0);
494 std::vector<CraftDefinition*> recipes = server->cdef()
495 ->getCraftRecipes(output, server);
497 push_craft_recipes(L, server, recipes, output);
501 void ModApiCraft::Initialize(lua_State *L, int top)
503 API_FCT(get_all_craft_recipes);
504 API_FCT(get_craft_recipe);
505 API_FCT(get_craft_result);
506 API_FCT(register_craft);
507 API_FCT(clear_craft);