EQEmulator Forums

EQEmulator Forums (https://www.eqemulator.org/forums/index.php)
-   Development::Server Code Submissions (https://www.eqemulator.org/forums/forumdisplay.php?f=669)
-   -   Lua-based Spawns and Grids (https://www.eqemulator.org/forums/showthread.php?t=36401)

Zaela_S 02-03-2013 01:56 AM

Lua-based Spawns and Grids
 
Per this thread.

Several new files, all part of the zone project. Should build and run normally without Lua includes and library as long as EMBLUA is not defined.

If someone does want to set it up with the Lua dependencies, make sure to retrieve Lua 5.1, NOT 5.2; I believe they renamed some C API functions used in my code in the newer version.

New files:
emblua.h
Code:

#ifndef EMBLUA_H
#define EMBLUA_H

#ifdef EMBLUA
#include "lua.hpp"
#include "masterentity.h"
#endif

class EmbLua
{
#ifdef EMBLUA
public:
        EmbLua();
        ~EmbLua();

        void        ZoneInitialize();
        void        ZoneShutdown();

        void        HandleSpawn(uint32 spawn_id);
        void        HandleDepop(uint32 spawn_id);
        void        ReloadSpawns();

        void        GetWaypoint(NPC* npc, int32 grid);
        void        ReloadPaths();

        void        RunString(Client* c, const char* command_string);

private:
        void        RegisterFuncs();

protected:
        lua_State* L;

#else //EMBLUA not defined
        //these are just here to save us having to put #ifdef EMBLUA before every call to Lua elsewhere in the code
public:
        void ZoneInitialize() { }
        void ZoneShutdown() { }
        void HandleSpawn(uint32 spawn_id) { }
        void HandleDepop(uint32 spawn_id) { }
        void GetWaypoint(NPC* npc, int32 grid) { }
#endif

};

#endif

emblua.cpp
Code:

#ifdef EMBLUA
#include "emblua.h"
#include "zone.h"

#include "lua_mob.h"
#include "lua_general.h"
#include "lua_spawn.h"

extern Zone* zone;

EmbLua::EmbLua() {
        //interpreter is not loaded until a zone actually boots - initialize to null for now
        L = 0;
}
EmbLua::~EmbLua() {

}

void EmbLua::ZoneInitialize() {

        L = luaL_newstate(); //load the interpreter
        luaL_openlibs(L); //and the standard libraries

        // Add module paths
        lua_getglobal(L,"package");
        lua_getfield(L,-1,"path");
        char module_path[512];
        sprintf(module_path,"%s;%s",lua_tostring(L,-1),"quests/modules/?.lua");
        lua_pop(L,1);
        lua_pushstring(L,(const char*)module_path);
        lua_setfield(L,-2,"path");
        lua_pop(L,1);


        // remove OS library for security
        lua_pushnil(L);
        lua_setglobal(L,"os");

        // Register Mob and General functions
        RegisterFuncs();

        const char* zonename = zone->GetShortName();

        // load wander paths
        char wander[64];
        sprintf(wander,"quests/%s/path_list.lua",zonename);
        if (luaL_dofile(L,(const char*)wander) != 0) {
                lua_pop(L,1);
        }

        // load spawnpoints
        char spawn[64];
        sprintf(spawn,"quests/%s/spawn_list.lua",zonename);
        if (luaL_dofile(L,(const char*)spawn) != 0) {
                lua_pop(L,1);
        }
        database.PopulateZoneSpawnList(zonename,zone->luaspawn_list,zone->GetInstanceVersion());

        // clear the stack
        lua_settop(L,0);
}

void EmbLua::ZoneShutdown() {
        // free the memory being used by the interpreter
        lua_close(L);
}

void EmbLua::RegisterFuncs() {

        // general functions from lua_general.h
        lua_getglobal(L,"_G");
        luaL_register(L,NULL,GeneralFuncs);
        lua_pop(L,1);

        // mob functions from lua_mob.h
        luaL_register(L,"_MobFuncs",MobFuncs);
        lua_pushvalue(L,-1); //copy table; tbl -1, tbl -2
        lua_setfield(L,-2,"__index"); //set _MobFuncs.__index = _MobFuncs; tbl -1 remaining
        lua_pop(L,1); //clear tbl from stack

        // add function to handle mob identify comparisons
        // lua handles mobs as pointers-to-pointers -- they can't be directly compared (x == y) without this
        luaL_dostring(L,"_MobFuncs.__eq = function(a,b) if a:GetID() == b:GetID() then return true end return false end");

        // seed randomness
        lua_getglobal(L,"math"); //get math library
        lua_getfield(L,-1,"randomseed"); //retrieve randomseed function from it
        if (lua_isfunction(L,-1)) {
                lua_pushnumber(L,MakeRandomFloat(0,999999));
                lua_pcall(L,1,0,0); //call math.randomseed with a random float as the argument
        }

        // clear the stack
        lua_settop(L,0);
}

void EmbLua::HandleSpawn(uint32 spawn_id) {
        lua_getglobal(L,"spawn_list");

        if (lua_istable(L,-1)) { //master list found
                lua_pushinteger(L,spawn_id);
                lua_gettable(L,-2);

                if (lua_istable(L,-1)) { //spawn entry found
                        lua_pushinteger(L,1);
                        lua_gettable(L,-2);

                        if (lua_istable(L,-1)) { //direct NPC table found
                                lua_getglobal(L,"spawn"); //get spawn function defined in lua_general.h

                                if (lua_isfunction(L,-1)) {
                                        lua_insert(L,-2); //table -1, func -2
                                        lua_pushinteger(L,spawn_id); //id -1, table -2, func -3
                                        lua_pcall(L,2,0,0); //call spawn(table,id)
                                }
                        }
                        else if (lua_isfunction(L,-1)) { //NPC-selecting function found
                                lua_pcall(L,0,1,0); //call the function and ask for 1 result

                                if (lua_istable(L,-1)) { //NPC table was returned
                                        lua_getglobal(L,"spawn"); //get spawn function defined in lua_general.h

                                        if (lua_isfunction(L,-1)) {
                                                lua_insert(L,-2); //table -1, func -2
                                                lua_pushinteger(L,spawn_id); //id -1, table -2, func -3
                                                lua_pcall(L,2,0,0); //call spawn(table,id)
                                        }
                                }
                                else { //returned nothing, reset spawn timer
                                        HandleDepop(spawn_id);
                                }
                        }
                }
                else { //spawn entry does not exist, remove it from DB
                        char errbuf[MYSQL_ERRMSG_SIZE];
                        char* query = 0;
                        database.RunQuery(query,MakeAnyLenString(&query,"DELETE FROM lua_spawn WHERE zone = '%s' AND id = %i AND instance = %i",zone->GetShortName(),spawn_id,zone->GetInstanceVersion()),errbuf);       
                        safe_delete_array(query);
                }
        }
        lua_settop(L,0);
}

void EmbLua::HandleDepop(uint32 spawn_id) {
        lua_getglobal(L,"spawn_list");

        if (lua_istable(L,-1)) { //master list found
                lua_pushinteger(L,spawn_id);
                lua_gettable(L,-2);

                if (lua_istable(L,-1)) { //spawn entry found
                        uint32 timestamp = 0xFFFFFFFF;
                        lua_pushinteger(L,2);
                        lua_gettable(L,-2);
                        timeval t;
                        gettimeofday(&t,NULL);

                        if (lua_isnumber(L,-1)) { //direct respawn time found
                                timestamp = t.tv_sec + lua_tointeger(L,-1);
                        }
                        else if (lua_isfunction(L,-1)) { //respawn time calculating function found
                                lua_pcall(L,0,1,0); //call the function and ask for 1 result

                                if (lua_isnumber(L,-1)) { //a number was returned
                                        timestamp = t.tv_sec + lua_tointeger(L,-1);
                                }
                        }

                        char errbuf[MYSQL_ERRMSG_SIZE];
                        char* query = 0;
                        database.RunQuery(query,MakeAnyLenString(&query,"UPDATE lua_spawn SET spawntime = %i WHERE zone = '%s' AND id = %i AND instance = %i",timestamp,zone->GetShortName(),spawn_id,zone->GetInstanceVersion()),errbuf);
                        safe_delete_array(query);

                        if (timestamp != 0xFFFFFFFF) {
                                LuaSpawn* respawn = new LuaSpawn(spawn_id,timestamp);
                                zone->luaspawn_list.Insert(respawn);
                        }
                }
                else { //spawn entry does not exist, remove it from DB
                        char errbuf[MYSQL_ERRMSG_SIZE];
                        char* query = 0;
                        database.RunQuery(query,MakeAnyLenString(&query,"DELETE FROM lua_spawn WHERE zone = '%s' AND id = %i AND instance = %i",zone->GetShortName(),spawn_id,zone->GetInstanceVersion()),errbuf);       
                        safe_delete_array(query);
                }
        }
        // clear the stack
        lua_settop(L,0);
}

void EmbLua::ReloadSpawns() {
        // clear the stack
        lua_settop(L,0);

        char path[64];
        sprintf(path,"quests/%s/spawn_list.lua",zone->GetShortName());
        if (luaL_dofile(L,(const char*)path) != 0) {
                entity_list.MessageStatus(0,100,0,"ReloadSpawns Lua Error: %s",lua_tostring(L,-1));
                lua_pop(L,1);
                return;
        }

        // create an ad-hoc helper function
        luaL_dostring(L,"function _reload_spawns() ret = {} for k in pairs(spawn_list) do table.insert(ret,k) end return ret end");
        lua_getglobal(L,"_reload_spawns");

        if (lua_isfunction(L,-1)) {
                lua_pcall(L,0,1,0); //call the helper function
                //result table simply contains spawn_list entries in a pure integer sequence
                //so we can be sure to find all the entries with a while loop

                if (lua_istable(L,-1)) {
                        //set up for the first loop call
                        int i = 1;
                        lua_pushinteger(L,i++);
                        lua_gettable(L,-2);

                        // the loop
                        while (lua_isnumber(L,-1)) {
                                uint32 id = lua_tointeger(L,-1);
                                char errbuf[MYSQL_ERRMSG_SIZE];
                                char* query = 0;
                                MYSQL_RES* result;
                                MYSQL_ROW row;

                                //this query could probably be better - we just want to know if the entry for this spawn already exists or not
                                if (database.RunQuery(query,MakeAnyLenString(&query,"SELECT COUNT(id) FROM lua_spawn WHERE zone = '%s' AND id = %i AND instance = %i",zone->GetShortName(),id,zone->GetInstanceVersion()),errbuf,&result)) {
                                        safe_delete_array(query);
                                        if ((row = mysql_fetch_row(result))) {
                                                if (atoi(row[0]) == 0) { //spawn point not inserted yet, add it
                                                        char* query2 = 0;
                                                        database.RunQuery(query2,MakeAnyLenString(&query2,"INSERT INTO lua_spawn SET zone = '%s', id = %i, instance = %i",zone->GetShortName(),id,zone->GetInstanceVersion()),errbuf);
                                                        safe_delete_array(query2);
                                                        LuaSpawn* newSpawn = new LuaSpawn(id);
                                                        zone->luaspawn_list.Insert(newSpawn);
                                                }
                                        }
                                        mysql_free_result(result);
                                }

                                lua_pop(L,1); //remove id
                                lua_pushinteger(L,i++);
                                lua_gettable(L,-2); //get next id, repeat loop
                        }
                }
        }
        lua_pushnil(L);
        lua_setglobal(L,"_reload_spawns"); //clean up the ad-hoc function

        // clear the stack on our way out
        lua_settop(L,0);
}

void EmbLua::GetWaypoint(NPC* npc, int32 grid) {
        lua_settop(L,0);
        lua_getglobal(L,"path_list");

        if (lua_istable(L,-1)) { //master list found
                lua_pushinteger(L,grid);
                lua_gettable(L,-2);

                if (lua_istable(L,-1)) { //path entry found
                        lua_pushinteger(L,2);
                        lua_gettable(L,-2);

                        if (lua_isfunction(L,-1)) { //point-selecting function found
                                //push arguments
                                lua_pushinteger(L,npc->GetCurWp());
                                lua_pushinteger(L,grid);
                                Mob** send_npc = (Mob**)lua_newuserdata(L,sizeof(Mob*));
                                *send_npc = npc->CastToMob();
                                lua_getglobal(L,"_MobFuncs");
                                lua_setmetatable(L,-2);

                                lua_pcall(L,3,2,0); //call function(cur_wp,grid_id,npc) and ask for 2 results

                                if (lua_isnumber(L,-2)) { //next wp index was returned
                                        int32 index = lua_tointeger(L,-2);
                                        uint32 pause = 100;

                                        if (lua_isnumber(L,-1)) { //pause time was also returned
                                                pause = lua_tointeger(L,-1);
                                        }

                                        lua_pop(L,2); //clear results from stack, left with path entry -1, master list -2
                                        lua_pushinteger(L,1);
                                        lua_gettable(L,-2);

                                        if (lua_istable(L,-1)) { //found wp locs list
                                                lua_pushinteger(L,index);
                                                lua_gettable(L,-2);

                                                if (lua_istable(L,-1)) { //found wp loc data for wp index
                                                        float x,y,z,h;
                                                        lua_pushinteger(L,1); lua_gettable(L,-2); x = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : npc->GetX(); lua_pop(L,1);
                                                        lua_pushinteger(L,2); lua_gettable(L,-2); y = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : npc->GetY(); lua_pop(L,1);
                                                        lua_pushinteger(L,3); lua_gettable(L,-2); z = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : npc->GetZ(); lua_pop(L,1);
                                                        lua_pushinteger(L,4); lua_gettable(L,-2); h = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : -1.0; lua_pop(L,1);
                                                        npc->LuaSetCurWP(index,x,y,z,h,pause);
                                                        return;
                                                }
                                        }
                                }
                        }
                }
        }
        //if we're still here, the npc has nowhere to go
        npc->SetRoamer(false);
        npc->SetLuaGridID(0);
}

void EmbLua::ReloadPaths() {
        char path[64];
        sprintf(path,"quests/%s/path_list.lua",zone->GetShortName());
        if (luaL_dofile(L,(const char*)path) != 0) {
                entity_list.MessageStatus(0,100,0,"ReloadPaths Lua Error: %s",lua_tostring(L,-1));
        }
        //clear the stack
        lua_settop(L,0);
}

void EmbLua::RunString(Client* c, const char* command_string) {

        //set the caller to a variable; the print() function uses this to display output to the user
        Mob** _self = (Mob**)lua_newuserdata(L,sizeof(Mob*));
        *_self = c->CastToMob();
        lua_getglobal(L,"_MobFuncs");
        lua_setmetatable(L,-2);
        lua_setglobal(L,"_self");
        lua_settop(L,0);

        if (luaL_loadstring(L,command_string) != 0) {
                c->Message(0,"#lua compile error: %s",lua_tostring(L,-1));
        }
        else if (lua_pcall(L,0,LUA_MULTRET,0) != 0) {
                c->Message(0,"#lua runtime error: %s",lua_tostring(L,-1));
        }
       
        // clear the stack
        lua_settop(L,0);
}

#endif

lua_general.h
Code:

#ifndef LUA_GENERAL_H
#define LUA_GENERAL_H

#include "masterentity.h"
#include "lua.hpp"

extern EntityList entity_list;

static int Lua_Spawn(lua_State* L) {
        int num_args = lua_gettop(L);
        if (!num_args || !lua_istable(L,1))
                return 0;

        int32 spawnid = (num_args > 1 && lua_isnumber(L,2)) ? lua_tointeger(L,2) : 0;

        NPCType* type = new NPCType;
        memset(type,0,sizeof(NPCType));
        float x, y, z, h;
        EmuAppearance anim;
        int16 path_grid;

        lua_getfield(L,1,"loc");
        if (lua_istable(L,-1)) {
                lua_pushinteger(L,1); lua_gettable(L,-2); x = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : 0.0f; lua_pop(L,1);
                lua_pushinteger(L,2); lua_gettable(L,-2); y = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : 0.0f; lua_pop(L,1);
                lua_pushinteger(L,3); lua_gettable(L,-2); z = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : 0.0f; lua_pop(L,1);
                lua_pushinteger(L,4); lua_gettable(L,-2); h = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : 0.0f; lua_pop(L,1);
        }
        else {
                lua_getfield(L,1,"x"); x = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : 0.0f; lua_pop(L,1);
                lua_getfield(L,1,"y"); y = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : 0.0f; lua_pop(L,1);
                lua_getfield(L,1,"z"); z = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : 0.0f; lua_pop(L,1);
                lua_getfield(L,1,"h"); h = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : 0.0f; lua_pop(L,1);
        }
        lua_pop(L,1); //remove loc table or nil if there wasn't one

        lua_getfield(L,1,"name"); strncpy(type->name,lua_isstring(L,-1) ? lua_tostring(L,-1) : "_",64); lua_pop(L,1);
        lua_getfield(L,1,"lastname"); if (lua_isstring(L,-1)) strncpy(type->lastname,lua_tostring(L,-1),70); lua_pop(L,1);
        lua_getfield(L,1,"level"); type->level = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 1; lua_pop(L,1);
        lua_getfield(L,1,"race"); type->race = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 1; lua_pop(L,1);
        lua_getfield(L,1,"class"); type->class_ = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 1; lua_pop(L,1);
        lua_getfield(L,1,"gender"); type->gender = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 2; lua_pop(L,1);
        lua_getfield(L,1,"hp"); type->max_hp = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 100; lua_pop(L,1);
        lua_getfield(L,1,"mana"); type->Mana = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"bodytype"); type->bodytype = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"size"); type->size = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 5; lua_pop(L,1);
        lua_getfield(L,1,"speed"); type->runspeed = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : 1.0f; lua_pop(L,1);
        lua_getfield(L,1,"light"); type->light = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"texture"); type->texture = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"helmtexture"); type->helmtexture = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : type->texture; lua_pop(L,1);
        lua_getfield(L,1,"ac"); type->AC = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"str"); type->STR = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"dex"); type->DEX = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"agi"); type->AGI = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"sta"); type->STA = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"cha"); type->CHA = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"int"); type->INT = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"wis"); type->WIS = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"cor"); type->Corrup = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"resists");
        if (lua_istable(L,-1)) {
                lua_pushinteger(L,1); lua_gettable(L,-2); type->MR = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
                lua_pushinteger(L,2); lua_gettable(L,-2); type->FR = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
                lua_pushinteger(L,3); lua_gettable(L,-2); type->CR = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
                lua_pushinteger(L,4); lua_gettable(L,-2); type->PR = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
                lua_pushinteger(L,5); lua_gettable(L,-2); type->DR = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        }
        else {
                type->MR = 0;
                type->FR = 0;
                type->CR = 0;
                type->PR = 0;
                type->DR = 0;
        }
        lua_pop(L,1);
        lua_getfield(L,1,"tint");
        if (lua_istable(L,-1)) {
                lua_pushinteger(L,1); lua_gettable(L,-2); type->armor_tint[0] = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
                lua_pushinteger(L,2); lua_gettable(L,-2); type->armor_tint[1] = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
                lua_pushinteger(L,3); lua_gettable(L,-2); type->armor_tint[2] = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
                lua_pushinteger(L,4); lua_gettable(L,-2); type->armor_tint[3] = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
                lua_pushinteger(L,5); lua_gettable(L,-2); type->armor_tint[4] = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
                lua_pushinteger(L,6); lua_gettable(L,-2); type->armor_tint[5] = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
                lua_pushinteger(L,7); lua_gettable(L,-2); type->armor_tint[6] = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
                lua_pushinteger(L,8); lua_gettable(L,-2); type->armor_tint[7] = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
                lua_pushinteger(L,9); lua_gettable(L,-2); type->armor_tint[8] = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        }
        lua_pop(L,1);
        lua_getfield(L,1,"hp_regen"); type->hp_regen = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"mana_regen"); type->mana_regen = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"see_invis"); type->see_invis = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"see_invis_undead"); type->see_invis_undead = lua_isboolean(L,-1) ? lua_toboolean(L,-1) : false; lua_pop(L,1);
        lua_getfield(L,1,"see_hide"); type->see_hide = lua_isboolean(L,-1) ? lua_toboolean(L,-1) : false; lua_pop(L,1);
        lua_getfield(L,1,"see_improved_hide"); type->see_improved_hide = lua_isboolean(L,-1) ? lua_toboolean(L,-1) : false; lua_pop(L,1);
        lua_getfield(L,1,"slow_mitigation"); type->slow_mitigation = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"aggro_range"); type->aggroradius = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 50; lua_pop(L,1);
        lua_getfield(L,1,"pri_texture"); type->d_meele_texture1 = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"sec_texture"); type->d_meele_texture2 = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"pri_meleetype"); type->prim_melee_type = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 28; lua_pop(L,1);
        lua_getfield(L,1,"sec_meleetype"); type->sec_melee_type = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 28; lua_pop(L,1);
        lua_getfield(L,1,"attack_count"); type->attack_count = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 1; lua_pop(L,1);
        lua_getfield(L,1,"anim"); anim = lua_isnumber(L,-1) ? (EmuAppearance)lua_tointeger(L,-1) : (EmuAppearance)0; lua_pop(L,1);
        lua_getfield(L,1,"underwater"); type->underwater = lua_isboolean(L,-1) ? lua_toboolean(L,-1) : false; lua_pop(L,1);
        lua_getfield(L,1,"npcid"); type->npc_id = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"path"); path_grid = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"max_dmg"); type->max_dmg = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        if (type->max_dmg > 0) {
                lua_getfield(L,1,"min_dmg"); type->min_dmg = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : type->max_dmg/2; lua_pop(L,1);
        }
        lua_getfield(L,1,"faction"); type->npc_faction_id = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"loot_table"); type->loottable_id = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"attack_speed"); type->attack_speed = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"trackable"); type->trackable = lua_isboolean(L,-1) ? lua_toboolean(L,-1) : true; lua_pop(L,1);
        lua_getfield(L,1,"accuracy"); type->accuracy_rating = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"adventure_template"); type->adventure_template = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"alt_currency_type"); type->alt_currency_type = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"merchant_type"); type->merchanttype = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
       
        lua_getfield(L,1,"scale_rate"); type->scalerate = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"spell_scale"); type->spellscale = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"heal_scale"); type->healscale = lua_isnumber(L,-1) ? lua_tonumber(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"qglobal"); type->qglobal = lua_isboolean(L,-1) ? lua_toboolean(L,-1) : false; lua_pop(L,1);
        lua_getfield(L,1,"maxlevel"); type->maxlevel = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"private_corpse"); type->private_corpse = lua_isboolean(L,-1) ? lua_toboolean(L,-1) : false; lua_pop(L,1);
        lua_getfield(L,1,"findable"); type->findable = lua_isboolean(L,-1) ? lua_toboolean(L,-1) : false; lua_pop(L,1);
        lua_getfield(L,1,"emote_id"); type->emoteid = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"spells_id"); type->npc_spells_id = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"npc_aggro"); type->npc_aggro = lua_isboolean(L,-1) ? lua_toboolean(L,-1) : false; lua_pop(L,1);
        lua_getfield(L,1,"npc_attacks"); strncpy(type->npc_attacks,lua_isstring(L,-1) ? lua_tostring(L,-1) : "",30); lua_pop(L,1);
        lua_getfield(L,1,"trap_template"); type->trap_template = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);

        lua_getfield(L,1,"beard"); type->beard = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"beard_color"); type->beardcolor = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"drakkin_details"); type->drakkin_details = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"drakkin_heritage"); type->drakkin_heritage = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"drakkin_tattoo"); type->drakkin_tattoo = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"eye_color"); type->eyecolor1 = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"eye_color2"); type->eyecolor2 = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"hair_color"); type->haircolor = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"hair_style"); type->hairstyle = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);
        lua_getfield(L,1,"face"); type->luclinface = lua_isnumber(L,-1) ? lua_tointeger(L,-1) : 0; lua_pop(L,1);


        NPC* npc = new NPC(type,0,x,y,z,h,0);
        npc->SaveGuardPointAnim(anim);
        npc->SetAppearance(anim);
        npc->AddLootTable();
        npc->SetLuaSpawnID(spawnid);

        entity_list.AddNPC(npc);
        entity_list.LimitAddNPC(npc);

        if (path_grid) {
                npc->SetRoamer(true);
                npc->SetLuaGridID(path_grid);
                npc->LuaGetNextWaypoint();
        }

        Mob** ptr = (Mob**)lua_newuserdata(L,sizeof(Mob*));
        *ptr = npc->CastToMob();
        lua_getglobal(L,"_MobFuncs");
        lua_setmetatable(L,-2);
        return 1;
}

