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