]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/lua_api/l_inventory.cpp
Add ItemStack:get_description() to get tooltip (#8847)
[dragonfireclient.git] / src / script / lua_api / l_inventory.cpp
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 #include "lua_api/l_inventory.h"
21 #include "lua_api/l_internal.h"
22 #include "lua_api/l_item.h"
23 #include "common/c_converter.h"
24 #include "common/c_content.h"
25 #include "server.h"
26 #include "remoteplayer.h"
27
28 /*
29         InvRef
30 */
31 InvRef* InvRef::checkobject(lua_State *L, int narg)
32 {
33         luaL_checktype(L, narg, LUA_TUSERDATA);
34         void *ud = luaL_checkudata(L, narg, className);
35         if(!ud) luaL_typerror(L, narg, className);
36         return *(InvRef**)ud;  // unbox pointer
37 }
38
39 Inventory* InvRef::getinv(lua_State *L, InvRef *ref)
40 {
41         return getServer(L)->getInventory(ref->m_loc);
42 }
43
44 InventoryList* InvRef::getlist(lua_State *L, InvRef *ref,
45                 const char *listname)
46 {
47         NO_MAP_LOCK_REQUIRED;
48         Inventory *inv = getinv(L, ref);
49         if(!inv)
50                 return NULL;
51         return inv->getList(listname);
52 }
53
54 void InvRef::reportInventoryChange(lua_State *L, InvRef *ref)
55 {
56         // Inform other things that the inventory has changed
57         getServer(L)->setInventoryModified(ref->m_loc);
58 }
59
60 // Exported functions
61
62 // garbage collector
63 int InvRef::gc_object(lua_State *L) {
64         InvRef *o = *(InvRef **)(lua_touserdata(L, 1));
65         delete o;
66         return 0;
67 }
68
69 // is_empty(self, listname) -> true/false
70 int InvRef::l_is_empty(lua_State *L)
71 {
72         NO_MAP_LOCK_REQUIRED;
73         InvRef *ref = checkobject(L, 1);
74         const char *listname = luaL_checkstring(L, 2);
75         InventoryList *list = getlist(L, ref, listname);
76         if(list && list->getUsedSlots() > 0){
77                 lua_pushboolean(L, false);
78         } else {
79                 lua_pushboolean(L, true);
80         }
81         return 1;
82 }
83
84 // get_size(self, listname)
85 int InvRef::l_get_size(lua_State *L)
86 {
87         NO_MAP_LOCK_REQUIRED;
88         InvRef *ref = checkobject(L, 1);
89         const char *listname = luaL_checkstring(L, 2);
90         InventoryList *list = getlist(L, ref, listname);
91         if(list){
92                 lua_pushinteger(L, list->getSize());
93         } else {
94                 lua_pushinteger(L, 0);
95         }
96         return 1;
97 }
98
99 // get_width(self, listname)
100 int InvRef::l_get_width(lua_State *L)
101 {
102         NO_MAP_LOCK_REQUIRED;
103         InvRef *ref = checkobject(L, 1);
104         const char *listname = luaL_checkstring(L, 2);
105         InventoryList *list = getlist(L, ref, listname);
106         if(list){
107                 lua_pushinteger(L, list->getWidth());
108         } else {
109                 lua_pushinteger(L, 0);
110         }
111         return 1;
112 }
113
114 // set_size(self, listname, size)
115 int InvRef::l_set_size(lua_State *L)
116 {
117         NO_MAP_LOCK_REQUIRED;
118         InvRef *ref = checkobject(L, 1);
119         const char *listname = luaL_checkstring(L, 2);
120
121         int newsize = luaL_checknumber(L, 3);
122         if (newsize < 0) {
123                 lua_pushboolean(L, false);
124                 return 1;
125         }
126
127         Inventory *inv = getinv(L, ref);
128         if(inv == NULL){
129                 lua_pushboolean(L, false);
130                 return 1;
131         }
132         if(newsize == 0){
133                 inv->deleteList(listname);
134                 reportInventoryChange(L, ref);
135                 lua_pushboolean(L, true);
136                 return 1;
137         }
138         InventoryList *list = inv->getList(listname);
139         if(list){
140                 list->setSize(newsize);
141         } else {
142                 list = inv->addList(listname, newsize);
143                 if (!list)
144                 {
145                         lua_pushboolean(L, false);
146                         return 1;
147                 }
148         }
149         reportInventoryChange(L, ref);
150         lua_pushboolean(L, true);
151         return 1;
152 }
153
154 // set_width(self, listname, size)
155 int InvRef::l_set_width(lua_State *L)
156 {
157         NO_MAP_LOCK_REQUIRED;
158         InvRef *ref = checkobject(L, 1);
159         const char *listname = luaL_checkstring(L, 2);
160         int newwidth = luaL_checknumber(L, 3);
161         Inventory *inv = getinv(L, ref);
162         if(inv == NULL){
163                 return 0;
164         }
165         InventoryList *list = inv->getList(listname);
166         if(list){
167                 list->setWidth(newwidth);
168         } else {
169                 return 0;
170         }
171         reportInventoryChange(L, ref);
172         return 0;
173 }
174
175 // get_stack(self, listname, i) -> itemstack
176 int InvRef::l_get_stack(lua_State *L)
177 {
178         NO_MAP_LOCK_REQUIRED;
179         InvRef *ref = checkobject(L, 1);
180         const char *listname = luaL_checkstring(L, 2);
181         int i = luaL_checknumber(L, 3) - 1;
182         InventoryList *list = getlist(L, ref, listname);
183         ItemStack item;
184         if(list != NULL && i >= 0 && i < (int) list->getSize())
185                 item = list->getItem(i);
186         LuaItemStack::create(L, item);
187         return 1;
188 }
189
190 // set_stack(self, listname, i, stack) -> true/false
191 int InvRef::l_set_stack(lua_State *L)
192 {
193         NO_MAP_LOCK_REQUIRED;
194         InvRef *ref = checkobject(L, 1);
195         const char *listname = luaL_checkstring(L, 2);
196         int i = luaL_checknumber(L, 3) - 1;
197         ItemStack newitem = read_item(L, 4, getServer(L)->idef());
198         InventoryList *list = getlist(L, ref, listname);
199         if(list != NULL && i >= 0 && i < (int) list->getSize()){
200                 list->changeItem(i, newitem);
201                 reportInventoryChange(L, ref);
202                 lua_pushboolean(L, true);
203         } else {
204                 lua_pushboolean(L, false);
205         }
206         return 1;
207 }
208
209 // get_list(self, listname) -> list or nil
210 int InvRef::l_get_list(lua_State *L)
211 {
212         NO_MAP_LOCK_REQUIRED;
213         InvRef *ref = checkobject(L, 1);
214         const char *listname = luaL_checkstring(L, 2);
215         Inventory *inv = getinv(L, ref);
216         if(inv){
217                 push_inventory_list(L, inv, listname);
218         } else {
219                 lua_pushnil(L);
220         }
221         return 1;
222 }
223
224 // set_list(self, listname, list)
225 int InvRef::l_set_list(lua_State *L)
226 {
227         NO_MAP_LOCK_REQUIRED;
228         InvRef *ref = checkobject(L, 1);
229         const char *listname = luaL_checkstring(L, 2);
230         Inventory *inv = getinv(L, ref);
231         if(inv == NULL){
232                 return 0;
233         }
234         InventoryList *list = inv->getList(listname);
235         if(list)
236                 read_inventory_list(L, 3, inv, listname,
237                                 getServer(L), list->getSize());
238         else
239                 read_inventory_list(L, 3, inv, listname, getServer(L));
240         reportInventoryChange(L, ref);
241         return 0;
242 }
243
244 // get_lists(self) -> list of InventoryLists
245 int InvRef::l_get_lists(lua_State *L)
246 {
247         NO_MAP_LOCK_REQUIRED;
248         InvRef *ref = checkobject(L, 1);
249         Inventory *inv = getinv(L, ref);
250         if (!inv) {
251                 return 0;
252         }
253         std::vector<const InventoryList*> lists = inv->getLists();
254         std::vector<const InventoryList*>::iterator iter = lists.begin();
255         lua_createtable(L, 0, lists.size());
256         for (; iter != lists.end(); iter++) {
257                 const char* name = (*iter)->getName().c_str();
258                 lua_pushstring(L, name);
259                 push_inventory_list(L, inv, name);
260                 lua_rawset(L, -3);
261         }
262         return 1;
263 }
264
265 // set_lists(self, lists)
266 int InvRef::l_set_lists(lua_State *L)
267 {
268         NO_MAP_LOCK_REQUIRED;
269         InvRef *ref = checkobject(L, 1);
270         Inventory *inv = getinv(L, ref);
271         if (!inv) {
272                 return 0;
273         }
274
275         // Make a temporary inventory in case reading fails
276         Inventory *tempInv(inv);
277         tempInv->clear();
278
279         Server *server = getServer(L);
280
281         lua_pushnil(L);
282         while (lua_next(L, 2)) {
283                 const char *listname = lua_tostring(L, -2);
284                 read_inventory_list(L, -1, tempInv, listname, server);
285                 lua_pop(L, 1);
286         }
287         inv = tempInv;
288         return 0;
289 }
290
291 // add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
292 // Returns the leftover stack
293 int InvRef::l_add_item(lua_State *L)
294 {
295         NO_MAP_LOCK_REQUIRED;
296         InvRef *ref = checkobject(L, 1);
297         const char *listname = luaL_checkstring(L, 2);
298         ItemStack item = read_item(L, 3, getServer(L)->idef());
299         InventoryList *list = getlist(L, ref, listname);
300         if(list){
301                 ItemStack leftover = list->addItem(item);
302                 if(leftover.count != item.count)
303                         reportInventoryChange(L, ref);
304                 LuaItemStack::create(L, leftover);
305         } else {
306                 LuaItemStack::create(L, item);
307         }
308         return 1;
309 }
310
311 // room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false
312 // Returns true if the item completely fits into the list
313 int InvRef::l_room_for_item(lua_State *L)
314 {
315         NO_MAP_LOCK_REQUIRED;
316         InvRef *ref = checkobject(L, 1);
317         const char *listname = luaL_checkstring(L, 2);
318         ItemStack item = read_item(L, 3, getServer(L)->idef());
319         InventoryList *list = getlist(L, ref, listname);
320         if(list){
321                 lua_pushboolean(L, list->roomForItem(item));
322         } else {
323                 lua_pushboolean(L, false);
324         }
325         return 1;
326 }
327
328 // contains_item(self, listname, itemstack or itemstring or table or nil, [match_meta]) -> true/false
329 // Returns true if the list contains the given count of the given item
330 int InvRef::l_contains_item(lua_State *L)
331 {
332         NO_MAP_LOCK_REQUIRED;
333         InvRef *ref = checkobject(L, 1);
334         const char *listname = luaL_checkstring(L, 2);
335         ItemStack item = read_item(L, 3, getServer(L)->idef());
336         InventoryList *list = getlist(L, ref, listname);
337         bool match_meta = false;
338         if (lua_isboolean(L, 4))
339                 match_meta = readParam<bool>(L, 4);
340         if (list) {
341                 lua_pushboolean(L, list->containsItem(item, match_meta));
342         } else {
343                 lua_pushboolean(L, false);
344         }
345         return 1;
346 }
347
348 // remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
349 // Returns the items that were actually removed
350 int InvRef::l_remove_item(lua_State *L)
351 {
352         NO_MAP_LOCK_REQUIRED;
353         InvRef *ref = checkobject(L, 1);
354         const char *listname = luaL_checkstring(L, 2);
355         ItemStack item = read_item(L, 3, getServer(L)->idef());
356         InventoryList *list = getlist(L, ref, listname);
357         if(list){
358                 ItemStack removed = list->removeItem(item);
359                 if(!removed.empty())
360                         reportInventoryChange(L, ref);
361                 LuaItemStack::create(L, removed);
362         } else {
363                 LuaItemStack::create(L, ItemStack());
364         }
365         return 1;
366 }
367
368 // get_location() -> location (like get_inventory(location))
369 int InvRef::l_get_location(lua_State *L)
370 {
371         NO_MAP_LOCK_REQUIRED;
372         InvRef *ref = checkobject(L, 1);
373         const InventoryLocation &loc = ref->m_loc;
374         switch(loc.type){
375         case InventoryLocation::PLAYER:
376                 lua_newtable(L);
377                 lua_pushstring(L, "player");
378                 lua_setfield(L, -2, "type");
379                 lua_pushstring(L, loc.name.c_str());
380                 lua_setfield(L, -2, "name");
381                 return 1;
382         case InventoryLocation::NODEMETA:
383                 lua_newtable(L);
384                 lua_pushstring(L, "node");
385                 lua_setfield(L, -2, "type");
386                 push_v3s16(L, loc.p);
387                 lua_setfield(L, -2, "pos");
388                 return 1;
389         case InventoryLocation::DETACHED:
390                 lua_newtable(L);
391                 lua_pushstring(L, "detached");
392                 lua_setfield(L, -2, "type");
393                 lua_pushstring(L, loc.name.c_str());
394                 lua_setfield(L, -2, "name");
395                 return 1;
396         case InventoryLocation::UNDEFINED:
397         case InventoryLocation::CURRENT_PLAYER:
398                 break;
399         }
400         lua_newtable(L);
401         lua_pushstring(L, "undefined");
402         lua_setfield(L, -2, "type");
403         return 1;
404 }
405
406
407 InvRef::InvRef(const InventoryLocation &loc):
408         m_loc(loc)
409 {
410 }
411
412 // Creates an InvRef and leaves it on top of stack
413 // Not callable from Lua; all references are created on the C side.
414 void InvRef::create(lua_State *L, const InventoryLocation &loc)
415 {
416         NO_MAP_LOCK_REQUIRED;
417         InvRef *o = new InvRef(loc);
418         *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
419         luaL_getmetatable(L, className);
420         lua_setmetatable(L, -2);
421 }
422 void InvRef::createPlayer(lua_State *L, RemotePlayer *player)
423 {
424         NO_MAP_LOCK_REQUIRED;
425         InventoryLocation loc;
426         loc.setPlayer(player->getName());
427         create(L, loc);
428 }
429 void InvRef::createNodeMeta(lua_State *L, v3s16 p)
430 {
431         InventoryLocation loc;
432         loc.setNodeMeta(p);
433         create(L, loc);
434 }
435
436 void InvRef::Register(lua_State *L)
437 {
438         lua_newtable(L);
439         int methodtable = lua_gettop(L);
440         luaL_newmetatable(L, className);
441         int metatable = lua_gettop(L);
442
443         lua_pushliteral(L, "__metatable");
444         lua_pushvalue(L, methodtable);
445         lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
446
447         lua_pushliteral(L, "__index");
448         lua_pushvalue(L, methodtable);
449         lua_settable(L, metatable);
450
451         lua_pushliteral(L, "__gc");
452         lua_pushcfunction(L, gc_object);
453         lua_settable(L, metatable);
454
455         lua_pop(L, 1);  // drop metatable
456
457         luaL_openlib(L, 0, methods, 0);  // fill methodtable
458         lua_pop(L, 1);  // drop methodtable
459
460         // Cannot be created from Lua
461         //lua_register(L, className, create_object);
462 }
463
464 const char InvRef::className[] = "InvRef";
465 const luaL_Reg InvRef::methods[] = {
466         luamethod(InvRef, is_empty),
467         luamethod(InvRef, get_size),
468         luamethod(InvRef, set_size),
469         luamethod(InvRef, get_width),
470         luamethod(InvRef, set_width),
471         luamethod(InvRef, get_stack),
472         luamethod(InvRef, set_stack),
473         luamethod(InvRef, get_list),
474         luamethod(InvRef, set_list),
475         luamethod(InvRef, get_lists),
476         luamethod(InvRef, set_lists),
477         luamethod(InvRef, add_item),
478         luamethod(InvRef, room_for_item),
479         luamethod(InvRef, contains_item),
480         luamethod(InvRef, remove_item),
481         luamethod(InvRef, get_location),
482         {0,0}
483 };
484
485 // get_inventory(location)
486 int ModApiInventory::l_get_inventory(lua_State *L)
487 {
488         InventoryLocation loc;
489
490         std::string type = checkstringfield(L, 1, "type");
491
492         if(type == "node"){
493                 MAP_LOCK_REQUIRED;
494                 lua_getfield(L, 1, "pos");
495                 v3s16 pos = check_v3s16(L, -1);
496                 loc.setNodeMeta(pos);
497
498                 if (getServer(L)->getInventory(loc) != NULL)
499                         InvRef::create(L, loc);
500                 else
501                         lua_pushnil(L);
502                 return 1;
503         }
504
505         NO_MAP_LOCK_REQUIRED;
506         if (type == "player") {
507                 std::string name = checkstringfield(L, 1, "name");
508                 loc.setPlayer(name);
509         } else if (type == "detached") {
510                 std::string name = checkstringfield(L, 1, "name");
511                 loc.setDetached(name);
512         }
513
514         if (getServer(L)->getInventory(loc) != NULL)
515                 InvRef::create(L, loc);
516         else
517                 lua_pushnil(L);
518         return 1;
519         // END NO_MAP_LOCK_REQUIRED;
520
521 }
522
523 // create_detached_inventory_raw(name, [player_name])
524 int ModApiInventory::l_create_detached_inventory_raw(lua_State *L)
525 {
526         NO_MAP_LOCK_REQUIRED;
527         const char *name = luaL_checkstring(L, 1);
528         std::string player = readParam<std::string>(L, 2, "");
529         if (getServer(L)->createDetachedInventory(name, player) != NULL) {
530                 InventoryLocation loc;
531                 loc.setDetached(name);
532                 InvRef::create(L, loc);
533         } else {
534                 lua_pushnil(L);
535         }
536         return 1;
537 }
538
539 // remove_detached_inventory_raw(name)
540 int ModApiInventory::l_remove_detached_inventory_raw(lua_State *L)
541 {
542         NO_MAP_LOCK_REQUIRED;
543         const std::string &name = luaL_checkstring(L, 1);
544         lua_pushboolean(L, getServer(L)->removeDetachedInventory(name));
545         return 1;
546 }
547
548 void ModApiInventory::Initialize(lua_State *L, int top)
549 {
550         API_FCT(create_detached_inventory_raw);
551         API_FCT(remove_detached_inventory_raw);
552         API_FCT(get_inventory);
553 }