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