lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Responses inline.

On 10/23/12 10:32 PM, Littlefield, Tyler wrote:
> Hello all:
> I've been working on a mud for a while, and my scripting engine is
> rather primative. basically a mud is just a multi-user text-based
> game, so it can be considered along the same lines as a game engine.
>
> I'd really like to revamp my scripting system before it gets more
> complicated than it is already. I'm not really sure how to do a couple
> of things though.
>
> First, triggers are obviously important. Currently I have events in
> game that just fire when an action is performed, so I can extend these
> to hit Lua.
> I'm thinking each object will just do something like:
> AddTrigger("give", onGive)
> This will just bind the onGive function/callback into an event that
> will be triggered when this happens. is that a decent idea? Every
> object that is loaded gets it's code executed in a virtual table of
> sorts. It looks something like this:
> void Script::Execute(Entity* obj, const std::string &code)
> {
>   lua_State* state = Script::GetState();
>   int ret = 0;
>   World* world = World::GetPtr();
>
>   if (!luaL_loadbuffer(state, code.c_str(), code.length(),
> "execution"))   // chunk is at -1
>     {
> //we need to create the metatable and store it in the registry:
>       lua_pushinteger(state, obj->GetOnum());
>       lua_newtable(state); // create shadow environment table at -1
>       lua_settable(state, LUA_REGISTRYINDEX);
> //now we get the table back again:
>       lua_pushinteger(state, obj->GetOnum());
>       lua_gettable(state, LUA_REGISTRYINDEX);
>       ObjectToStack(state, obj);
>       lua_setfield(state, -2, "this");
>       lua_getfield(state, LUA_REGISTRYINDEX, "meta"); //our metatable
> is at -1
>       lua_setmetatable(state, -2); //set table and pop
>
>       lua_setfenv(state, -2); //sets the environment
>       ret = lua_pcall(state, 0, 0, 0);
>       if (ret)
>         {
>           world->WriteLog(lua_tostring(state, -1), SCRIPT, "script");
>           lua_pop(state, 1);
>         }
>     }
> }
> Is this recommended? The idea was to keep objects from having naming
> collisions. For example if I had a function named foo on a sword and a
> player. Would this also be something to use when loading global scripts?
I'm not 100% on what this code is supposed to do; is it registering new
event callbacks, or is it invoking the callbacks for an event?
>
> Second, I'd really like to objify everything. For example in order to
> send a message to a player currently, I have to do:
> player.send(mob, "hello world!")
> How hard would it be to attach ehese in classes somehow, so I just
> call self:send("hello world!" or whatever?
> Right now my registration is done in a table:
> BOOL InitPlayerScript(Script* s)
> {
>   lua_State* lstate = s->GetState();
>   luaL_newmetatable(lstate, "player");
>   lua_pushstring(lstate, "__index");
>   lua_pushvalue(lstate, -2);
>   lua_settable(lstate, -3);
>   luaL_openlib(lstate, "player", player_table, 0);
> ...
> }
> static const struct luaL_reg player_table [] =
>   {"GetTitle", SCR_GetTitle},
>   {"SetTitle", SCR_SetTitle},
> ...
>   {NULL, NULL}
> };
> int SCR_GetTitle(lua_State* l)
> {
>   UserData* udata = NULL;
>
>   if (lua_gettop(l) != 1)
>     {
>       SCR_Error(l, "Invalid number of arguments to \'GetTitle\'.");
>       return 0;
>     }
>
>   udata = (UserData*)lua_touserdata(l, -1);
>   if (!IsPlayer(l, udata))
>     {
>       return 0;
>     }
>
>   lua_pushlstring(l, ((Player*)udata->ptr)->GetTitle().c_str(),
> ((Player*)udata->ptr)->GetTitle().length());
>   return 1;
> }
> int SCR_SetTitle(lua_State* l)
> {
>   const char* title = NULL;
>   UserData* udata = NULL;
>
>   if (lua_gettop(l) != 2)
>     {
>       SCR_Error(l, "Invalid number of arguments to \'SetTitle\'.");
>       return 0;
>     }
>
>   title = lua_tostring(l, -1);
>   if (!title)
>     {
>       SCR_Error(l, "Argument 2 to \'SetTitle\' must be a string.");
>       return 0;
>     }
>
>   udata = (UserData*)lua_touserdata(l, -2);
>   if (!IsPlayer(l, udata))
>     {
>       return 0;
>     }
>
>   ((Player*)udata->ptr)->SetTitle(title);
>   return 0;
> }
> Can something like this be more OO like?
Which part do you want to be more OO? The Lua part? The C++ part?  Your
C++ code looks like it would work with OO-style Lua, although I would do
a few things differently (namely, if !IsPlayer, I would throw
an error).
>
> Finally, I'm curious how to go about setting up object properties. a
> lot of muds that have used lua (I was just looking at the Aard page)
> sets stuff up
> like ch for the calling player, self for the current object, then they
> allow for like ch.gold, ch.int etc. I'm curious how this works. I know
> about setting values on a table, but it seems that the table would
> have to call it's underlying c++ object's GetGold method to retrieve it.
What you could do is have an __index metamethod that returned the value
of the property as needed.  Here's an example in Lua; converting it to
C++ is left as an exercise for the reader. =)

   local mt      = {}
   local object = setmetatable({}, mt)
   function mt:__index(key)
     if key == 'time' then
       return os.time()
     end
   end
   print(object.time)
>
> Thanks in advance, and sorry for all the questions.
>