diff options
author | Thorbjørn Lindeijer <thorbjorn@lindeijer.nl> | 2012-01-30 22:16:40 +0100 |
---|---|---|
committer | Thorbjørn Lindeijer <thorbjorn@lindeijer.nl> | 2012-01-31 21:50:49 +0100 |
commit | 4cd1957231605e976c5cf001eddea80d5e49272f (patch) | |
tree | 1fd13522adeb62c408562a4ff76c98ee2e254342 | |
parent | bc291713c56a2bc27ec10cef52dd3a52a0579c7d (diff) | |
download | manaserv-4cd1957231605e976c5cf001eddea80d5e49272f.tar.gz manaserv-4cd1957231605e976c5cf001eddea80d5e49272f.tar.bz2 manaserv-4cd1957231605e976c5cf001eddea80d5e49272f.tar.xz manaserv-4cd1957231605e976c5cf001eddea80d5e49272f.zip |
Use a full user data object for MapObject references
Based on a templated helper class, MapObject references in Lua scripts are now
full user data objects. Using the '__index' member of their metatable, a
library is associated with it so that member functions can be called directly
on the object.
Reviewed-by: Yohann Ferreira
Reviewed-by: Erik Schilling
-rw-r--r-- | example/serverdata/scripts/npcs/merchant.lua | 10 | ||||
-rw-r--r-- | src/scripting/lua.cpp | 66 | ||||
-rw-r--r-- | src/scripting/luautil.cpp | 5 | ||||
-rw-r--r-- | src/scripting/luautil.h | 66 |
4 files changed, 94 insertions, 53 deletions
diff --git a/example/serverdata/scripts/npcs/merchant.lua b/example/serverdata/scripts/npcs/merchant.lua index 23035b11..c3bd36a0 100644 --- a/example/serverdata/scripts/npcs/merchant.lua +++ b/example/serverdata/scripts/npcs/merchant.lua @@ -48,6 +48,7 @@ function Merchant(npc, ch, buy_sell_table) else table.insert (choice_table, "Can you make me a price for what I have?") end + table.insert (choice_table, "Tell me about the objects on this map") table.insert (choice_table, "Nevermind...") local v = do_choice(npc, ch, choice_table) @@ -94,6 +95,15 @@ function Merchant(npc, ch, buy_sell_table) end end + elseif v == 3 then + + local objects = mana.map_get_objects() + do_message(npc, ch, "There are " .. #objects .. " objects on this map, their names are:") + for i=1,#objects do + do_message(npc, ch, tostring(i) .. ": " .. objects[i]:name()) + end + end + do_message(npc, ch, "See you later!") end diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index c7053b3c..63149792 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -2500,31 +2500,17 @@ static int map_get_objects(lua_State *s) static int map_object_get_property(lua_State *s) { const char *key = luaL_checkstring(s, 2); - if (!lua_islightuserdata(s, 1)) - { - raiseScriptError(s, "map_object_get_property called with invalid" - "object handle"); - return 0; - } - MapObject *obj = static_cast<MapObject *>(lua_touserdata(s, 1)); - if (obj) + MapObject *obj = LuaMapObject::check(s, 1); + + std::string property = obj->getProperty(key); + if (!property.empty()) { - std::string property = obj->getProperty(key); - if (!property.empty()) - { - lua_pushstring(s, property.c_str()); - return 1; - } - else - { - // scripts can check for nil - return 0; - } + lua_pushstring(s, property.c_str()); + return 1; } else { - raiseScriptError(s, "map_object_get_property called with invalid" - "object handle"); + // scripts can check for nil return 0; } } @@ -2535,13 +2521,7 @@ static int map_object_get_property(lua_State *s) */ static int map_object_get_bounds(lua_State *s) { - if (!lua_islightuserdata(s, 1)) - { - raiseScriptError(s, "map_object_get_bounds called with invalid" - "object handle"); - return 0; - } - MapObject *obj = static_cast<MapObject *>(lua_touserdata(s, 1)); + MapObject *obj = LuaMapObject::check(s, 1); const Rectangle &bounds = obj->getBounds(); lua_pushinteger(s, bounds.x); lua_pushinteger(s, bounds.y); @@ -2556,13 +2536,7 @@ static int map_object_get_bounds(lua_State *s) */ static int map_object_get_name(lua_State *s) { - if (!lua_islightuserdata(s, 1)) - { - raiseScriptError(s, "map_object_get_name called with invalid" - "object handle"); - return 0; - } - MapObject *obj = static_cast<MapObject *>(lua_touserdata(s, 1)); + MapObject *obj = LuaMapObject::check(s, 1); lua_pushstring(s, obj->getName().c_str()); return 1; } @@ -2573,13 +2547,7 @@ static int map_object_get_name(lua_State *s) */ static int map_object_get_type(lua_State *s) { - if (!lua_islightuserdata(s, 1)) - { - raiseScriptError(s, "map_object_get_type called with invalid" - "object handle"); - return 0; - } - MapObject *obj = static_cast<MapObject *>(lua_touserdata(s, 1)); + MapObject *obj = LuaMapObject::check(s, 1); lua_pushstring(s, obj->getType().c_str()); return 1; } @@ -2718,16 +2686,22 @@ LuaScript::LuaScript(): { "log", &log }, { "get_distance", &get_distance }, { "map_get_objects", &map_get_objects }, - { "map_object_get_property", &map_object_get_property }, - { "map_object_get_bounds", &map_object_get_bounds }, - { "map_object_get_name", &map_object_get_name }, - { "map_object_get_type", &map_object_get_type }, { "announce", &announce }, { NULL, NULL } }; luaL_register(mState, "mana", callbacks); lua_pop(mState, 1); // pop the 'mana' table + static luaL_Reg const members_MapObject[] = { + { "property", &map_object_get_property }, + { "bounds", &map_object_get_bounds }, + { "name", &map_object_get_name }, + { "type", &map_object_get_type }, + { NULL, NULL } + }; + + LuaMapObject::registerType(mState, "MapObject", members_MapObject); + // Make script object available to callback functions. lua_pushlightuserdata(mState, (void *)®istryKey); lua_pushlightuserdata(mState, this); diff --git a/src/scripting/luautil.cpp b/src/scripting/luautil.cpp index a7521426..b7680c6b 100644 --- a/src/scripting/luautil.cpp +++ b/src/scripting/luautil.cpp @@ -116,8 +116,3 @@ void push(lua_State *s, double val) { lua_pushnumber(s, val); } - -void push(lua_State *s, MapObject *val) -{ - lua_pushlightuserdata(s, val); -} diff --git a/src/scripting/luautil.h b/src/scripting/luautil.h index ee3f1142..4f456ab6 100644 --- a/src/scripting/luautil.h +++ b/src/scripting/luautil.h @@ -44,19 +44,81 @@ void raiseScriptError(lua_State *s, const char *format, ...); void raiseWarning(lua_State *s, const char *format, ...); +/** + * A helper class for pushing and checking custom Lua user data types. + */ +template <typename T> +class LuaUserData +{ +public: + /** + * Creates a metatable to be used for the user data associated with the + * type. Then, registers the \a members with a library named \a typeName, + * and sets the '__index' member of the metatable to this library. + */ + static void registerType(lua_State *s, + const char *typeName, + const luaL_Reg *members) + { + mTypeName = typeName; + + luaL_newmetatable(s, mTypeName); // metatable + lua_pushstring(s, "__index"); // metatable, "__index" + luaL_register(s, typeName, members); // metatable, "__index", {} + lua_rawset(s, -3); // metatable + lua_pop(s, 1); // -empty- + } + + /** + * Pushes a userdata reference to the given object on the stack. Either by + * creating one, or reusing an existing one. + */ + static int push(lua_State *L, T *object) + { + T **userData = static_cast<T**>(lua_newuserdata(L, sizeof(T*))); + *userData = object; + + luaL_newmetatable(L, mTypeName); + lua_setmetatable(L, -2); + + return 1; + } + + /** + * Returns the argument at position \a narg when it is of the right type, + * and raises a Lua error otherwise. + */ + static T *check(lua_State *L, int narg) + { + void *userData = luaL_checkudata(L, narg, mTypeName); + return *(static_cast<T**>(userData)); + } + +private: + static const char *mTypeName; +}; + +template <typename T> const char * LuaUserData<T>::mTypeName; + +typedef LuaUserData<MapObject> LuaMapObject; + + NPC *getNPC(lua_State *s, int p); Character *getCharacter(lua_State *s, int p); Monster *getMonster(lua_State *s, int p); Being *getBeing(lua_State *s, int p); - /* Polymorphic wrapper for pushing variables. Useful for templates.*/ void push(lua_State *s, int val); void push(lua_State *s, const std::string &val); void push(lua_State *s, Thing *val); void push(lua_State *s, double val); -void push(lua_State *s, MapObject *val); + +inline void push(lua_State *s, MapObject *val) +{ + LuaMapObject::push(s, val); +} /* Pushes an STL LIST */ |