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