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