]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/lua_api/l_inventory.cpp
Clang-format styles fixes since previous commit
[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 "player.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) -> true/false
329 // Returns true if the list contains the given count of the given item name
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         if(list){
338                 lua_pushboolean(L, list->containsItem(item));
339         } else {
340                 lua_pushboolean(L, false);
341         }
342         return 1;
343 }
344
345 // remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
346 // Returns the items that were actually removed
347 int InvRef::l_remove_item(lua_State *L)
348 {
349         NO_MAP_LOCK_REQUIRED;
350         InvRef *ref = checkobject(L, 1);
351         const char *listname = luaL_checkstring(L, 2);
352         ItemStack item = read_item(L, 3, getServer(L)->idef());
353         InventoryList *list = getlist(L, ref, listname);
354         if(list){
355                 ItemStack removed = list->removeItem(item);
356                 if(!removed.empty())
357                         reportInventoryChange(L, ref);
358                 LuaItemStack::create(L, removed);
359         } else {
360                 LuaItemStack::create(L, ItemStack());
361         }
362         return 1;
363 }
364
365 // get_location() -> location (like get_inventory(location))
366 int InvRef::l_get_location(lua_State *L)
367 {
368         NO_MAP_LOCK_REQUIRED;
369         InvRef *ref = checkobject(L, 1);
370         const InventoryLocation &loc = ref->m_loc;
371         switch(loc.type){
372         case InventoryLocation::PLAYER:
373                 lua_newtable(L);
374                 lua_pushstring(L, "player");
375                 lua_setfield(L, -2, "type");
376                 lua_pushstring(L, loc.name.c_str());
377                 lua_setfield(L, -2, "name");
378                 return 1;
379         case InventoryLocation::NODEMETA:
380                 lua_newtable(L);
381                 lua_pushstring(L, "node");
382                 lua_setfield(L, -2, "type");
383                 push_v3s16(L, loc.p);
384                 lua_setfield(L, -2, "pos");
385                 return 1;
386         case InventoryLocation::DETACHED:
387                 lua_newtable(L);
388                 lua_pushstring(L, "detached");
389                 lua_setfield(L, -2, "type");
390                 lua_pushstring(L, loc.name.c_str());
391                 lua_setfield(L, -2, "name");
392                 return 1;
393         case InventoryLocation::UNDEFINED:
394         case InventoryLocation::CURRENT_PLAYER:
395                 break;
396         }
397         lua_newtable(L);
398         lua_pushstring(L, "undefined");
399         lua_setfield(L, -2, "type");
400         return 1;
401 }
402
403
404 InvRef::InvRef(const InventoryLocation &loc):
405         m_loc(loc)
406 {
407 }
408
409 InvRef::~InvRef()
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         std::string type = checkstringfield(L, 1, "type");
492
493         if(type == "node"){
494                 MAP_LOCK_REQUIRED;
495                 lua_getfield(L, 1, "pos");
496                 v3s16 pos = check_v3s16(L, -1);
497                 loc.setNodeMeta(pos);
498
499                 if(getServer(L)->getInventory(loc) != NULL)
500                         InvRef::create(L, loc);
501                 else
502                         lua_pushnil(L);
503                 return 1;
504         } else {
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         const char *player = lua_isstring(L, 2) ? lua_tostring(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 void ModApiInventory::Initialize(lua_State *L, int top)
540 {
541         API_FCT(create_detached_inventory_raw);
542         API_FCT(get_inventory);
543 }