Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Server Code Submissions

Reply
 
Thread Tools Display Modes
  #1  
Old 02-03-2013, 01:56 AM
Zaela_S
Hill Giant
 
Join Date: Jun 2012
Posts: 216
Default 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.
Reply With Quote
  #2  
Old 02-03-2013, 10:24 AM
Tabasco's Avatar
Tabasco
Discordant
 
Join Date: Sep 2009
Posts: 269
Default

Good work! I always enjoyed working with lua and I'm looking forward to hooking this into some kind of general modding framework.
__________________
http://dungeoncrawl.us.to
Reply With Quote
  #3  
Old 02-03-2013, 10:59 AM
Zaela_S
Hill Giant
 
Join Date: Jun 2012
Posts: 216
Default

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"
Reply With Quote
  #4  
Old 02-03-2013, 10:03 PM
cavedude's Avatar
cavedude
The PEQ Dude
 
Join Date: Apr 2003
Location: -
Posts: 1,988
Default

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")
Reply With Quote
  #5  
Old 02-03-2013, 10:12 PM
c0ncrete's Avatar
c0ncrete
Dragon
 
Join Date: Dec 2009
Posts: 719
Default

thanks to both of you.
__________________
I muck about @ The Forge.
say(rand 99>49?'try '.('0x'.join '',map{unpack 'H*',chr rand 256}1..2):'incoherent nonsense')while our $Noport=1;
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 09:24 PM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3