int EntityList::LuaNPCList(lua_State* L) {
        LinkedListIterator<NPC*> iterator(npc_list);
        iterator.Reset();
        lua_newtable(L);
        int i = 1;

        while (iterator.MoreElements()) {
                lua_pushinteger(L,i++);
                Mob** mob = (Mob**)lua_newuserdata(L,sizeof(Mob*));
                *mob = iterator.GetData()->CastToMob();
                lua_getglobal(L,"_MobFuncs");
                lua_setmetatable(L,-2);
                lua_settable(L,-3);
                iterator.Advance();
        }
        return 1;
}
static int Lua_GetNPCList(lua_State* L) {
        return entity_list.LuaNPCList(L);
}
int EntityList::LuaClientList(lua_State* L) {
        LinkedListIterator<Client*> iterator(client_list);
        iterator.Reset();
        lua_newtable(L);
        int i = 1;

        while (iterator.MoreElements()) {
                lua_pushinteger(L,i++);
                Mob** mob = (Mob**)lua_newuserdata(L,sizeof(Mob*));
                *mob = iterator.GetData()->CastToMob();
                lua_getglobal(L,"_MobFuncs");
                lua_setmetatable(L,-2);
                lua_settable(L,-3);
                iterator.Advance();
        }
        return 1;
}
static int Lua_GetClientList(lua_State* L) {
        return entity_list.LuaClientList(L);
}
int EntityList::LuaCorpseList(lua_State* L) {
        LinkedListIterator<Corpse*> iterator(corpse_list);
        iterator.Reset();
        lua_newtable(L);
        int i = 1;

        while (iterator.MoreElements()) {
                lua_pushinteger(L,i++);
                Mob** mob = (Mob**)lua_newuserdata(L,sizeof(Mob*));
                *mob = iterator.GetData()->CastToMob();
                lua_getglobal(L,"_MobFuncs");
                lua_setmetatable(L,-2);
                lua_settable(L,-3);
                iterator.Advance();
        }
        return 1;
}
static int Lua_GetCorpseList(lua_State* L) {
        return entity_list.LuaCorpseList(L);
}
static int Lua_GetTime(lua_State* L) {
        lua_pushinteger(L,(int)time(NULL));
        return 1;
}
static int Lua_Print(lua_State* L) {
        if (!lua_gettop(L) || !lua_isstring(L,1))
                return 0;

        lua_getglobal(L,"_self");
        if (lua_isuserdata(L,-1)) {
                Mob* gm = *(Mob**)lua_touserdata(L,-1);
                if (gm && entity_list.IsMobInZone(gm)) {
                        gm->Message(0,lua_tostring(L,1));
                }
        }
        return 0;
}

