This is one heck of a question. I am not familliar with luabind, so I will go over the general ideas, and maybe hit some specifics.
I'm going to make a few assumptions, and some of which might be wrong. I'm assuming that the Lua scripts are on the server, and that the ProcessAI function is called fairly regularly (at least every 100 ms for decent accuracy, up to 1000 ms). I'm also assuming that you are using luabind, rather than rolling your own bindings. I strongly suggest you not do your own bindings unless you really want to learn the Lua C API or have a large timeframe you want to complete the project in.
The ScheduleEvent function is first. Hopefully
http://www.rasterbar...g-lua-functions will give you a decent idea as to where to start when calling Lua functions from C++. The interesting part is having timed events. You will need a single container to hold all of the events. It needs to be sequential and have fast random access; linked list seems to fit the bill perfectly.
Overall idea is to have a sorted list, which is done by placing each node in the list to its correct location upon insertion. The next part assumes that you have a running total game time. Each node will have the time at which the event should be fired, which function to call, and any data that needs to be passed to that function. During or before each ProcessAI call, you should go through the list comparing the current time to the time when it should fire. Because it is sorted, you can stop iterating over the list when you find an event that should not be fired. C++ code will look something like this:
//note that this probably won't compile, it is a mix of real C++ and pseudo code
//function is the function to call
//delay is the time in milliseconds from now from when it should be called
//repeat is the number of times to call (bad name?). -1 is a special value for "infinite"
//args is the object to be passed to "function". It can be an actual object or a table of objects.
void ScheduleEvent(luabind::object const& function, int delay, int repeat, luabind::object const& args)
{
if(repeat < 1) return; //if the user wishes to call the function 0 times, then don't call it.
long absolute_delay = current_time + delay; //current time is how long it has been since the program started in milliseconds
ScriptEvent* event = new ScriptEvent(absolute_delay,repeat,delay,function,args);
//there is a list defined elsewhere named EventList as std::list<ScriptEvent*>
//insertion sort
for(itr = iterate over EventList)
{
if(event->delay <= (*itr)->delay)
{ EventList.insert(itr,event); break; }
}
}
struct ScriptEvent
{
long delay;
int repeat;
int repeat_delay; //the original time passed to the function
luabind::object function;
luabind::object args;
ScriptEvent(long delay,int repeat,int repeat_delay, luabind::object const& function, luabind::object const& args) { set the values}
ScriptEvent(const ScriptEvent& se) { copy constructor}
}Simple enough, it does exactly as I explained above. The next snippet is called every ProcessAI or Update or some regular interval.
//I'm going to assume that by now, current_time has been updated
void CheckEvents()
{
//fire all of the events that should have happened since now.
for(itr = iterate through EventList)
{
if((*itr)->delay >= current_time)
{
ScriptEvent* evt = (*itr);
luabind::call_function(evt->function,evt->args);
if(evt->repeat == -1) //special case of inifinite
{
evt->delay += evt->repeat_delay;
AddToEventList(evt);
continue;
}
evt->repeat -= 1; //decrement the number of times to fire the event
if(evt->repeat > 0) //if we should repeat again
{
evt->delay += evt->repeat_delay; //set next time to fire
//insertion to a list while iterating through it does NOT
//invalidate the iterator
AddToEventList(evt);
}
else
{
delete evt;
evt = NULL;
}
//since it has been called, remove it from the list so it won't get
//called again.
itr = EventList.erase(itr);
}
else
break; //it is sorted, so no need to go through every item
}
}
//insertion sort function
void AddToEventList(ScriptEvent* node)
{
for(finditr = iterate through EventList)
{
if(node->delay <= (*finditr)->delay)
{ EventList.insert(finditr,node); return; }
}
}It looks like a lot, but it is just a whole bunch of integer comparisons and pointer dereferencing, so it should not be too expensive other than luabind::call_function.
To answer your second question: The functions are in the same scope as long as they are loaded (luaL_dofile) with the same lua_State. So you can call those functions any time you want. However, I do suggest that you create tables named after each NPC behavior to hold those functions to avoid name conflicts. Example
--Lua code in file Miner.lua or something
--create a table to hold the NPC behavior functions. This NPC is a "Miner"
Miner = Miner or {} --equivelant to if(Miner == nil) then Miner = {} end, so we do not overwrite a table that has already been created
function Miner.StartConversation1(NPC)
NPC:DoStuff()
end
--some other file BigBoss.lua
BigBoss = BigBoss or {}
function BigBoss.BargainWithMiner(NPC)
local miner = BigBoss.FindMiner(NPC) --finds a miner in range of it
Miner.StartConversation1(miner)
endIt is a lot, and I have probably misunderstood a question. If you need more details on a certain point, just say so.