|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
02-03-2013, 01:56 AM
|
Hill Giant
|
|
Join Date: Jun 2012
Posts: 216
|
|
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.
|
|
|
|
02-03-2013, 10:24 AM
|
|
Discordant
|
|
Join Date: Sep 2009
Posts: 269
|
|
Good work! I always enjoyed working with lua and I'm looking forward to hooking this into some kind of general modding framework.
|
02-03-2013, 10:59 AM
|
Hill Giant
|
|
Join Date: Jun 2012
Posts: 216
|
|
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"
|
|
|
|
02-03-2013, 10:03 PM
|
|
The PEQ Dude
|
|
Join Date: Apr 2003
Location: -
Posts: 1,988
|
|
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")
|
|
|
|
02-03-2013, 10:12 PM
|
|
Dragon
|
|
Join Date: Dec 2009
Posts: 719
|
|
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;
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -4. The time now is 07:34 PM.
|
|
|
|
|
|
|
|
|
|
|
|
|