//these pollute the global space, but there aren't too many of them
static const luaL_Reg GeneralFuncs[] = {
        {"spawn",Lua_Spawn},
        {"GetNPCList",Lua_GetNPCList},
        {"GetClientList",Lua_GetClientList},
        {"GetCorpseList",Lua_GetCorpseList},
        {"GetTime",Lua_GetTime},
        {"print",Lua_Print}, //only used for #lua commandbb
        {NULL,NULL}
};

#endif

lua_mob.h
Code:

#ifndef LUA_MOB_H
#define LUA_MOB_H

#include "masterentity.h"
#include "../common/races.h"
#include "lua.hpp"

//TEXT
static int Lua_GetName(lua_State* L) {
        int num_args = lua_gettop(L);
        if (!num_args || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                if (check->IsNPC()) {
                        if (num_args > 1 && lua_isboolean(L,2) && lua_toboolean(L,2)) {
                                lua_pushstring(L,check->GetName());
                        }
                        else {
                                lua_pushstring(L,check->GetCleanName());
                        }
                }
                else {
                        lua_pushstring(L,check->GetName());
                }
                return 1;
        }
        return 0;
}
static int Lua_GetOrigName(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushstring(L,check->GetOrigName());
                return 1;
        }
        return 0;
}
static int Lua_GetRaceName(lua_State* L) {
        if (!lua_gettop(L))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushstring(L,GetRaceName(check->GetRace()));
                return 1;
        }
        return 0;
}

