You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

525 lines
16 KiB

  1. /*
  2. Minetest
  3. Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include "lua_api/l_craft.h"
  17. #include "lua_api/l_internal.h"
  18. #include "lua_api/l_item.h"
  19. #include "common/c_converter.h"
  20. #include "common/c_content.h"
  21. #include "server.h"
  22. #include "craftdef.h"
  23. struct EnumString ModApiCraft::es_CraftMethod[] =
  24. {
  25. {CRAFT_METHOD_NORMAL, "normal"},
  26. {CRAFT_METHOD_COOKING, "cooking"},
  27. {CRAFT_METHOD_FUEL, "fuel"},
  28. {0, NULL},
  29. };
  30. // helper for register_craft
  31. bool ModApiCraft::readCraftRecipeShaped(lua_State *L, int index,
  32. int &width, std::vector<std::string> &recipe)
  33. {
  34. if(index < 0)
  35. index = lua_gettop(L) + 1 + index;
  36. if(!lua_istable(L, index))
  37. return false;
  38. lua_pushnil(L);
  39. int rowcount = 0;
  40. while(lua_next(L, index) != 0){
  41. int colcount = 0;
  42. // key at index -2 and value at index -1
  43. if(!lua_istable(L, -1))
  44. return false;
  45. int table2 = lua_gettop(L);
  46. lua_pushnil(L);
  47. while(lua_next(L, table2) != 0){
  48. // key at index -2 and value at index -1
  49. if(!lua_isstring(L, -1))
  50. return false;
  51. recipe.push_back(readParam<std::string>(L, -1));
  52. // removes value, keeps key for next iteration
  53. lua_pop(L, 1);
  54. colcount++;
  55. }
  56. if(rowcount == 0){
  57. width = colcount;
  58. } else {
  59. if(colcount != width)
  60. return false;
  61. }
  62. // removes value, keeps key for next iteration
  63. lua_pop(L, 1);
  64. rowcount++;
  65. }
  66. return width != 0;
  67. }
  68. // helper for register_craft
  69. bool ModApiCraft::readCraftRecipeShapeless(lua_State *L, int index,
  70. std::vector<std::string> &recipe)
  71. {
  72. if(index < 0)
  73. index = lua_gettop(L) + 1 + index;
  74. if(!lua_istable(L, index))
  75. return false;
  76. lua_pushnil(L);
  77. while(lua_next(L, index) != 0){
  78. // key at index -2 and value at index -1
  79. if(!lua_isstring(L, -1))
  80. return false;
  81. recipe.push_back(readParam<std::string>(L, -1));
  82. // removes value, keeps key for next iteration
  83. lua_pop(L, 1);
  84. }
  85. return true;
  86. }
  87. // helper for register_craft
  88. bool ModApiCraft::readCraftReplacements(lua_State *L, int index,
  89. CraftReplacements &replacements)
  90. {
  91. if(index < 0)
  92. index = lua_gettop(L) + 1 + index;
  93. if(!lua_istable(L, index))
  94. return false;
  95. lua_pushnil(L);
  96. while(lua_next(L, index) != 0){
  97. // key at index -2 and value at index -1
  98. if(!lua_istable(L, -1))
  99. return false;
  100. lua_rawgeti(L, -1, 1);
  101. if(!lua_isstring(L, -1))
  102. return false;
  103. std::string replace_from = readParam<std::string>(L, -1);
  104. lua_pop(L, 1);
  105. lua_rawgeti(L, -1, 2);
  106. if(!lua_isstring(L, -1))
  107. return false;
  108. std::string replace_to = readParam<std::string>(L, -1);
  109. lua_pop(L, 1);
  110. replacements.pairs.push_back(
  111. std::make_pair(replace_from, replace_to));
  112. // removes value, keeps key for next iteration
  113. lua_pop(L, 1);
  114. }
  115. return true;
  116. }
  117. // register_craft({output=item, recipe={{item00,item10},{item01,item11}})
  118. int ModApiCraft::l_register_craft(lua_State *L)
  119. {
  120. NO_MAP_LOCK_REQUIRED;
  121. //infostream<<"register_craft"<<std::endl;
  122. luaL_checktype(L, 1, LUA_TTABLE);
  123. int table = 1;
  124. // Get the writable craft definition manager from the server
  125. IWritableCraftDefManager *craftdef =
  126. getServer(L)->getWritableCraftDefManager();
  127. std::string type = getstringfield_default(L, table, "type", "shaped");
  128. /*
  129. CraftDefinitionShaped
  130. */
  131. if(type == "shaped"){
  132. std::string output = getstringfield_default(L, table, "output", "");
  133. if(output == "")
  134. throw LuaError("Crafting definition is missing an output");
  135. int width = 0;
  136. std::vector<std::string> recipe;
  137. lua_getfield(L, table, "recipe");
  138. if(lua_isnil(L, -1))
  139. throw LuaError("Crafting definition is missing a recipe"
  140. " (output=\"" + output + "\")");
  141. if(!readCraftRecipeShaped(L, -1, width, recipe))
  142. throw LuaError("Invalid crafting recipe"
  143. " (output=\"" + output + "\")");
  144. CraftReplacements replacements;
  145. lua_getfield(L, table, "replacements");
  146. if(!lua_isnil(L, -1))
  147. {
  148. if(!readCraftReplacements(L, -1, replacements))
  149. throw LuaError("Invalid replacements"
  150. " (output=\"" + output + "\")");
  151. }
  152. CraftDefinition *def = new CraftDefinitionShaped(
  153. output, width, recipe, replacements);
  154. craftdef->registerCraft(def, getServer(L));
  155. }
  156. /*
  157. CraftDefinitionShapeless
  158. */
  159. else if(type == "shapeless"){
  160. std::string output = getstringfield_default(L, table, "output", "");
  161. if(output == "")
  162. throw LuaError("Crafting definition (shapeless)"
  163. " is missing an output");
  164. std::vector<std::string> recipe;
  165. lua_getfield(L, table, "recipe");
  166. if(lua_isnil(L, -1))
  167. throw LuaError("Crafting definition (shapeless)"
  168. " is missing a recipe"
  169. " (output=\"" + output + "\")");
  170. if(!readCraftRecipeShapeless(L, -1, recipe))
  171. throw LuaError("Invalid crafting recipe"
  172. " (output=\"" + output + "\")");
  173. CraftReplacements replacements;
  174. lua_getfield(L, table, "replacements");
  175. if(!lua_isnil(L, -1))
  176. {
  177. if(!readCraftReplacements(L, -1, replacements))
  178. throw LuaError("Invalid replacements"
  179. " (output=\"" + output + "\")");
  180. }
  181. CraftDefinition *def = new CraftDefinitionShapeless(
  182. output, recipe, replacements);
  183. craftdef->registerCraft(def, getServer(L));
  184. }
  185. /*
  186. CraftDefinitionToolRepair
  187. */
  188. else if(type == "toolrepair"){
  189. float additional_wear = getfloatfield_default(L, table,
  190. "additional_wear", 0.0);
  191. CraftDefinition *def = new CraftDefinitionToolRepair(
  192. additional_wear);
  193. craftdef->registerCraft(def, getServer(L));
  194. }
  195. /*
  196. CraftDefinitionCooking
  197. */
  198. else if(type == "cooking"){
  199. std::string output = getstringfield_default(L, table, "output", "");
  200. if(output == "")
  201. throw LuaError("Crafting definition (cooking)"
  202. " is missing an output");
  203. std::string recipe = getstringfield_default(L, table, "recipe", "");
  204. if(recipe == "")
  205. throw LuaError("Crafting definition (cooking)"
  206. " is missing a recipe"
  207. " (output=\"" + output + "\")");
  208. float cooktime = getfloatfield_default(L, table, "cooktime", 3.0);
  209. CraftReplacements replacements;
  210. lua_getfield(L, table, "replacements");
  211. if(!lua_isnil(L, -1))
  212. {
  213. if(!readCraftReplacements(L, -1, replacements))
  214. throw LuaError("Invalid replacements"
  215. " (cooking output=\"" + output + "\")");
  216. }
  217. CraftDefinition *def = new CraftDefinitionCooking(
  218. output, recipe, cooktime, replacements);
  219. craftdef->registerCraft(def, getServer(L));
  220. }
  221. /*
  222. CraftDefinitionFuel
  223. */
  224. else if(type == "fuel"){
  225. std::string recipe = getstringfield_default(L, table, "recipe", "");
  226. if(recipe == "")
  227. throw LuaError("Crafting definition (fuel)"
  228. " is missing a recipe");
  229. float burntime = getfloatfield_default(L, table, "burntime", 1.0);
  230. CraftReplacements replacements;
  231. lua_getfield(L, table, "replacements");
  232. if(!lua_isnil(L, -1))
  233. {
  234. if(!readCraftReplacements(L, -1, replacements))
  235. throw LuaError("Invalid replacements"
  236. " (fuel recipe=\"" + recipe + "\")");
  237. }
  238. CraftDefinition *def = new CraftDefinitionFuel(
  239. recipe, burntime, replacements);
  240. craftdef->registerCraft(def, getServer(L));
  241. }
  242. else
  243. {
  244. throw LuaError("Unknown crafting definition type: \"" + type + "\"");
  245. }
  246. lua_pop(L, 1);
  247. return 0; /* number of results */
  248. }
  249. // clear_craft({[output=item], [recipe={{item00,item10},{item01,item11}}])
  250. int ModApiCraft::l_clear_craft(lua_State *L)
  251. {
  252. NO_MAP_LOCK_REQUIRED;
  253. luaL_checktype(L, 1, LUA_TTABLE);
  254. int table = 1;
  255. // Get the writable craft definition manager from the server
  256. IWritableCraftDefManager *craftdef =
  257. getServer(L)->getWritableCraftDefManager();
  258. std::string output = getstringfield_default(L, table, "output", "");
  259. std::string type = getstringfield_default(L, table, "type", "shaped");
  260. CraftOutput c_output(output, 0);
  261. if (output != "") {
  262. if (craftdef->clearCraftsByOutput (c_output, getServer(L)))
  263. return 0;
  264. else
  265. throw LuaError("No craft recipe known for output"
  266. " (output=\"" + output + "\")");
  267. }
  268. std::vector<std::string> recipe;
  269. int width = 0;
  270. CraftMethod method = CRAFT_METHOD_NORMAL;
  271. /*
  272. CraftDefinitionShaped
  273. */
  274. if (type == "shaped") {
  275. lua_getfield(L, table, "recipe");
  276. if (lua_isnil(L, -1))
  277. throw LuaError("Either output or recipe has to be defined");
  278. if (!readCraftRecipeShaped(L, -1, width, recipe))
  279. throw LuaError("Invalid crafting recipe");
  280. }
  281. /*
  282. CraftDefinitionShapeless
  283. */
  284. else if (type == "shapeless") {
  285. lua_getfield(L, table, "recipe");
  286. if (lua_isnil(L, -1))
  287. throw LuaError("Either output or recipe has to be defined");
  288. if (!readCraftRecipeShapeless(L, -1, recipe))
  289. throw LuaError("Invalid crafting recipe");
  290. }
  291. /*
  292. CraftDefinitionCooking
  293. */
  294. else if (type == "cooking") {
  295. method = CRAFT_METHOD_COOKING;
  296. std::string rec = getstringfield_default(L, table, "recipe", "");
  297. if (rec == "")
  298. throw LuaError("Crafting definition (cooking)"
  299. " is missing a recipe");
  300. recipe.push_back(rec);
  301. }
  302. /*
  303. CraftDefinitionFuel
  304. */
  305. else if (type == "fuel") {
  306. method = CRAFT_METHOD_FUEL;
  307. std::string rec = getstringfield_default(L, table, "recipe", "");
  308. if (rec == "")
  309. throw LuaError("Crafting definition (fuel)"
  310. " is missing a recipe");
  311. recipe.push_back(rec);
  312. } else {
  313. throw LuaError("Unknown crafting definition type: \"" + type + "\"");
  314. }
  315. std::vector<ItemStack> items;
  316. items.reserve(recipe.size());
  317. for (const auto &item : recipe)
  318. items.emplace_back(item, 1, 0, getServer(L)->idef());
  319. CraftInput input(method, width, items);
  320. if (!craftdef->clearCraftsByInput(input, getServer(L)))
  321. throw LuaError("No crafting specified for input");
  322. lua_pop(L, 1);
  323. return 0;
  324. }
  325. // get_craft_result(input)
  326. int ModApiCraft::l_get_craft_result(lua_State *L)
  327. {
  328. NO_MAP_LOCK_REQUIRED;
  329. int input_i = 1;
  330. std::string method_s = getstringfield_default(L, input_i, "method", "normal");
  331. enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method",
  332. es_CraftMethod, CRAFT_METHOD_NORMAL);
  333. int width = 1;
  334. lua_getfield(L, input_i, "width");
  335. if(lua_isnumber(L, -1))
  336. width = luaL_checkinteger(L, -1);
  337. lua_pop(L, 1);
  338. lua_getfield(L, input_i, "items");
  339. std::vector<ItemStack> items = read_items(L, -1,getServer(L));
  340. lua_pop(L, 1); // items
  341. IGameDef *gdef = getServer(L);
  342. ICraftDefManager *cdef = gdef->cdef();
  343. CraftInput input(method, width, items);
  344. CraftOutput output;
  345. std::vector<ItemStack> output_replacements;
  346. bool got = cdef->getCraftResult(input, output, output_replacements, true, gdef);
  347. lua_newtable(L); // output table
  348. if (got) {
  349. ItemStack item;
  350. item.deSerialize(output.item, gdef->idef());
  351. LuaItemStack::create(L, item);
  352. lua_setfield(L, -2, "item");
  353. setintfield(L, -1, "time", output.time);
  354. push_items(L, output_replacements);
  355. lua_setfield(L, -2, "replacements");
  356. } else {
  357. LuaItemStack::create(L, ItemStack());
  358. lua_setfield(L, -2, "item");
  359. setintfield(L, -1, "time", 0);
  360. lua_newtable(L);
  361. lua_setfield(L, -2, "replacements");
  362. }
  363. lua_newtable(L); // decremented input table
  364. lua_pushstring(L, method_s.c_str());
  365. lua_setfield(L, -2, "method");
  366. lua_pushinteger(L, width);
  367. lua_setfield(L, -2, "width");
  368. push_items(L, input.items);
  369. lua_setfield(L, -2, "items");
  370. return 2;
  371. }
  372. static void push_craft_recipe(lua_State *L, IGameDef *gdef,
  373. const CraftDefinition *recipe,
  374. const CraftOutput &tmpout)
  375. {
  376. CraftInput input = recipe->getInput(tmpout, gdef);
  377. CraftOutput output = recipe->getOutput(input, gdef);
  378. lua_newtable(L); // items
  379. std::vector<ItemStack>::const_iterator iter = input.items.begin();
  380. for (u16 j = 1; iter != input.items.end(); ++iter, j++) {
  381. if (iter->empty())
  382. continue;
  383. lua_pushstring(L, iter->name.c_str());
  384. lua_rawseti(L, -2, j);
  385. }
  386. lua_setfield(L, -2, "items");
  387. setintfield(L, -1, "width", input.width);
  388. std::string method_s;
  389. switch (input.method) {
  390. case CRAFT_METHOD_NORMAL:
  391. method_s = "normal";
  392. break;
  393. case CRAFT_METHOD_COOKING:
  394. method_s = "cooking";
  395. break;
  396. case CRAFT_METHOD_FUEL:
  397. method_s = "fuel";
  398. break;
  399. default:
  400. method_s = "unknown";
  401. }
  402. lua_pushstring(L, method_s.c_str());
  403. lua_setfield(L, -2, "method");
  404. // Deprecated, only for compatibility's sake
  405. lua_pushstring(L, method_s.c_str());
  406. lua_setfield(L, -2, "type");
  407. lua_pushstring(L, output.item.c_str());
  408. lua_setfield(L, -2, "output");
  409. }
  410. static void push_craft_recipes(lua_State *L, IGameDef *gdef,
  411. const std::vector<CraftDefinition*> &recipes,
  412. const CraftOutput &output)
  413. {
  414. lua_createtable(L, recipes.size(), 0);
  415. if (recipes.empty()) {
  416. lua_pushnil(L);
  417. return;
  418. }
  419. std::vector<CraftDefinition*>::const_iterator it = recipes.begin();
  420. for (unsigned i = 0; it != recipes.end(); ++it) {
  421. lua_newtable(L);
  422. push_craft_recipe(L, gdef, *it, output);
  423. lua_rawseti(L, -2, ++i);
  424. }
  425. }
  426. // get_craft_recipe(result item)
  427. int ModApiCraft::l_get_craft_recipe(lua_State *L)
  428. {
  429. NO_MAP_LOCK_REQUIRED;
  430. std::string item = luaL_checkstring(L, 1);
  431. Server *server = getServer(L);
  432. CraftOutput output(item, 0);
  433. std::vector<CraftDefinition*> recipes = server->cdef()
  434. ->getCraftRecipes(output, server, 1);
  435. lua_createtable(L, 1, 0);
  436. if (recipes.empty()) {
  437. lua_pushnil(L);
  438. lua_setfield(L, -2, "items");
  439. setintfield(L, -1, "width", 0);
  440. return 1;
  441. }
  442. push_craft_recipe(L, server, recipes[0], output);
  443. return 1;
  444. }
  445. // get_all_craft_recipes(result item)
  446. int ModApiCraft::l_get_all_craft_recipes(lua_State *L)
  447. {
  448. NO_MAP_LOCK_REQUIRED;
  449. std::string item = luaL_checkstring(L, 1);
  450. Server *server = getServer(L);
  451. CraftOutput output(item, 0);
  452. std::vector<CraftDefinition*> recipes = server->cdef()
  453. ->getCraftRecipes(output, server);
  454. push_craft_recipes(L, server, recipes, output);
  455. return 1;
  456. }
  457. void ModApiCraft::Initialize(lua_State *L, int top)
  458. {
  459. API_FCT(get_all_craft_recipes);
  460. API_FCT(get_craft_recipe);
  461. API_FCT(get_craft_result);
  462. API_FCT(register_craft);
  463. API_FCT(clear_craft);
  464. }