]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/lua_api/l_inventory.cpp
Fix itemstack:add item not working correct
[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(newsize == 0){
125                 inv->deleteList(listname);
126                 reportInventoryChange(L, ref);
127                 return 0;
128         }
129         InventoryList *list = inv->getList(listname);
130         if(list){
131                 list->setSize(newsize);
132         } else {
133                 list = inv->addList(listname, newsize);
134         }
135         reportInventoryChange(L, ref);
136         return 0;
137 }
138
139 // set_width(self, listname, size)
140 int InvRef::l_set_width(lua_State *L)
141 {
142         NO_MAP_LOCK_REQUIRED;
143         InvRef *ref = checkobject(L, 1);
144         const char *listname = luaL_checkstring(L, 2);
145         int newwidth = luaL_checknumber(L, 3);
146         Inventory *inv = getinv(L, ref);
147         InventoryList *list = inv->getList(listname);
148         if(list){
149                 list->setWidth(newwidth);
150         } else {
151                 return 0;
152         }
153         reportInventoryChange(L, ref);
154         return 0;
155 }
156
157 // get_stack(self, listname, i) -> itemstack
158 int InvRef::l_get_stack(lua_State *L)
159 {
160         NO_MAP_LOCK_REQUIRED;
161         InvRef *ref = checkobject(L, 1);
162         const char *listname = luaL_checkstring(L, 2);
163         int i = luaL_checknumber(L, 3) - 1;
164         InventoryList *list = getlist(L, ref, listname);
165         ItemStack item;
166         if(list != NULL && i >= 0 && i < (int) list->getSize())
167                 item = list->getItem(i);
168         LuaItemStack::create(L, item);
169         return 1;
170 }
171
172 // set_stack(self, listname, i, stack) -> true/false
173 int InvRef::l_set_stack(lua_State *L)
174 {
175         NO_MAP_LOCK_REQUIRED;
176         InvRef *ref = checkobject(L, 1);
177         const char *listname = luaL_checkstring(L, 2);
178         int i = luaL_checknumber(L, 3) - 1;
179         ItemStack newitem = read_item(L, 4,STACK_TO_SERVER(L));
180         InventoryList *list = getlist(L, ref, listname);
181         if(list != NULL && i >= 0 && i < (int) list->getSize()){
182                 list->changeItem(i, newitem);
183                 reportInventoryChange(L, ref);
184                 lua_pushboolean(L, true);
185         } else {
186                 lua_pushboolean(L, false);
187         }
188         return 1;
189 }
190
191 // get_list(self, listname) -> list or nil
192 int InvRef::l_get_list(lua_State *L)
193 {
194         NO_MAP_LOCK_REQUIRED;
195         InvRef *ref = checkobject(L, 1);
196         const char *listname = luaL_checkstring(L, 2);
197         Inventory *inv = getinv(L, ref);
198         push_inventory_list(inv, listname, L);
199         return 1;
200 }
201
202 // set_list(self, listname, list)
203 int InvRef::l_set_list(lua_State *L)
204 {
205         NO_MAP_LOCK_REQUIRED;
206         InvRef *ref = checkobject(L, 1);
207         const char *listname = luaL_checkstring(L, 2);
208         Inventory *inv = getinv(L, ref);
209         InventoryList *list = inv->getList(listname);
210         if(list)
211                 read_inventory_list(inv, listname, L, 3,
212                                 STACK_TO_SERVER(L),list->getSize());
213         else
214                 read_inventory_list(inv, listname, L, 3,STACK_TO_SERVER(L));
215         reportInventoryChange(L, ref);
216         return 0;
217 }
218
219 // add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
220 // Returns the leftover stack
221 int InvRef::l_add_item(lua_State *L)
222 {
223         NO_MAP_LOCK_REQUIRED;
224         InvRef *ref = checkobject(L, 1);
225         const char *listname = luaL_checkstring(L, 2);
226         ItemStack item = read_item(L, 3,STACK_TO_SERVER(L));
227         InventoryList *list = getlist(L, ref, listname);
228         if(list){
229                 ItemStack leftover = list->addItem(item);
230                 if(leftover.count != item.count)
231                         reportInventoryChange(L, ref);
232                 LuaItemStack::create(L, leftover);
233         } else {
234                 LuaItemStack::create(L, item);
235         }
236         return 1;
237 }
238
239 // room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false
240 // Returns true if the item completely fits into the list
241 int InvRef::l_room_for_item(lua_State *L)
242 {
243         NO_MAP_LOCK_REQUIRED;
244         InvRef *ref = checkobject(L, 1);
245         const char *listname = luaL_checkstring(L, 2);
246         ItemStack item = read_item(L, 3,STACK_TO_SERVER(L));
247         InventoryList *list = getlist(L, ref, listname);
248         if(list){
249                 lua_pushboolean(L, list->roomForItem(item));
250         } else {
251                 lua_pushboolean(L, false);
252         }
253         return 1;
254 }
255
256 // contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false
257 // Returns true if the list contains the given count of the given item name
258 int InvRef::l_contains_item(lua_State *L)
259 {
260         NO_MAP_LOCK_REQUIRED;
261         InvRef *ref = checkobject(L, 1);
262         const char *listname = luaL_checkstring(L, 2);
263         ItemStack item = read_item(L, 3, STACK_TO_SERVER(L));
264         InventoryList *list = getlist(L, ref, listname);
265         if(list){
266                 lua_pushboolean(L, list->containsItem(item));
267         } else {
268                 lua_pushboolean(L, false);
269         }
270         return 1;
271 }
272
273 // remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
274 // Returns the items that were actually removed
275 int InvRef::l_remove_item(lua_State *L)
276 {
277         NO_MAP_LOCK_REQUIRED;
278         InvRef *ref = checkobject(L, 1);
279         const char *listname = luaL_checkstring(L, 2);
280         ItemStack item = read_item(L, 3,STACK_TO_SERVER(L));
281         InventoryList *list = getlist(L, ref, listname);
282         if(list){
283                 ItemStack removed = list->removeItem(item);
284                 if(!removed.empty())
285                         reportInventoryChange(L, ref);
286                 LuaItemStack::create(L, removed);
287         } else {
288                 LuaItemStack::create(L, ItemStack());
289         }
290         return 1;
291 }
292
293 // get_location() -> location (like minetest.get_inventory(location))
294 int InvRef::l_get_location(lua_State *L)
295 {
296         NO_MAP_LOCK_REQUIRED;
297         InvRef *ref = checkobject(L, 1);
298         const InventoryLocation &loc = ref->m_loc;
299         switch(loc.type){
300         case InventoryLocation::PLAYER:
301                 lua_newtable(L);
302                 lua_pushstring(L, "player");
303                 lua_setfield(L, -2, "type");
304                 lua_pushstring(L, loc.name.c_str());
305                 lua_setfield(L, -2, "name");
306                 return 1;
307         case InventoryLocation::NODEMETA:
308                 lua_newtable(L);
309                 lua_pushstring(L, "nodemeta");
310                 lua_setfield(L, -2, "type");
311                 push_v3s16(L, loc.p);
312                 lua_setfield(L, -2, "name");
313                 return 1;
314         case InventoryLocation::DETACHED:
315                 lua_newtable(L);
316                 lua_pushstring(L, "detached");
317                 lua_setfield(L, -2, "type");
318                 lua_pushstring(L, loc.name.c_str());
319                 lua_setfield(L, -2, "name");
320                 return 1;
321         case InventoryLocation::UNDEFINED:
322         case InventoryLocation::CURRENT_PLAYER:
323                 break;
324         }
325         lua_newtable(L);
326         lua_pushstring(L, "undefined");
327         lua_setfield(L, -2, "type");
328         return 1;
329 }
330
331
332 InvRef::InvRef(const InventoryLocation &loc):
333         m_loc(loc)
334 {
335 }
336
337 InvRef::~InvRef()
338 {
339 }
340
341 // Creates an InvRef and leaves it on top of stack
342 // Not callable from Lua; all references are created on the C side.
343 void InvRef::create(lua_State *L, const InventoryLocation &loc)
344 {
345         NO_MAP_LOCK_REQUIRED;
346         InvRef *o = new InvRef(loc);
347         *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
348         luaL_getmetatable(L, className);
349         lua_setmetatable(L, -2);
350 }
351 void InvRef::createPlayer(lua_State *L, Player *player)
352 {
353         NO_MAP_LOCK_REQUIRED;
354         InventoryLocation loc;
355         loc.setPlayer(player->getName());
356         create(L, loc);
357 }
358 void InvRef::createNodeMeta(lua_State *L, v3s16 p)
359 {
360         InventoryLocation loc;
361         loc.setNodeMeta(p);
362         create(L, loc);
363 }
364
365 void InvRef::Register(lua_State *L)
366 {
367         lua_newtable(L);
368         int methodtable = lua_gettop(L);
369         luaL_newmetatable(L, className);
370         int metatable = lua_gettop(L);
371
372         lua_pushliteral(L, "__metatable");
373         lua_pushvalue(L, methodtable);
374         lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
375
376         lua_pushliteral(L, "__index");
377         lua_pushvalue(L, methodtable);
378         lua_settable(L, metatable);
379
380         lua_pushliteral(L, "__gc");
381         lua_pushcfunction(L, gc_object);
382         lua_settable(L, metatable);
383
384         lua_pop(L, 1);  // drop metatable
385
386         luaL_openlib(L, 0, methods, 0);  // fill methodtable
387         lua_pop(L, 1);  // drop methodtable
388
389         // Cannot be created from Lua
390         //lua_register(L, className, create_object);
391 }
392
393 const char InvRef::className[] = "InvRef";
394 const luaL_reg InvRef::methods[] = {
395         luamethod(InvRef, is_empty),
396         luamethod(InvRef, get_size),
397         luamethod(InvRef, set_size),
398         luamethod(InvRef, get_width),
399         luamethod(InvRef, set_width),
400         luamethod(InvRef, get_stack),
401         luamethod(InvRef, set_stack),
402         luamethod(InvRef, get_list),
403         luamethod(InvRef, set_list),
404         luamethod(InvRef, add_item),
405         luamethod(InvRef, room_for_item),
406         luamethod(InvRef, contains_item),
407         luamethod(InvRef, remove_item),
408         luamethod(InvRef, get_location),
409         {0,0}
410 };
411
412 // get_inventory(location)
413 int ModApiInventory::l_get_inventory(lua_State *L)
414 {
415         InventoryLocation loc;
416
417         std::string type = checkstringfield(L, 1, "type");
418
419         if(type != "pos"){
420                 NO_MAP_LOCK_REQUIRED;
421
422                 if(type == "player"){
423                         std::string name = checkstringfield(L, 1, "name");
424                         loc.setPlayer(name);
425                 } else if(type == "detached"){
426                         std::string name = checkstringfield(L, 1, "name");
427                         loc.setDetached(name);
428                 }
429
430                 if(getServer(L)->getInventory(loc) != NULL)
431                         InvRef::create(L, loc);
432                 else
433                         lua_pushnil(L);
434
435                 return 1;
436         }
437         else {
438                 if(type == "node"){
439                                 lua_getfield(L, 1, "pos");
440                                 v3s16 pos = check_v3s16(L, -1);
441                                 loc.setNodeMeta(pos);
442                 }
443                 if(getServer(L)->getInventory(loc) != NULL)
444                         InvRef::create(L, loc);
445                 else
446                         lua_pushnil(L);
447                 return 1;
448         }
449
450 }
451
452 // create_detached_inventory_raw(name)
453 int ModApiInventory::l_create_detached_inventory_raw(lua_State *L)
454 {
455         NO_MAP_LOCK_REQUIRED;
456         const char *name = luaL_checkstring(L, 1);
457         if(getServer(L)->createDetachedInventory(name) != NULL){
458                 InventoryLocation loc;
459                 loc.setDetached(name);
460                 InvRef::create(L, loc);
461         }else{
462                 lua_pushnil(L);
463         }
464         return 1;
465 }
466
467 bool ModApiInventory::Initialize(lua_State *L, int top) {
468         bool retval = true;
469
470         retval &= API_FCT(create_detached_inventory_raw);
471         retval &= API_FCT(get_inventory);
472
473         InvRef::Register(L);
474
475         return retval;
476 }
477
478 ModApiInventory::ModApiInventory()
479         : ModApiBase() {
480
481 }
482
483 ModApiInventory modapiinventory_prototype;