//LOC
static int Lua_GetLoc(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_newtable(L);
                lua_pushinteger(L,1); lua_pushinteger(L,check->GetX()); lua_settable(L,-3);
                lua_pushinteger(L,2); lua_pushinteger(L,check->GetY()); lua_settable(L,-3);
                lua_pushinteger(L,3); lua_pushinteger(L,check->GetZ()); lua_settable(L,-3);
                lua_pushinteger(L,4); lua_pushinteger(L,check->GetHeading()); lua_settable(L,-3);
                return 1;
        }
        return 0;
}
static int Lua_GetX(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushinteger(L,check->GetX());
                return 1;
        }
        return 0;
}
static int Lua_GetY(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushinteger(L,check->GetY());
                return 1;
        }
        return 0;
}
static int Lua_GetZ(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushinteger(L,check->GetZ());
                return 1;
        }
        return 0;
}
static int Lua_GetGroundZ(lua_State* L) {
        int num_args = lua_gettop(L);
        if (!num_args || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushnumber(L,check->GetGroundZ((num_args > 1 && lua_isnumber(L,2)) ? lua_tonumber(L,2) : check->GetX(),(num_args > 2 && lua_isnumber(L,3)) ? lua_tonumber(L,3) : check->GetY(),(num_args > 3 && lua_isnumber(L,4)) ? lua_tonumber(L,4) : 0.000000));
                return 1;
        }
        return 0;
}
static int Lua_GetHeading(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushinteger(L,check->GetHeading());
                return 1;
        }
        return 0;
}
static int Lua_GetHeadingTo(lua_State* L) {
        int num_args = lua_gettop(L);
        if (num_args < 2 || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                float x = 0;
                float y = 0;
                if (lua_isuserdata(L,2)) {
                        Mob* targ = *(Mob**)lua_touserdata(L,2);
                        x = targ->GetX();
                        y = targ->GetY();
                }
                else if (num_args > 2 && lua_isnumber(L,2) && lua_isnumber(L,3)) {
                        x = lua_tonumber(L,2);
                        y = lua_tonumber(L,3);
                }
                lua_pushnumber(L,check->CalculateHeadingToTarget(x,y));
                return 1;
        }
        return 0;
}
                       
