3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "scriptapi.h"
21 #include "scriptapi_inventory.h"
25 #include "scriptapi_types.h"
26 #include "scriptapi_common.h"
27 #include "scriptapi_inventory.h"
28 #include "scriptapi_item.h"
29 #include "scriptapi_object.h"
35 InvRef* InvRef::checkobject(lua_State *L, int narg)
37 luaL_checktype(L, narg, LUA_TUSERDATA);
38 void *ud = luaL_checkudata(L, narg, className);
39 if(!ud) luaL_typerror(L, narg, className);
40 return *(InvRef**)ud; // unbox pointer
43 Inventory* InvRef::getinv(lua_State *L, InvRef *ref)
45 return get_server(L)->getInventory(ref->m_loc);
48 InventoryList* InvRef::getlist(lua_State *L, InvRef *ref,
51 Inventory *inv = getinv(L, ref);
54 return inv->getList(listname);
57 void InvRef::reportInventoryChange(lua_State *L, InvRef *ref)
59 // Inform other things that the inventory has changed
60 get_server(L)->setInventoryModified(ref->m_loc);
66 int InvRef::gc_object(lua_State *L) {
67 InvRef *o = *(InvRef **)(lua_touserdata(L, 1));
72 // is_empty(self, listname) -> true/false
73 int InvRef::l_is_empty(lua_State *L)
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);
81 lua_pushboolean(L, true);
86 // get_size(self, listname)
87 int InvRef::l_get_size(lua_State *L)
89 InvRef *ref = checkobject(L, 1);
90 const char *listname = luaL_checkstring(L, 2);
91 InventoryList *list = getlist(L, ref, listname);
93 lua_pushinteger(L, list->getSize());
95 lua_pushinteger(L, 0);
100 // get_width(self, listname)
101 int InvRef::l_get_width(lua_State *L)
103 InvRef *ref = checkobject(L, 1);
104 const char *listname = luaL_checkstring(L, 2);
105 InventoryList *list = getlist(L, ref, listname);
107 lua_pushinteger(L, list->getWidth());
109 lua_pushinteger(L, 0);
114 // set_size(self, listname, size)
115 int InvRef::l_set_size(lua_State *L)
117 InvRef *ref = checkobject(L, 1);
118 const char *listname = luaL_checkstring(L, 2);
119 int newsize = luaL_checknumber(L, 3);
120 Inventory *inv = getinv(L, ref);
122 inv->deleteList(listname);
123 reportInventoryChange(L, ref);
126 InventoryList *list = inv->getList(listname);
128 list->setSize(newsize);
130 list = inv->addList(listname, newsize);
132 reportInventoryChange(L, ref);
136 // set_width(self, listname, size)
137 int InvRef::l_set_width(lua_State *L)
139 InvRef *ref = checkobject(L, 1);
140 const char *listname = luaL_checkstring(L, 2);
141 int newwidth = luaL_checknumber(L, 3);
142 Inventory *inv = getinv(L, ref);
143 InventoryList *list = inv->getList(listname);
145 list->setWidth(newwidth);
149 reportInventoryChange(L, ref);
153 // get_stack(self, listname, i) -> itemstack
154 int InvRef::l_get_stack(lua_State *L)
156 InvRef *ref = checkobject(L, 1);
157 const char *listname = luaL_checkstring(L, 2);
158 int i = luaL_checknumber(L, 3) - 1;
159 InventoryList *list = getlist(L, ref, listname);
161 if(list != NULL && i >= 0 && i < (int) list->getSize())
162 item = list->getItem(i);
163 LuaItemStack::create(L, item);
167 // set_stack(self, listname, i, stack) -> true/false
168 int InvRef::l_set_stack(lua_State *L)
170 InvRef *ref = checkobject(L, 1);
171 const char *listname = luaL_checkstring(L, 2);
172 int i = luaL_checknumber(L, 3) - 1;
173 ItemStack newitem = read_item(L, 4);
174 InventoryList *list = getlist(L, ref, listname);
175 if(list != NULL && i >= 0 && i < (int) list->getSize()){
176 list->changeItem(i, newitem);
177 reportInventoryChange(L, ref);
178 lua_pushboolean(L, true);
180 lua_pushboolean(L, false);
185 // get_list(self, listname) -> list or nil
186 int InvRef::l_get_list(lua_State *L)
188 InvRef *ref = checkobject(L, 1);
189 const char *listname = luaL_checkstring(L, 2);
190 Inventory *inv = getinv(L, ref);
191 inventory_get_list_to_lua(inv, listname, L);
195 // set_list(self, listname, list)
196 int InvRef::l_set_list(lua_State *L)
198 InvRef *ref = checkobject(L, 1);
199 const char *listname = luaL_checkstring(L, 2);
200 Inventory *inv = getinv(L, ref);
201 InventoryList *list = inv->getList(listname);
203 inventory_set_list_from_lua(inv, listname, L, 3,
206 inventory_set_list_from_lua(inv, listname, L, 3);
207 reportInventoryChange(L, ref);
211 // add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
212 // Returns the leftover stack
213 int InvRef::l_add_item(lua_State *L)
215 InvRef *ref = checkobject(L, 1);
216 const char *listname = luaL_checkstring(L, 2);
217 ItemStack item = read_item(L, 3);
218 InventoryList *list = getlist(L, ref, listname);
220 ItemStack leftover = list->addItem(item);
221 if(leftover.count != item.count)
222 reportInventoryChange(L, ref);
223 LuaItemStack::create(L, leftover);
225 LuaItemStack::create(L, item);
230 // room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false
231 // Returns true if the item completely fits into the list
232 int InvRef::l_room_for_item(lua_State *L)
234 InvRef *ref = checkobject(L, 1);
235 const char *listname = luaL_checkstring(L, 2);
236 ItemStack item = read_item(L, 3);
237 InventoryList *list = getlist(L, ref, listname);
239 lua_pushboolean(L, list->roomForItem(item));
241 lua_pushboolean(L, false);
246 // contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false
247 // Returns true if the list contains the given count of the given item name
248 int InvRef::l_contains_item(lua_State *L)
250 InvRef *ref = checkobject(L, 1);
251 const char *listname = luaL_checkstring(L, 2);
252 ItemStack item = read_item(L, 3);
253 InventoryList *list = getlist(L, ref, listname);
255 lua_pushboolean(L, list->containsItem(item));
257 lua_pushboolean(L, false);
262 // remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
263 // Returns the items that were actually removed
264 int InvRef::l_remove_item(lua_State *L)
266 InvRef *ref = checkobject(L, 1);
267 const char *listname = luaL_checkstring(L, 2);
268 ItemStack item = read_item(L, 3);
269 InventoryList *list = getlist(L, ref, listname);
271 ItemStack removed = list->removeItem(item);
273 reportInventoryChange(L, ref);
274 LuaItemStack::create(L, removed);
276 LuaItemStack::create(L, ItemStack());
281 // get_location() -> location (like minetest.get_inventory(location))
282 int InvRef::l_get_location(lua_State *L)
284 InvRef *ref = checkobject(L, 1);
285 const InventoryLocation &loc = ref->m_loc;
287 case InventoryLocation::PLAYER:
289 lua_pushstring(L, "player");
290 lua_setfield(L, -2, "type");
291 lua_pushstring(L, loc.name.c_str());
292 lua_setfield(L, -2, "name");
294 case InventoryLocation::NODEMETA:
296 lua_pushstring(L, "nodemeta");
297 lua_setfield(L, -2, "type");
298 push_v3s16(L, loc.p);
299 lua_setfield(L, -2, "name");
301 case InventoryLocation::DETACHED:
303 lua_pushstring(L, "detached");
304 lua_setfield(L, -2, "type");
305 lua_pushstring(L, loc.name.c_str());
306 lua_setfield(L, -2, "name");
308 case InventoryLocation::UNDEFINED:
309 case InventoryLocation::CURRENT_PLAYER:
313 lua_pushstring(L, "undefined");
314 lua_setfield(L, -2, "type");
319 InvRef::InvRef(const InventoryLocation &loc):
328 // Creates an InvRef and leaves it on top of stack
329 // Not callable from Lua; all references are created on the C side.
330 void InvRef::create(lua_State *L, const InventoryLocation &loc)
332 InvRef *o = new InvRef(loc);
333 *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
334 luaL_getmetatable(L, className);
335 lua_setmetatable(L, -2);
337 void InvRef::createPlayer(lua_State *L, Player *player)
339 InventoryLocation loc;
340 loc.setPlayer(player->getName());
343 void InvRef::createNodeMeta(lua_State *L, v3s16 p)
345 InventoryLocation loc;
350 void InvRef::Register(lua_State *L)
353 int methodtable = lua_gettop(L);
354 luaL_newmetatable(L, className);
355 int metatable = lua_gettop(L);
357 lua_pushliteral(L, "__metatable");
358 lua_pushvalue(L, methodtable);
359 lua_settable(L, metatable); // hide metatable from Lua getmetatable()
361 lua_pushliteral(L, "__index");
362 lua_pushvalue(L, methodtable);
363 lua_settable(L, metatable);
365 lua_pushliteral(L, "__gc");
366 lua_pushcfunction(L, gc_object);
367 lua_settable(L, metatable);
369 lua_pop(L, 1); // drop metatable
371 luaL_openlib(L, 0, methods, 0); // fill methodtable
372 lua_pop(L, 1); // drop methodtable
374 // Cannot be created from Lua
375 //lua_register(L, className, create_object);
378 const char InvRef::className[] = "InvRef";
379 const luaL_reg InvRef::methods[] = {
380 luamethod(InvRef, is_empty),
381 luamethod(InvRef, get_size),
382 luamethod(InvRef, set_size),
383 luamethod(InvRef, get_width),
384 luamethod(InvRef, set_width),
385 luamethod(InvRef, get_stack),
386 luamethod(InvRef, set_stack),
387 luamethod(InvRef, get_list),
388 luamethod(InvRef, set_list),
389 luamethod(InvRef, add_item),
390 luamethod(InvRef, room_for_item),
391 luamethod(InvRef, contains_item),
392 luamethod(InvRef, remove_item),
393 luamethod(InvRef, get_location),
397 void inventory_get_list_to_lua(Inventory *inv, const char *name,
400 InventoryList *invlist = inv->getList(name);
405 std::vector<ItemStack> items;
406 for(u32 i=0; i<invlist->getSize(); i++)
407 items.push_back(invlist->getItem(i));
408 push_items(L, items);
411 void inventory_set_list_from_lua(Inventory *inv, const char *name,
412 lua_State *L, int tableindex, int forcesize)
415 tableindex = lua_gettop(L) + 1 + tableindex;
416 // If nil, delete list
417 if(lua_isnil(L, tableindex)){
418 inv->deleteList(name);
421 // Otherwise set list
422 std::vector<ItemStack> items = read_items(L, tableindex);
423 int listsize = (forcesize != -1) ? forcesize : items.size();
424 InventoryList *invlist = inv->addList(name, listsize);
426 for(std::vector<ItemStack>::const_iterator
427 i = items.begin(); i != items.end(); i++){
428 if(forcesize != -1 && index == forcesize)
430 invlist->changeItem(index, *i);
433 while(forcesize != -1 && index < forcesize){
434 invlist->deleteItem(index);
439 // get_inventory(location)
440 int l_get_inventory(lua_State *L)
442 InventoryLocation loc;
444 std::string type = checkstringfield(L, 1, "type");
445 if(type == "player"){
446 std::string name = checkstringfield(L, 1, "name");
448 } else if(type == "node"){
449 lua_getfield(L, 1, "pos");
450 v3s16 pos = check_v3s16(L, -1);
451 loc.setNodeMeta(pos);
452 } else if(type == "detached"){
453 std::string name = checkstringfield(L, 1, "name");
454 loc.setDetached(name);
457 if(get_server(L)->getInventory(loc) != NULL)
458 InvRef::create(L, loc);
465 Detached inventory callbacks
468 // Retrieves minetest.detached_inventories[name][callbackname]
469 // If that is nil or on error, return false and stack is unchanged
470 // If that is a function, returns true and pushes the
471 // function onto the stack
472 static bool get_detached_inventory_callback(lua_State *L,
473 const std::string &name, const char *callbackname)
475 lua_getglobal(L, "minetest");
476 lua_getfield(L, -1, "detached_inventories");
478 luaL_checktype(L, -1, LUA_TTABLE);
479 lua_getfield(L, -1, name.c_str());
482 if(lua_type(L, -1) != LUA_TTABLE)
484 errorstream<<"Item \""<<name<<"\" not defined"<<std::endl;
488 lua_getfield(L, -1, callbackname);
490 // Should be a function or nil
491 if(lua_type(L, -1) == LUA_TFUNCTION)
495 else if(lua_isnil(L, -1))
502 errorstream<<"Detached inventory \""<<name<<"\" callback \""
503 <<callbackname<<"\" is not a function"<<std::endl;
509 // Return number of accepted items to be moved
510 int scriptapi_detached_inventory_allow_move(lua_State *L,
511 const std::string &name,
512 const std::string &from_list, int from_index,
513 const std::string &to_list, int to_index,
514 int count, ServerActiveObject *player)
517 assert(lua_checkstack(L, 20));
518 StackUnroller stack_unroller(L);
520 // Push callback function on stack
521 if(!get_detached_inventory_callback(L, name, "allow_move"))
524 // function(inv, from_list, from_index, to_list, to_index, count, player)
526 InventoryLocation loc;
527 loc.setDetached(name);
528 InvRef::create(L, loc);
530 lua_pushstring(L, from_list.c_str());
532 lua_pushinteger(L, from_index + 1);
534 lua_pushstring(L, to_list.c_str());
536 lua_pushinteger(L, to_index + 1);
538 lua_pushinteger(L, count);
540 objectref_get_or_create(L, player);
541 if(lua_pcall(L, 7, 1, 0))
542 script_error(L, "error: %s", lua_tostring(L, -1));
543 if(!lua_isnumber(L, -1))
544 throw LuaError(L, "allow_move should return a number");
545 return luaL_checkinteger(L, -1);
548 // Return number of accepted items to be put
549 int scriptapi_detached_inventory_allow_put(lua_State *L,
550 const std::string &name,
551 const std::string &listname, int index, ItemStack &stack,
552 ServerActiveObject *player)
555 assert(lua_checkstack(L, 20));
556 StackUnroller stack_unroller(L);
558 // Push callback function on stack
559 if(!get_detached_inventory_callback(L, name, "allow_put"))
560 return stack.count; // All will be accepted
562 // Call function(inv, listname, index, stack, player)
564 InventoryLocation loc;
565 loc.setDetached(name);
566 InvRef::create(L, loc);
568 lua_pushstring(L, listname.c_str());
570 lua_pushinteger(L, index + 1);
572 LuaItemStack::create(L, stack);
574 objectref_get_or_create(L, player);
575 if(lua_pcall(L, 5, 1, 0))
576 script_error(L, "error: %s", lua_tostring(L, -1));
577 if(!lua_isnumber(L, -1))
578 throw LuaError(L, "allow_put should return a number");
579 return luaL_checkinteger(L, -1);
582 // Return number of accepted items to be taken
583 int scriptapi_detached_inventory_allow_take(lua_State *L,
584 const std::string &name,
585 const std::string &listname, int index, ItemStack &stack,
586 ServerActiveObject *player)
589 assert(lua_checkstack(L, 20));
590 StackUnroller stack_unroller(L);
592 // Push callback function on stack
593 if(!get_detached_inventory_callback(L, name, "allow_take"))
594 return stack.count; // All will be accepted
596 // Call function(inv, listname, index, stack, player)
598 InventoryLocation loc;
599 loc.setDetached(name);
600 InvRef::create(L, loc);
602 lua_pushstring(L, listname.c_str());
604 lua_pushinteger(L, index + 1);
606 LuaItemStack::create(L, stack);
608 objectref_get_or_create(L, player);
609 if(lua_pcall(L, 5, 1, 0))
610 script_error(L, "error: %s", lua_tostring(L, -1));
611 if(!lua_isnumber(L, -1))
612 throw LuaError(L, "allow_take should return a number");
613 return luaL_checkinteger(L, -1);
616 // Report moved items
617 void scriptapi_detached_inventory_on_move(lua_State *L,
618 const std::string &name,
619 const std::string &from_list, int from_index,
620 const std::string &to_list, int to_index,
621 int count, ServerActiveObject *player)
624 assert(lua_checkstack(L, 20));
625 StackUnroller stack_unroller(L);
627 // Push callback function on stack
628 if(!get_detached_inventory_callback(L, name, "on_move"))
631 // function(inv, from_list, from_index, to_list, to_index, count, player)
633 InventoryLocation loc;
634 loc.setDetached(name);
635 InvRef::create(L, loc);
637 lua_pushstring(L, from_list.c_str());
639 lua_pushinteger(L, from_index + 1);
641 lua_pushstring(L, to_list.c_str());
643 lua_pushinteger(L, to_index + 1);
645 lua_pushinteger(L, count);
647 objectref_get_or_create(L, player);
648 if(lua_pcall(L, 7, 0, 0))
649 script_error(L, "error: %s", lua_tostring(L, -1));
653 void scriptapi_detached_inventory_on_put(lua_State *L,
654 const std::string &name,
655 const std::string &listname, int index, ItemStack &stack,
656 ServerActiveObject *player)
659 assert(lua_checkstack(L, 20));
660 StackUnroller stack_unroller(L);
662 // Push callback function on stack
663 if(!get_detached_inventory_callback(L, name, "on_put"))
666 // Call function(inv, listname, index, stack, player)
668 InventoryLocation loc;
669 loc.setDetached(name);
670 InvRef::create(L, loc);
672 lua_pushstring(L, listname.c_str());
674 lua_pushinteger(L, index + 1);
676 LuaItemStack::create(L, stack);
678 objectref_get_or_create(L, player);
679 if(lua_pcall(L, 5, 0, 0))
680 script_error(L, "error: %s", lua_tostring(L, -1));
683 // Report taken items
684 void scriptapi_detached_inventory_on_take(lua_State *L,
685 const std::string &name,
686 const std::string &listname, int index, ItemStack &stack,
687 ServerActiveObject *player)
690 assert(lua_checkstack(L, 20));
691 StackUnroller stack_unroller(L);
693 // Push callback function on stack
694 if(!get_detached_inventory_callback(L, name, "on_take"))
697 // Call function(inv, listname, index, stack, player)
699 InventoryLocation loc;
700 loc.setDetached(name);
701 InvRef::create(L, loc);
703 lua_pushstring(L, listname.c_str());
705 lua_pushinteger(L, index + 1);
707 LuaItemStack::create(L, stack);
709 objectref_get_or_create(L, player);
710 if(lua_pcall(L, 5, 0, 0))
711 script_error(L, "error: %s", lua_tostring(L, -1));
714 // create_detached_inventory_raw(name)
715 int l_create_detached_inventory_raw(lua_State *L)
717 const char *name = luaL_checkstring(L, 1);
718 if(get_server(L)->createDetachedInventory(name) != NULL){
719 InventoryLocation loc;
720 loc.setDetached(name);
721 InvRef::create(L, loc);