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