//COMPARISON/STATE
static int Lua_GetID(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushinteger(L,check->GetID());
                return 1;
        }
        return 0;
}
static int Lua_IsClient(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushboolean(L,check->IsClient());
                return 1;
        }
        return 0;
}
static int Lua_IsNPC(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushboolean(L,check->IsNPC());
                return 1;
        }
        return 0;
}
static int Lua_IsCorpse(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushboolean(L,check->IsCorpse());
                return 1;
        }
        return 0;
}
static int Lua_IsPet(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushboolean(L,check->IsPet());
                return 1;
        }
        return 0;
}
static int Lua_IsCharmed(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushboolean(L,check->IsCharmed());
                return 1;
        }
        return 0;
}
static int Lua_InZone(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        lua_pushboolean(L,entity_list.IsMobInZone(check));
        return 1;
}
static int Lua_IsInvul(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushboolean(L,check->GetInvul());
                return 1;
        }
        return 0;
}
static int Lua_InLos(lua_State* L) {
        int num_args = lua_gettop(L);
        if (num_args < 2 || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                if (lua_isuserdata(L,2)) {
                        Mob* targ = *(Mob**)lua_touserdata(L,2);
                        lua_pushboolean(L,check->CheckLosFN(targ));
                }
                else if (num_args > 3 && lua_isnumber(L,2) && lua_isnumber(L,3) && lua_isnumber(L,4)) {
                        lua_pushboolean(L,check->CheckLosFN(lua_tonumber(L,2),lua_tonumber(L,3),lua_tonumber(L,4),check->GetSize()));
                }
                return 1;
        }
        return 0;
}
static int Lua_IsEngaged(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushboolean(L,check->IsEngaged());
                return 1;
        }
        return 0;
}
static int Lua_IsMoving(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check) {
                lua_pushboolean(L,check->IsMoving());
                return 1;
        }
        return 0;
}
static int Lua_IsClientCorpse(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check && check->IsCorpse()) {
                lua_pushboolean(L,check->CastToCorpse()->IsPlayerCorpse());
                return 1;
        }
        return 0;
}

//SPAWN
static int Lua_Depop(lua_State* L) {
        if (!lua_gettop(L) || !lua_isuserdata(L,1))
                return 0;

        Mob* check = *(Mob**)lua_touserdata(L,1);
        if (check && check->IsNPC()) {
                check->Depop(true);
        }
        return 0;
}

static const luaL_Reg MobFuncs[] = {
        //TEXT
        {"GetName",Lua_GetName},
        {"GetOrigName",Lua_GetOrigName},
        {"GetRaceName",Lua_GetRaceName},
        //LOC
        {"GetLoc",Lua_GetLoc},
        {"GetX",Lua_GetX},
        {"GetY",Lua_GetY},
        {"GetZ",Lua_GetZ},
        {"GetGroundZ",Lua_GetGroundZ},
        {"GetHeading",Lua_GetHeading},
        {"GetHeadingTo",Lua_GetHeadingTo},
        //COMPARISON/STATE
        {"GetID",Lua_GetID},
        {"IsClient",Lua_IsClient},
        {"IsNPC",Lua_IsNPC},
        {"IsCorpse",Lua_IsCorpse},
        {"IsPet",Lua_IsPet},
        {"IsCharmed",Lua_IsCharmed},
        {"InZone",Lua_InZone},
        {"IsInvul",Lua_IsInvul},
        {"InLos",Lua_InLos},
        {"IsEngaged",Lua_IsEngaged},
        {"IsMoving",Lua_IsMoving},
        {"IsClientCorpse",Lua_IsClientCorpse},
        //SPAWN
        {"depop",Lua_Depop},
        {NULL,NULL}
};

#endif

lua_spawn.h
Code:

#ifndef LUA_SPAWN_H
#define LUA_SPAWN_H
/*
CREATE TABLE IF NOT EXISTS lua_spawn (
        id INT UNSIGNED NOT NULL DEFAULT 0,
        zone VARCHAR(32) NOT NULL,
        instance INT UNSIGNED NOT NULL DEFAULT 0,
        spawntime INT UNSIGNED NOT NULL DEFAULT 0,
        PRIMARY KEY (id, zone, instance)
);
*/

#include "../common/timer.h"

class LuaSpawn {
public:
        LuaSpawn(uint32 spawn_id, uint32 spawn_time = 0);
        ~LuaSpawn();

        bool Process();
private:
        uint32 id;
        uint32 spawn_time;
protected:
        Timer timer;
};

#endif

lua_spawn.cpp
Code:

#include "lua_spawn.h"
#include "zone.h"
#include "zonedb.h"
#include "emblua.h"

extern EmbLua* Lua;

LuaSpawn::LuaSpawn(uint32 spawn_id, uint32 in_spawn_time) : timer(100000) {
        id = spawn_id;
        spawn_time = in_spawn_time;

        timeval t;
        gettimeofday(&t,NULL);

        if (spawn_time == 0xFFFFFFFF) {
                //special disable timeleft
                timer.Disable();
        }
        else if (spawn_time > t.tv_sec) {
                //we have a timeleft from the DB
                int32 timeleft = (spawn_time - t.tv_sec)*1000;
                timer.Start(timeleft);
        }
        else {
                //no timeleft at all, reset to
                timer.Start(1000);
                timer.Trigger();
        }
}
LuaSpawn::~LuaSpawn() {

}

bool LuaSpawn::Process() {
        if (timer.Check()) {
                timer.Disable();
                Lua->HandleSpawn(id);
                return false;
        }
        return true;
}

bool ZoneDatabase::PopulateZoneSpawnList(const char* zone_name, LinkedList<LuaSpawn*> &luaspawn_list, int16 instance, int32 repopdelay) {
        char errbuf[MYSQL_ERRMSG_SIZE];
        char* query = 0;
        MYSQL_RES* result;
        MYSQL_ROW row;

        MakeAnyLenString(&query, "SELECT id, spawntime FROM lua_spawn WHERE zone='%s' AND instance=%i",zone_name,instance);       
        if (RunQuery(query, strlen(query), errbuf, &result))
        {
                safe_delete_array(query);
                while((row = mysql_fetch_row(result)))
                {
                        LuaSpawn* newSpawn = new LuaSpawn(atoi(row[0]), atoi(row[1]));       
                        luaspawn_list.Insert(newSpawn);
                }
                mysql_free_result(result);
        }
        else
        {
                LogFile->write(EQEMuLog::Error, "Error in PopulateZoneLists query '%s': %s", query, errbuf);
                safe_delete_array(query);
                return false;
        }
        return true;
}


Diffs:
Code:

Index: attack.cpp
===================================================================
--- attack.cpp        (revision 2473)
+++ attack.cpp        (working copy)
@@ -43,7 +43,9 @@
 #include "QuestParserCollection.h"
 #include "watermap.h"
 #include "worldserver.h"
+#include "emblua.h"
 extern WorldServer worldserver;
+extern EmbLua* Lua;
 
 #ifdef _WINDOWS
 #define snprintf        _snprintf
@@ -2320,6 +2322,10 @@
        if(killerMob && killerMob->GetTarget() == this) //we can kill things without having them targeted
                killerMob->SetTarget(NULL); //via AE effects and such..
 
+        if (lua_spawnid) {
+                Lua->HandleDepop(lua_spawnid);
+        }
+
        entity_list.UpdateFindableNPCState(this, true);
 }
 
Index: command.cpp
===================================================================
--- command.cpp        (revision 2473)
+++ command.cpp        (working copy)
@@ -65,11 +65,13 @@
 #include "guild_mgr.h"
 #include "titles.h"
 #include "../common/patches/patches.h"
+#include "emblua.h"
 
 // these should be in the headers...
 extern WorldServer worldserver;       
 extern bool spells_loaded;
 extern TaskManager *taskmanager;
+extern EmbLua* Lua;
 
 #include "QuestParserCollection.h"
 
@@ -428,6 +430,12 @@
                command_add("bot","- Type \"#bot help\" to the see the list of available commands for bots.", 0, command_bot) ||
 #endif
 
+#ifdef EMBLUA
+                command_add("reloadspawns","- Reloads LuaSpawns from /quests/<zone shortname>/spawn_list.lua",100,command_reloadspawns) ||
+                command_add("reloadpaths","- Reloads Lua path grids from /quests/<zone shortname>/path_list.lua",100,command_reloadpaths) ||
+                command_add("lua","- Runs the given string as a Lua chunk.",250,command_lua) ||
+#endif
+
                command_add("traindisc","[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)",150,command_traindisc) ||
                command_add("setgraveyard","[zone name] - Creates a graveyard for the specified zone based on your target's LOC.", 200, command_setgraveyard) ||
                command_add("deletegraveyard","[zone name] - Deletes the graveyard for the specified zone.", 200, command_deletegraveyard) ||
@@ -1890,7 +1898,7 @@
                //c->Message(0, "Weapon Item Number: %s",c->GetTarget()->GetWeapNo());
                c->Message(0, "Gender: %i  Size: %f  Bodytype: %d", c->GetTarget()->GetGender(), c->GetTarget()->GetSize(), c->GetTarget()->GetBodyType());
                c->Message(0, "Runspeed: %f  Walkspeed: %f", c->GetTarget()->GetRunspeed(), c->GetTarget()->GetWalkspeed());
-                c->Message(0, "Spawn Group: %i  Grid: %i", c->GetTarget()->CastToNPC()->GetSp2(), c->GetTarget()->CastToNPC()->GetGrid());
+                c->Message(0, "Spawn Group: %i  Grid: %i  LuaSpawn: %i  LuaGrid: %i", c->GetTarget()->CastToNPC()->GetSp2(), c->GetTarget()->CastToNPC()->GetGrid(), c->GetTarget()->CastToNPC()->GetLuaSpawnID(), c->GetTarget()->CastToNPC()->GetLuaGridID());
                c->Message(0, "EmoteID: %i", c->GetTarget()->CastToNPC()->GetNPCEmoteID());
                c->GetTarget()->CastToNPC()->QueryLoot(c);
        }
@@ -11777,4 +11785,31 @@
                if(c->GetTradeskillObject() != NULL)
                Object::HandleAugmentation(c, in_augment, c->GetTradeskillObject());
                safe_delete(in_augment);
-}
\ No newline at end of file
+}
+
+#ifdef EMBLUA
+
+void command_reloadspawns(Client *c, const Seperator *sep)
+{
+        c->Message(0,"Reloading spawns from Lua. Any new entries will be spawned immediately.");
+        Lua->ReloadSpawns();
+}
+
+void command_reloadpaths(Client *c, const Seperator *sep)
+{
+        c->Message(0,"Reloading paths from Lua.");
+        Lua->ReloadPaths();
+}
+
+void command_lua(Client *c, const Seperator *sep)
+{
+        if (sep->argnum < 1) {
+                c->Message(0,"Usage: #lua <string of lua code>");
+                c->Message(0,"Example: #lua for k in pairs(_G) do print(k) end");
+                c->Message(0,"Warning: the string must be a complete Lua chunk, multi-line input is not supported.");
+                return;
+        }
+        Lua->RunString(c,sep->argplus[1]);
+}
+
+#endif
Index: command.h
===================================================================
--- command.h        (revision 2473)
+++ command.h        (working copy)
@@ -347,5 +347,10 @@
 void command_bot(Client*c, const Seperator *sep);
 #endif
 
+#ifdef EMBLUA
+void command_reloadspawns(Client *c, const Seperator *sep);
+void command_reloadpaths(Client *c, const Seperator *sep);
+void command_lua(Client *c, const Seperator *sep);
 #endif
 
+#endif
Index: entity.h
===================================================================
--- entity.h        (revision 2473)
+++ entity.h        (working copy)
@@ -27,6 +27,9 @@
 #include "../common/servertalk.h"
 #include "../common/bodytypes.h"
 #include "QGlobals.h"
+#ifdef EMBLUA
+#include "lua.hpp"
+#endif
 
 // max number of newspawns to send per bulk packet
 #define SPAWNS_PER_POINT_DATARATE 10
@@ -401,6 +404,12 @@
        void RefreshAutoXTargets(Client *c);
        void RefreshClientXTargets(Client *c);
 
+#ifdef EMBLUA
+        int LuaNPCList(lua_State* L);
+        int LuaClientList(lua_State* L);
+        int LuaCorpseList(lua_State* L);
+#endif
+
 protected:
        friend class Zone;
        void        Depop(bool StartSpawnTimer = false);
Index: MobAI.cpp
===================================================================
--- MobAI.cpp        (revision 2473)
+++ MobAI.cpp        (working copy)
@@ -1544,6 +1544,7 @@
 
               
                int16 gridno = CastToNPC()->GetGrid();
+                int32 lua_gridid = CastToNPC()->GetLuaGridID();
 
                if (gridno > 0 || cur_wp==-2)  {
                        if (movetimercompleted==true) {  // time to pause at wp is over
@@ -1645,7 +1646,76 @@
                                CalculateNewWaypoint();
                        }
                }
+                else if (lua_gridid > 0) {
+                        //lua waypoints are partially reversed and simplified compared to sql-based waypoints, so we'll do them
+                        //separately rather than turn the standard waypoint code into swiss cheese
 
+                        if (movetimercompleted == true) {  // time to pause at wp is over
+                                movetimercompleted=false;
+                                       
+                                mlog(QUESTS__PATHING, "We are departing waypoint %d.", cur_wp);
+                                       
+                                if(GetAppearance() != eaStanding)
+                                        SetAppearance(eaStanding, false);
+                                       
+                                entity_list.OpenDoorsNear(CastToNPC());
+
+                                if(!DistractedFromGrid) {
+                                        //kick off event_waypoint depart
+                                        char temp[16];
+                                        sprintf(temp, "%d", cur_wp);
+                                        parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), NULL, temp, 0);
+                                }
+                                else {
+                                        DistractedFromGrid = false;
+                                }
+            }        // endif (movetimercompleted==true)   
+                        else if (!AIwalking_timer->Enabled())
+                        {        // currently moving
+                                if (cur_wp_x == GetX() && cur_wp_y == GetY())
+                                {        // are we there yet? then stop
+                                        mlog(AI__WAYPOINTS, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid());
+                                        if(GetAppearance() != eaStanding)
+                                                SetAppearance(eaStanding, false);
+                                        SetMoving(false);
+                                        if (cur_wp_heading >= 0.0) {
+                                                SetHeading(cur_wp_heading);
+                                        }
+                                        SendPosition();
+                                       
+                                        //kick off event_waypoint arrive
+                                        char temp[16];
+                                        sprintf(temp, "%d", cur_wp);
+                                        parse->EventNPC(EVENT_WAYPOINT_ARRIVE, CastToNPC(), NULL, temp, 0);
+                                       
+                                        // EverHood - wipe feign memory since we reached our first waypoint
+                                        if(cur_wp == 1)
+                                                ClearFeignMemory();
+
+                                        //setup our next waypoint
+                                        CastToNPC()->LuaGetNextWaypoint();
+                                }
+                                else
+                                {        // not at waypoint yet, so keep moving
+                                        if(!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0))
+                                                CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, walksp, true);
+                                        else
+                                        {
+                                                bool WaypointChanged;
+                                                bool NodeReached;
+                                                VERTEX Goal = UpdatePath(cur_wp_x, cur_wp_y, cur_wp_z, walksp, WaypointChanged, NodeReached);
+                                                if(WaypointChanged)
+                                                        tar_ndx = 20;
+
+                                                if(NodeReached)
+                                                        entity_list.OpenDoorsNear(CastToNPC());
+
+                                                CalculateNewPosition2(Goal.x, Goal.y, Goal.z, walksp, true);
+                                        }
+                                }
+                        }
+                } //if lua_gridid > 0
+
  }
  else if (IsGuarding())
  {
Index: net.cpp
===================================================================
--- net.cpp        (revision 2473)
+++ net.cpp        (working copy)
@@ -104,6 +104,7 @@
 #include "guild_mgr.h"
 #include "tasks.h"
 #include "QuestParserCollection.h"
+#include "emblua.h"
 
 TimeoutManager          timeout_manager;
 NetConnection                net;
@@ -124,6 +125,7 @@
 RuleManager *rules = new RuleManager();
 TaskManager *taskmanager = 0;
 QuestParserCollection *parse = 0;
+EmbLua* Lua = new EmbLua();
 
 bool zoneprocess;
 
Index: npc.cpp
===================================================================
--- npc.cpp        (revision 2473)
+++ npc.cpp        (working copy)
@@ -44,6 +44,7 @@
 #include "../common/MiscFunctions.h"
 #include "../common/rulesys.h"
 #include "StringIDs.h"
+#include "emblua.h"
 
 //#define SPELLQUEUE //Use only if you want to be spammed by spell testing
 
@@ -51,6 +52,7 @@
 extern Zone* zone;
 extern volatile bool ZoneLoaded;
 extern EntityList entity_list;
+extern EmbLua* Lua;
 #if !defined(NEW_LoadSPDat) && !defined(DB_LoadSPDat)
        extern SPDat_Spell_Struct spells[SPDAT_RECORDS];
 #endif
@@ -354,6 +356,10 @@
        guard_heading_saved = 0;
        InitializeBuffSlots();
        CalcBonuses();
+
+        //Lua-related
+        lua_spawnid = 0;
+        lua_gridid = 0;
 }
         
 NPC::~NPC()
@@ -744,6 +750,9 @@
                if (respawn2 != 0) {
                        respawn2->DeathReset();
                }
+                if (lua_spawnid) {
+                        Lua->HandleDepop(lua_spawnid);
+                }
        }
 }
 
Index: npc.h
===================================================================
--- npc.h        (revision 2473)
+++ npc.h        (working copy)
@@ -350,6 +350,15 @@
 
        void AddQuestItem(ItemInst* inst) { questItems.Insert(inst); }
 
+        //Lua-related
+        void SetLuaSpawnID(int32 id) { lua_spawnid = id; }
+        int32 GetLuaSpawnID() const { return lua_spawnid; }
+        void SetLuaGridID(int32 id) { lua_gridid = id; }
+        int32 GetLuaGridID() const { return lua_gridid; }
+        void LuaGetNextWaypoint();
+        void SetRoamer(bool in) { roamer = in; }
+        void LuaSetCurWP(int32 index, float x, float y, float z, float heading, uint32 pause);
+
        void ClearQuestLists()
        {
                ClearQuestItems(true);
@@ -551,6 +560,10 @@
        std::list<MercType> mercTypeList;
        std::list<MercData> mercDataList;
 
+        //Lua-related
+        int32 lua_spawnid;
+        int32 lua_gridid;
+
 private:
        uint32        loottable_id;
        bool        p_depop;
Index: waypoints.cpp
===================================================================
--- waypoints.cpp        (revision 2473)
+++ waypoints.cpp        (working copy)
@@ -35,7 +35,10 @@
 #include "../common/rulesys.h"
 #include "features.h"
 #include "QuestParserCollection.h"
+#include "emblua.h"
 
+extern EmbLua* Lua;
+
 struct wp_distance
 {
        float dist;
@@ -1406,3 +1409,20 @@
        guard_z = guard_z_saved;
        guard_heading = guard_heading_saved;
 }
+
+void NPC::LuaGetNextWaypoint() {
+        int32 grid = GetLuaGridID();
+        if (!grid) {
+                return;
+        }
+        Lua->GetWaypoint(this,grid);
+}
+
+void NPC::LuaSetCurWP(int32 index, float x, float y, float z, float heading, uint32 pause) {
+        cur_wp = index;
+        cur_wp_x = x;
+        cur_wp_y = y;
+        cur_wp_z = z;
+        cur_wp_heading = heading;
+        AIwalking_timer->Start(pause);
+}
\ No newline at end of file
Index: zone.cpp
===================================================================
--- zone.cpp        (revision 2473)
+++ zone.cpp        (working copy)
@@ -61,6 +61,8 @@
 #include "../common/rulesys.h"
 #include "guild_mgr.h"
 #include "QuestParserCollection.h"
+#include "emblua.h"
+#include "lua_spawn.h"
 
 #ifdef _WINDOWS
 #define snprintf        _snprintf
@@ -82,6 +84,7 @@
 extern QuestParserCollection* parse;
 extern DBAsyncFinishedQueue MTdbafq;
 extern DBAsync *dbasync;
+extern EmbLua* Lua;
 void CleanupLoadZoneState(uint32 spawn2_count, ZSDump_Spawn2** spawn2_dump, ZSDump_NPC** npc_dump, ZSDump_NPC_Loot** npcloot_dump, NPCType** gmspawntype_dump, Spawn2*** spawn2_loaded, NPC*** npc_loaded, MYSQL_RES** result);
 
 
@@ -875,6 +878,7 @@
        dbasync->CommitWrites();
    if(parse) { parse->ReloadQuests(true); }
        UpdateWindowTitle();
+        Lua->ZoneShutdown();
 }
 
 void Zone::LoadZoneDoors(const char* zone, int16 version)
@@ -1007,6 +1011,7 @@
        if(pQueuedMerchantsWorkID != 0)
                dbasync->CancelWork(pQueuedMerchantsWorkID);
        spawn2_list.Clear();
+        luaspawn_list.Clear();
        safe_delete(zonemap);
        safe_delete(watermap);
        safe_delete(pathing);
@@ -1046,6 +1051,11 @@
 //Modified for timezones.
 bool Zone::Init(bool iStaticZone) {
        SetStaticZone(iStaticZone);
+
+#ifdef EMBLUA
+        LogFile->write(EQEMuLog::Status, "Loading Lua...");
+        Lua->ZoneInitialize();
+#endif
       
        LogFile->write(EQEMuLog::Status, "Loading spawn conditions...");
        if(!spawn_conditions.LoadSpawnConditions(short_name, instanceid)) {
@@ -1344,6 +1354,20 @@
                                iterator.RemoveCurrent();
                        }
                }
+
+                //Lua-based spawns
+                LinkedListIterator<LuaSpawn*> iterator2(luaspawn_list);
+
+                iterator2.Reset();
+                while (iterator2.MoreElements()) {
+                        if (iterator2.GetData()->Process()) {
+                                iterator2.Advance();
+                        }
+                        else {
+                                iterator2.RemoveCurrent();
+                        }
+                }
+
                if(adv_data && !did_adventure_actions)
                {
                        DoAdventureActions();
Index: zone.h
===================================================================
--- zone.h        (revision 2473)
+++ zone.h        (working copy)
@@ -33,6 +33,7 @@
 #include "tasks.h"
 #include "pathing.h"
 #include "QGlobals.h"
+#include "lua_spawn.h"
 
 class Map;
 class WaterMap;
@@ -247,6 +248,7 @@
        void        DeleteQGlobal(std::string name, uint32 npcID, uint32 charID, uint32 zoneID);
 
        LinkedList<Spawn2*> spawn2_list;
+        LinkedList<LuaSpawn*> luaspawn_list;
        LinkedList<ZonePoint*> zone_point_list;
        uint32        numzonepoints;
       
Index: zonedb.h
===================================================================
--- zonedb.h        (revision 2473)
+++ zonedb.h        (working copy)
@@ -5,6 +5,7 @@
 #include "../common/eq_packet_structs.h"
 #include "loottable.h"
 #include "faction.h"
+#include "lua_spawn.h"
 //#include "doors.h"
 
 struct wplist {
@@ -302,6 +303,11 @@
        void        UpdateSpawn2Timeleft(uint32 id, uint16 instance_id,uint32 timeleft);
        uint32        GetSpawnTimeLeft(uint32 id, uint16 instance_id);
        void        UpdateSpawn2Status(uint32 id, uint8 new_status);
+
+        /*
+        * Lua-based Spawns and Spawn Points
+        */
+        bool        PopulateZoneSpawnList(const char* zonename, LinkedList<LuaSpawn*> &luaspawn_list, int16 instance, int32 repopdelay = 0);
       
        /*
          * Grids/Paths

Most of the action is in emblua.cpp; valid input for Lua-spawned NPCs is defined in a big block in lua_general.h. See the thread linked at the top of the post for example usage. If you do want to try it out, make sure to add the SQL table given in lua_spawn.h first.

Some basic script functions are included for use in spawn scripts (e.g. GetNPCList() and mob:GetName()), but they are pretty minimal thus far. Should be enough to give an idea of how to write functions exposing C++ to Lua, though.

Tabasco 02-03-2013 10:24 AM

Good work! I always enjoyed working with lua and I'm looking forward to hooking this into some kind of general modding framework.

Zaela_S 02-03-2013 10:59 AM

Oh, spotted one little mistake at the top of the first file, emblua.h:

Code:

#ifdef EMBLUA
#include "lua.hpp"
#include "masterentity.h"
#endif

should be

Code:

#ifdef EMBLUA
#include "lua.hpp"
#endif
#include "masterentity.h"


cavedude 02-03-2013 10:03 PM

Thank you for sharing! I'm very excited to get working on this.


Here are the required changes to CMake if anybody else wishes to try it out right away:

Code:

Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt        (revision 2480)
+++ CMakeLists.txt        (working copy)
@@ -91,11 +91,13 @@
 ADD_DEFINITIONS(-DSHAREMEM)
 ADD_DEFINITIONS(-DINVERSEXY)
 ADD_DEFINITIONS(-DFIELD_ITEMS)
+ADD_DEFINITIONS(-DEMBLUA)
 
 FIND_PACKAGE(ZLIB REQUIRED)
 FIND_PACKAGE(MySQL REQUIRED)
 FIND_PACKAGE(PerlLibs REQUIRED)
-INCLUDE_DIRECTORIES("${ZLIB_INCLUDE_DIRS}" "${PERL_INCLUDE_PATH}" "${MySQL_INCLUDE_DIR}")
+FIND_PACKAGE(Lua51 REQUIRED)
+INCLUDE_DIRECTORIES("${ZLIB_INCLUDE_DIRS}" "${PERL_INCLUDE_PATH}" "${MySQL_INCLUDE_DIR}" "${LUA_INCLUDE_DIR}")
 
 IF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN)
    ADD_SUBDIRECTORY(common)
Index: zone/CMakeLists.txt
===================================================================
--- zone/CMakeLists.txt        (revision 2480)
+++ zone/CMakeLists.txt        (working copy)
@@ -16,6 +16,7 @@
    command.cpp
    doors.cpp
    effects.cpp
+    emblua.cpp
    embparser.cpp
    embperl.cpp
    embxs.cpp
@@ -31,6 +32,7 @@
    horse.cpp
    inventory.cpp
    loottables.cpp
+    lua_spawn.cpp
    Map.cpp
    merc.cpp
    mob.cpp
@@ -100,6 +102,7 @@
    client_packet.h
    command.h
    doors.h
+    emblua.h
    embparser.h
    embperl.h
    embxs.h
@@ -114,6 +117,9 @@
    hate_list.h
    horse.h
    loottable.h
+    lua_general.h
+    lua_mob.h
+    lua_spawn.h
    map.h
    masterentity.h
    maxskill.h
@@ -161,7 +167,7 @@
 
 ADD_DEFINITIONS(-DZONE)
 
-TARGET_LINK_LIBRARIES(zone Common ${PERL_LIBRARY} debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE})
+TARGET_LINK_LIBRARIES(zone Common ${PERL_LIBRARY} ${LUA_LIBRARY} debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE})
 
 IF(MSVC)
    SET_TARGET_PROPERTIES(zone PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF")


c0ncrete 02-03-2013 10:12 PM

thanks to both of you. :)


All times are GMT -4. The time now is 01:21 AM.

Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.