Browse Source

Merge branch 'master' of http://cutpon.com:3000/devn00b/EQ2EMu

LethalEncounter 3 years ago
parent
commit
3ea7ffebb6
33 changed files with 1509 additions and 127 deletions
  1. 7 0
      CMakeLists.txt
  2. 16 0
      EQ2/source/LoginServer/CMakeLists.txt
  3. 22 18
      EQ2/source/LoginServer/LWorld.cpp
  4. 3 3
      EQ2/source/LoginServer/LWorld.h
  5. 2 2
      EQ2/source/LoginServer/LoginDatabase.cpp
  6. 1 1
      EQ2/source/LoginServer/LoginDatabase.h
  7. 1 1
      EQ2/source/LoginServer/client.h
  8. 19 0
      EQ2/source/WorldServer/CMakeLists.txt
  9. 2 2
      EQ2/source/WorldServer/Combat.cpp
  10. 56 8
      EQ2/source/WorldServer/Commands/Commands.cpp
  11. 1 0
      EQ2/source/WorldServer/Commands/Commands.h
  12. 5 5
      EQ2/source/WorldServer/Entity.cpp
  13. 1 1
      EQ2/source/WorldServer/Entity.h
  14. 212 7
      EQ2/source/WorldServer/LuaFunctions.cpp
  15. 8 0
      EQ2/source/WorldServer/LuaFunctions.h
  16. 65 6
      EQ2/source/WorldServer/LuaInterface.cpp
  17. 11 1
      EQ2/source/WorldServer/LuaInterface.h
  18. 6 1
      EQ2/source/WorldServer/Spawn.cpp
  19. 64 44
      EQ2/source/WorldServer/SpellProcess.cpp
  20. 4 3
      EQ2/source/WorldServer/SpellProcess.h
  21. 858 1
      EQ2/source/WorldServer/Spells.cpp
  22. 10 1
      EQ2/source/WorldServer/Spells.h
  23. 1 0
      EQ2/source/WorldServer/Zone/mob_movement_manager.h
  24. 3 3
      EQ2/source/WorldServer/zoneserver.cpp
  25. 2 2
      EQ2/source/WorldServer/zoneserver.h
  26. 2 2
      EQ2/source/common/ConfigReader.cpp
  27. 1 1
      EQ2/source/common/servertalk.h
  28. 2 2
      EQ2/source/common/version.h
  29. 112 0
      cmake/FindMySQL.cmake
  30. BIN
      server/EQ2Login__Debug64.exe
  31. BIN
      server/EQ2World__Debug_x64.exe
  32. 9 9
      server/SpawnScripts/ThunderingSteppes/Brianna.lua
  33. 3 3
      server/SpawnScripts/ThunderingSteppes/Jacques.lua

+ 7 - 0
CMakeLists.txt

@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 2.8)
+project (eq2emu)
+
+set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
+
+add_subdirectory(EQ2/source/LoginServer)
+add_subdirectory(EQ2/source/WorldServer)

+ 16 - 0
EQ2/source/LoginServer/CMakeLists.txt

@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 2.8)
+project (LoginServer)
+
+file(GLOB LOGIN_SRCS "*.cpp" "*.h")
+file(GLOB COMMON_SRCS "../common/*.cpp" "../common/*.h")
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")  
+find_package(MySQL)
+find_package(Threads)
+find_package(ZLIB)
+
+add_definitions(-DEQ2 -DLOGIN)
+add_executable(login ${LOGIN_SRCS} ${COMMON_SRCS})
+
+target_include_directories(login PUBLIC ${MySQL_INCLUDE_DIRS} ../common/)
+target_link_libraries(login PUBLIC ${MySQL_LIBRARIES} ${ZLIB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
+

+ 22 - 18
EQ2/source/LoginServer/LWorld.cpp

@@ -396,8 +396,8 @@ bool LWorld::Process() {
 			if (pack->size != sizeof(ServerLSInfo_Struct)) {
 			if (pack->size != sizeof(ServerLSInfo_Struct)) {
 				this->Kick(ERROR_BADVERSION);
 				this->Kick(ERROR_BADVERSION);
 				ret = false;
 				ret = false;
-				struct in_addr  in;
-				in.s_addr = GetIP();
+				//struct in_addr  in;
+				//in.s_addr = GetIP();
 			}
 			}
 			else {
 			else {
 				ServerLSInfo_Struct* lsi = (ServerLSInfo_Struct*) pack->pBuffer;
 				ServerLSInfo_Struct* lsi = (ServerLSInfo_Struct*) pack->pBuffer;
@@ -406,14 +406,14 @@ bool LWorld::Process() {
 					cout << "To allow all world server versions to login, run query on your login database (alternatively replacing * with the database version if preferred): insert into login_versions set version = '*';" << endl;
 					cout << "To allow all world server versions to login, run query on your login database (alternatively replacing * with the database version if preferred): insert into login_versions set version = '*';" << endl;
 					this->Kick(ERROR_BADVERSION);
 					this->Kick(ERROR_BADVERSION);
 					ret = false;
 					ret = false;
-					struct in_addr  in;
-					in.s_addr = GetIP();
+					//struct in_addr  in;
+					//in.s_addr = GetIP();
 				}
 				}
 				else if (!SetupWorld(lsi->name, lsi->address, lsi->account, lsi->password, lsi->serverversion)) {
 				else if (!SetupWorld(lsi->name, lsi->address, lsi->account, lsi->password, lsi->serverversion)) {
 					this->Kick(ERROR_BADPASSWORD);
 					this->Kick(ERROR_BADPASSWORD);
 					ret = false;
 					ret = false;
-					struct in_addr  in;
-					in.s_addr = GetIP();
+					//struct in_addr  in;
+					//in.s_addr = GetIP();
 				}
 				}
 				else{
 				else{
 					isAuthenticated = true;
 					isAuthenticated = true;
@@ -461,8 +461,8 @@ bool LWorld::Process() {
 			}
 			}
 			ServerSyncWorldList_Struct* sswls = (ServerSyncWorldList_Struct*) pack->pBuffer;
 			ServerSyncWorldList_Struct* sswls = (ServerSyncWorldList_Struct*) pack->pBuffer;
 			if (!CheckServerName(sswls->name)) {
 			if (!CheckServerName(sswls->name)) {
-				struct in_addr  in;
-				in.s_addr = sswls->ip;
+				//struct in_addr  in;
+				//in.s_addr = sswls->ip;
 				break; // Someone needs to tell the other login server to update it's exe!
 				break; // Someone needs to tell the other login server to update it's exe!
 			}
 			}
 			LWorld* world = world_list.FindByLink(this->Link, sswls->RemoteID);
 			LWorld* world = world_list.FindByLink(this->Link, sswls->RemoteID);
@@ -621,7 +621,7 @@ void LWorld::SendPacket(ServerPacket* pack) {
 	}
 	}
 }
 }
 
 
-void LWorld::Message(char* to, char* message, ...) {
+void LWorld::Message(const char* to, const char* message, ...) {
 	va_list argptr;
 	va_list argptr;
 	char buffer[256];
 	char buffer[256];
 
 
@@ -637,7 +637,7 @@ void LWorld::Message(char* to, char* message, ...) {
 	delete pack;
 	delete pack;
 }
 }
 
 
-void LWorld::Kick(char* message, bool iSetKickedFlag) {
+void LWorld::Kick(const char* message, bool iSetKickedFlag) {
 	if (iSetKickedFlag)
 	if (iSetKickedFlag)
 		kicked = true;
 		kicked = true;
 	if (message) {
 	if (message) {
@@ -649,10 +649,10 @@ void LWorld::Kick(char* message, bool iSetKickedFlag) {
 	if (Link && GetRemoteID() == 0)
 	if (Link && GetRemoteID() == 0)
 		Link->Disconnect();
 		Link->Disconnect();
 }
 }
-bool LWorld::CheckServerName(char* name) {
+bool LWorld::CheckServerName(const char* name) {
 	if (strlen(name) < 10)
 	if (strlen(name) < 10)
 		return false;
 		return false;
-	for (int i=0; i<strlen(name); i++) {
+	for (size_t i=0; i<strlen(name); i++) {
 		if (!((name[i] >= 'a' && name[i] <= 'z') || (name[i] >= 'A' && name[i] <= 'Z') || (name[i] >= '0' && name[i] <= '9') || name[i] == ' ' || name[i] == '\'' || name[i] == '-' || name[i] == '(' || name[i] == ')' || name[i] == '[' || name[i] == ']' || name[i] == '/' || name[i] == '.' || name[i] == ',' || name[i] == '_' || name[i] == '+' || name[i] == '=' || name[i] == ':' || name[i] == '~'))
 		if (!((name[i] >= 'a' && name[i] <= 'z') || (name[i] >= 'A' && name[i] <= 'Z') || (name[i] >= '0' && name[i] <= '9') || name[i] == ' ' || name[i] == '\'' || name[i] == '-' || name[i] == '(' || name[i] == ')' || name[i] == '[' || name[i] == ']' || name[i] == '/' || name[i] == '.' || name[i] == ',' || name[i] == '_' || name[i] == '+' || name[i] == '=' || name[i] == ':' || name[i] == '~'))
 			return false;
 			return false;
 	}
 	}
@@ -819,6 +819,7 @@ void LWorldList::KickGhostIP(int32 ip, LWorld* NotMe, int16 iClientPort) {
 				in.s_addr = world->GetIP();
 				in.s_addr = world->GetIP();
 				//				cout << "Removing GhostIP LWorld(" << world->GetID() << ") from ip: " << inet_ntoa(in) << " port: " << (int16)(world->GetPort());
 				//				cout << "Removing GhostIP LWorld(" << world->GetID() << ") from ip: " << inet_ntoa(in) << " port: " << (int16)(world->GetPort());
 				if (!world->Connected())
 				if (!world->Connected())
+				{
 					//					cout << " (it wasnt connected)";
 					//					cout << " (it wasnt connected)";
 					//				cout << endl;
 					//				cout << endl;
 					if (NotMe) {
 					if (NotMe) {
@@ -826,6 +827,7 @@ void LWorldList::KickGhostIP(int32 ip, LWorld* NotMe, int16 iClientPort) {
 						cout << "NotMe(" << NotMe->GetID() << ") = " << inet_ntoa(in) << ":" << NotMe->GetPort() << " (" << NotMe->GetClientPort() << ")" << endl;
 						cout << "NotMe(" << NotMe->GetID() << ") = " << inet_ntoa(in) << ":" << NotMe->GetPort() << " (" << NotMe->GetClientPort() << ")" << endl;
 					}
 					}
 					world->Kick("Ghost IP kick");
 					world->Kick("Ghost IP kick");
+				}
 			}
 			}
 		}
 		}
 	}
 	}
@@ -839,8 +841,8 @@ void LWorldList::KickGhost(ConType in_type, int32 in_accountid, LWorld* ButNotMe
 		LWorld* world = map_list->second;
 		LWorld* world = map_list->second;
 		if (!world->IsKicked() && world->GetType() == in_type && world != ButNotMe && (in_accountid == 0 || world->GetAccountID() == in_accountid)) {
 		if (!world->IsKicked() && world->GetType() == in_type && world != ButNotMe && (in_accountid == 0 || world->GetAccountID() == in_accountid)) {
 			if (world->GetIP() != 0) {
 			if (world->GetIP() != 0) {
-				struct in_addr  in;
-				in.s_addr = world->GetIP();
+				//struct in_addr  in;
+				//in.s_addr = world->GetIP();
 				//				cout << "Removing GhostAcc LWorld(" << world->GetID() << ") from ip: " << inet_ntoa(in) << " port: " << (int16)(world->GetPort()) << endl;
 				//				cout << "Removing GhostAcc LWorld(" << world->GetID() << ") from ip: " << inet_ntoa(in) << " port: " << (int16)(world->GetPort()) << endl;
 			}
 			}
 			if (world->GetType() == Login && world->IsOutgoingUplink()) {
 			if (world->GetType() == Login && world->IsOutgoingUplink()) {
@@ -915,8 +917,8 @@ void LWorldList::Process() {
 			continue;
 			continue;
 		}
 		}
 		else if (!world->Process()) {
 		else if (!world->Process()) {
-			struct in_addr  in;
-			in.s_addr = world->GetIP();
+			//struct in_addr  in;
+			//in.s_addr = world->GetIP();
 			if (world->GetAccountID() == 0 || !(world->ShowDown()) || world->GetType() == Chat) {
 			if (world->GetAccountID() == 0 || !(world->ShowDown()) || world->GetType() == Chat) {
 				map_list++;
 				map_list++;
 				worldmap.erase ( account_id );
 				worldmap.erase ( account_id );
@@ -1120,8 +1122,7 @@ EQ2Packet* LWorldList::MakeServerListPacket(int8 lsadmin, int16 version) {
 		return ServerListData[version];
 		return ServerListData[version];
 	}
 	}
 
 
-	LWorld* world = 0;
-	uchar num_servers[2];
+	//LWorld* world = 0;
 	int32 ServerNum = 0;
 	int32 ServerNum = 0;
 	/*	while(iterator.MoreElements()){
 	/*	while(iterator.MoreElements()){
 	world = iterator.GetData();
 	world = iterator.GetData();
@@ -1191,8 +1192,11 @@ EQ2Packet* LWorldList::MakeServerListPacket(int8 lsadmin, int16 version) {
 	}
 	}
 
 
 	EQ2Packet* pack = packet->serialize();
 	EQ2Packet* pack = packet->serialize();
+	#ifdef DEBUG
+	//Only dump these for people trying to debug this...
 	printf("WorldList:\n");
 	printf("WorldList:\n");
 	DumpPacket(pack->pBuffer, pack->size);
 	DumpPacket(pack->pBuffer, pack->size);
+	#endif
 	if (ServerListData.count(version))
 	if (ServerListData.count(version))
 	{
 	{
 		map<int32, EQ2Packet*>::iterator it = ServerListData.find(version);
 		map<int32, EQ2Packet*>::iterator it = ServerListData.find(version);

+ 3 - 3
EQ2/source/LoginServer/LWorld.h

@@ -50,11 +50,11 @@ public:
 	LWorld(TCPConnection* in_RemoteLink, int32 in_ip, int32 in_RemoteID, int32 in_accountid, char* in_accountname, char* in_worldname, char* in_address, sint32 in_status, int32 in_adminid, bool in_showdown, int8 in_authlevel, bool in_placeholder, int32 iLinkWorldID);
 	LWorld(TCPConnection* in_RemoteLink, int32 in_ip, int32 in_RemoteID, int32 in_accountid, char* in_accountname, char* in_worldname, char* in_address, sint32 in_status, int32 in_adminid, bool in_showdown, int8 in_authlevel, bool in_placeholder, int32 iLinkWorldID);
     ~LWorld();
     ~LWorld();
 
 
-	static bool CheckServerName(char* name);
+	static bool CheckServerName(const char* name);
 	
 	
 	bool	Process();
 	bool	Process();
 	void	SendPacket(ServerPacket* pack);
 	void	SendPacket(ServerPacket* pack);
-	void	Message(char* to, char* message, ...);
+	void	Message(const char* to, const char* message, ...);
 
 
 	bool	SetupWorld(char* in_worldname, char* in_worldaddress, char* in_account, char* in_password, char* in_version);
 	bool	SetupWorld(char* in_worldname, char* in_worldaddress, char* in_account, char* in_password, char* in_version);
 	void	UpdateStatus(sint32 in_status, sint32 in_players, sint32 in_zones) {
 	void	UpdateStatus(sint32 in_status, sint32 in_players, sint32 in_zones) {
@@ -91,7 +91,7 @@ public:
 	int8			GetWorldStatus();
 	int8			GetWorldStatus();
 
 
 	void			ChangeToPlaceholder();
 	void			ChangeToPlaceholder();
-	void			Kick(char* message = ERROR_GHOST, bool iSetKickedFlag = true);
+	void			Kick(const char* message = ERROR_GHOST, bool iSetKickedFlag = true);
 	inline bool		IsKicked()				{ return kicked; }
 	inline bool		IsKicked()				{ return kicked; }
 	inline bool		IsNeverKick()			{ return pNeverKick; }
 	inline bool		IsNeverKick()			{ return pNeverKick; }
 	inline  ConType	GetType()				{ return ptype; }
 	inline  ConType	GetType()				{ return ptype; }

+ 2 - 2
EQ2/source/LoginServer/LoginDatabase.cpp

@@ -229,7 +229,7 @@ void LoginDatabase::LoadCharacters(LoginAccount* acct, int16 version){
 			player->packet->setColorByName("unknown14", 0xFF, 0xFF, 0xFF);
 			player->packet->setColorByName("unknown14", 0xFF, 0xFF, 0xFF);
 
 
 			uchar tmp[] = {0xFF, 0xFF, 0xFF, 0x61, 0x00, 0x2C, 0x04, 0xA5, 0x09, 0x02, 0x0F, 0x00, 0x00};
 			uchar tmp[] = {0xFF, 0xFF, 0xFF, 0x61, 0x00, 0x2C, 0x04, 0xA5, 0x09, 0x02, 0x0F, 0x00, 0x00};
-			for(int y=0;y<sizeof(tmp);y++)
+			for(size_t y=0;y<sizeof(tmp);y++)
 				player->packet->setDataByName("unknown11", tmp[y], y);
 				player->packet->setDataByName("unknown11", tmp[y], y);
 
 
 			MYSQL_RES* result3 = query2.RunQuery2(Q_SELECT, "SELECT slot, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue from login_equipment where login_characters_id=%i order by slot",id);
 			MYSQL_RES* result3 = query2.RunQuery2(Q_SELECT, "SELECT slot, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue from login_equipment where login_characters_id=%i order by slot",id);
@@ -623,7 +623,7 @@ void LoginDatabase::GetServerAccounts(vector<LWorld*>* server_list){
 		server_list->push_back(world);
 		server_list->push_back(world);
 	}
 	}
 }
 }
-void LoginDatabase::SaveClientLog(char* type, char* message, char* player_name, int16 version){
+void LoginDatabase::SaveClientLog(const char* type, const char* message, const char* player_name, int16 version){
 	Query query;
 	Query query;
 	query.escaped_data1 = getEscapeString(message);
 	query.escaped_data1 = getEscapeString(message);
 	query.escaped_name = getEscapeString(player_name);
 	query.escaped_name = getEscapeString(player_name);

+ 1 - 1
EQ2/source/LoginServer/LoginDatabase.h

@@ -61,7 +61,7 @@ public:
 	int32 GetRaceID(char* name);
 	int32 GetRaceID(char* name);
 	void UpdateRaceID(char* name);
 	void UpdateRaceID(char* name);
 	bool DeleteCharacter(int32 account_id, int32 character_id, int32 server_id);
 	bool DeleteCharacter(int32 account_id, int32 character_id, int32 server_id);
-	void SaveClientLog(char* type, char* message, char* player_name, int16 version);
+	void SaveClientLog(const char* type, const char* message, const char* player_name, int16 version);
 	bool CheckVersion(char* version);
 	bool CheckVersion(char* version);
 	void GetLatestTableVersions(LatestTableVersions* table_versions);
 	void GetLatestTableVersions(LatestTableVersions* table_versions);
 	TableQuery* GetLatestTableQuery(int32 server_ip, char* name, int16 version);
 	TableQuery* GetLatestTableQuery(int32 server_ip, char* name, int16 version);

+ 1 - 1
EQ2/source/LoginServer/client.h

@@ -49,7 +49,7 @@ public:
 	int32	GetIP()    { return ip; }
 	int32	GetIP()    { return ip; }
 	int16	GetPort()  { return port; }
 	int16	GetPort()  { return port; }
 	int32	GetAccountID() { return account_id; }
 	int32	GetAccountID() { return account_id; }
-	char*	GetAccountName(){ return (char*)account_name.c_str(); }
+	const char*	GetAccountName(){ return (char*)account_name.c_str(); }
 	void	SetAccountName(const char* name){ account_name = string(name); }
 	void	SetAccountName(const char* name){ account_name = string(name); }
 	void	ProcessLogin(char* name, char* pass,int seq=0);
 	void	ProcessLogin(char* name, char* pass,int seq=0);
 	void	QueuePacket(EQ2Packet* app);
 	void	QueuePacket(EQ2Packet* app);

+ 19 - 0
EQ2/source/WorldServer/CMakeLists.txt

@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.8)
+project (WorldServer)
+
+file(GLOB WORLD_SRCS "*.cpp" "*.h" "*/*.cpp" "*/*.h")
+file(GLOB COMMON_SRCS "../common/*.cpp" "../common/*.h")
+file(GLOB LUA_SRCS "../LUA/*.c" "../LUA/*.h")
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")  
+find_package(MySQL)
+find_package(Threads)
+find_package(ZLIB)
+
+add_definitions(-DEQ2 -DWORLD)
+add_executable(eq2world ${WORLD_SRCS} ${COMMON_SRCS} ${LUA_SRCS})
+
+set(RECAST_LIBRARIES -L${CMAKE_SOURCE_DIR}/EQ2/source/depends/recastnavigation/RecastDemo/Build/gmake/lib/Debug -lDebugUtils -lDetour -lDetourCrowd -lDetourTileCache -lRecast)
+
+target_include_directories(eq2world PUBLIC ${MySQL_INCLUDE_DIRS} ../common/ ../depends/recastnavigation/Detour/Include)
+target_link_libraries(eq2world PUBLIC ${MySQL_LIBRARIES} ${ZLIB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${RECAST_LIBRARIES})
+

+ 2 - 2
EQ2/source/WorldServer/Combat.cpp

@@ -545,7 +545,7 @@ bool Entity::ProcAttack(Spawn* victim, int8 damage_type, int32 low_damage, int32
 	return true;
 	return true;
 }
 }
 
 
-bool Entity::SpellHeal(Spawn* target, float distance, LuaSpell* luaspell, string heal_type, int32 low_heal, int32 high_heal, int8 crit_mod, bool no_calcs){
+bool Entity::SpellHeal(Spawn* target, float distance, LuaSpell* luaspell, string heal_type, int32 low_heal, int32 high_heal, int8 crit_mod, bool no_calcs, string custom_spell_name){
 	 if(!target || !luaspell || !luaspell->spell)
 	 if(!target || !luaspell || !luaspell->spell)
 		return false;
 		return false;
 
 
@@ -673,7 +673,7 @@ bool Entity::SpellHeal(Spawn* target, float distance, LuaSpell* luaspell, string
 
 
 	target->GetZone()->TriggerCharSheetTimer();
 	target->GetZone()->TriggerCharSheetTimer();
 	if (heal_amt > 0)
 	if (heal_amt > 0)
-		GetZone()->SendHealPacket(this, target, type, heal_amt, luaspell->spell->GetName());
+		GetZone()->SendHealPacket(this, target, type, heal_amt, custom_spell_name.length() > 0 ? (char*)custom_spell_name.c_str() : luaspell->spell->GetName());
 	CheckProcs(PROC_TYPE_HEALING, target);
 	CheckProcs(PROC_TYPE_HEALING, target);
 	CheckProcs(PROC_TYPE_BENEFICIAL, target);
 	CheckProcs(PROC_TYPE_BENEFICIAL, target);
 
 

+ 56 - 8
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -154,6 +154,7 @@ Commands::Commands(){
 	spawn_set_values["holiday_flag"] = SPAWN_SET_VALUE_HOLIDAY_FLAG;
 	spawn_set_values["holiday_flag"] = SPAWN_SET_VALUE_HOLIDAY_FLAG;
 	spawn_set_values["merchant_min_level"] = SPAWN_SET_VALUE_MERCHANT_MIN_LEVEL;
 	spawn_set_values["merchant_min_level"] = SPAWN_SET_VALUE_MERCHANT_MIN_LEVEL;
 	spawn_set_values["merchant_max_level"] = SPAWN_SET_VALUE_MERCHANT_MAX_LEVEL;
 	spawn_set_values["merchant_max_level"] = SPAWN_SET_VALUE_MERCHANT_MAX_LEVEL;
+	spawn_set_values["skin_color"] = SPAWN_SET_SKIN_COLOR;
 
 
 	zone_set_values["expansion_id"] = ZONE_SET_VALUE_EXPANSION_ID;
 	zone_set_values["expansion_id"] = ZONE_SET_VALUE_EXPANSION_ID;
 	zone_set_values["name"] = ZONE_SET_VALUE_NAME;
 	zone_set_values["name"] = ZONE_SET_VALUE_NAME;
@@ -197,7 +198,9 @@ bool Commands::SetSpawnCommand(Client* client, Spawn* target, int8 type, const c
 		return false;
 		return false;
 	int32 val = 0;
 	int32 val = 0;
 	try{
 	try{
-		if(type != SPAWN_SET_VALUE_NAME && !(type >= SPAWN_SET_VALUE_SPAWN_SCRIPT && type <= SPAWN_SET_VALUE_SUB_TITLE) && !(type >= SPAWN_SET_VALUE_PREFIX && type <= SPAWN_SET_VALUE_EXPANSION_FLAG || type == SPAWN_SET_VALUE_HOLIDAY_FLAG))
+		if(type != SPAWN_SET_VALUE_NAME && 
+			!(type >= SPAWN_SET_VALUE_SPAWN_SCRIPT && type <= SPAWN_SET_VALUE_SUB_TITLE) && !(type >= SPAWN_SET_VALUE_PREFIX && type <= SPAWN_SET_VALUE_EXPANSION_FLAG || type == SPAWN_SET_VALUE_HOLIDAY_FLAG)
+			&& type != SPAWN_SET_SKIN_COLOR)
 			val = atoul(value);
 			val = atoul(value);
 	}
 	}
 	catch(...){
 	catch(...){
@@ -205,7 +208,7 @@ bool Commands::SetSpawnCommand(Client* client, Spawn* target, int8 type, const c
 			client->Message(CHANNEL_COLOR_RED, "Invalid numeric spawn value: %s", value);
 			client->Message(CHANNEL_COLOR_RED, "Invalid numeric spawn value: %s", value);
 		return false;
 		return false;
 	}
 	}
-	if(temporary && temp_value){
+	if(temporary){
 		char tmp[128] = {0};
 		char tmp[128] = {0};
 		switch(type){
 		switch(type){
 			case SPAWN_SET_VALUE_NAME:{
 			case SPAWN_SET_VALUE_NAME:{
@@ -500,8 +503,26 @@ bool Commands::SetSpawnCommand(Client* client, Spawn* target, int8 type, const c
 				target->SetMerchantLevelRange(target->GetMerchantMinLevel(), atoul(value));
 				target->SetMerchantLevelRange(target->GetMerchantMinLevel(), atoul(value));
 				break;
 				break;
 			}
 			}
+			case SPAWN_SET_SKIN_COLOR: {
+				if (target->IsNPC())
+				{
+					Seperator* skinsep = new Seperator(value, ' ', 3, 500, true);
+					if (skinsep->IsNumber(0) && skinsep->IsNumber(1) && skinsep->IsNumber(2))
+					{
+						EQ2_Color clr;
+						clr.red = atoul(skinsep->arg[0]);
+						clr.green = atoul(skinsep->arg[1]);
+						clr.blue = atoul(skinsep->arg[2]);
+
+						((Entity*)target)->SetSkinColor(clr);
+					}
+					safe_delete(skinsep);
+				}
+				break;
+			}
 
 
-			*temp_value = string(tmp);
+			if(temp_value)
+				*temp_value = string(tmp);
 		}
 		}
 	}
 	}
 	else{
 	else{
@@ -838,7 +859,28 @@ bool Commands::SetSpawnCommand(Client* client, Spawn* target, int8 type, const c
 				else
 				else
 					target->SetSpawnScript(value);
 					target->SetSpawnScript(value);
 				break;
 				break;
-												   }
+			}
+
+			case SPAWN_SET_SKIN_COLOR: {
+				if (target->IsNPC())
+				{
+					Seperator* skinsep = new Seperator(value, ' ', 3, 500, true);
+					if (skinsep->IsNumber(0) && skinsep->IsNumber(1) && skinsep->IsNumber(2))
+					{
+						EQ2_Color clr;
+						clr.red = atoul(skinsep->arg[0]);
+						clr.green = atoul(skinsep->arg[1]);
+						clr.blue = atoul(skinsep->arg[2]);
+
+						((Entity*)target)->SetSkinColor(clr);
+						Query replaceSkinQuery;
+						replaceSkinQuery.AddQueryAsync(0, &database, Q_DELETE, "delete from npc_appearance where spawn_id=%u and type='skin_color'", target->GetDatabaseID());
+						replaceSkinQuery.AddQueryAsync(0, &database, Q_INSERT, "insert into npc_appearance set spawn_id=%u, type='skin_color', red=%u, green=%u, blue=%u", target->GetDatabaseID(), clr.red, clr.green, clr.blue);
+					}
+					safe_delete(skinsep);
+				}
+				break;
+			}
 		}
 		}
 	}
 	}
 	return true;
 	return true;
@@ -1390,6 +1432,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 							item->save_needed = true;
 							item->save_needed = true;
 							client->QueuePacket(item->serialize(client->GetVersion(), false, client->GetPlayer()));
 							client->QueuePacket(item->serialize(client->GetVersion(), false, client->GetPlayer()));
 						}
 						}
+						else
+							LogWrite(PLAYER__ERROR, 0, "Command", "%s: Item %s (%i) attempted to be used, however display_charges is 0.", client->GetPlayer()->GetName(), item->name.c_str(), item->details.item_id);
 					}
 					}
 				}
 				}
 			}
 			}
@@ -3675,16 +3719,20 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 					else
 					else
 					{
 					{
 						string name = string(spawn->GetName());
 						string name = string(spawn->GetName());
-						if(SetSpawnCommand(client, spawn, set_type, sep->arg[1]))
+						if(SetSpawnCommand(client, spawn, set_type, sep->argplus[1]))
 						{
 						{
 							if (set_type == SPAWN_SET_VALUE_EXPANSION_FLAG || set_type == SPAWN_SET_VALUE_HOLIDAY_FLAG)
 							if (set_type == SPAWN_SET_VALUE_EXPANSION_FLAG || set_type == SPAWN_SET_VALUE_HOLIDAY_FLAG)
 							{
 							{
 								client->SimpleMessage(CHANNEL_COLOR_YELLOW, "A /reload spawns is required to properly update the spawns with the xpack/holiday flag.");
 								client->SimpleMessage(CHANNEL_COLOR_YELLOW, "A /reload spawns is required to properly update the spawns with the xpack/holiday flag.");
 							}
 							}
-							else if(set_type == SPAWN_SET_VALUE_NAME)
+							else if (set_type == SPAWN_SET_VALUE_NAME)
 							{
 							{
 								client->SimpleMessage(CHANNEL_COLOR_YELLOW, "New name will not be effective until zone reload.");
 								client->SimpleMessage(CHANNEL_COLOR_YELLOW, "New name will not be effective until zone reload.");
 							}
 							}
+							else if (set_type == SPAWN_SET_SKIN_COLOR)
+							{
+								client->Message(CHANNEL_COLOR_YELLOW, "Successfully set skin_color to R G B: %s.", sep->argplus[1]);
+							}
 							else if(set_type == SPAWN_SET_VALUE_LOCATION)
 							else if(set_type == SPAWN_SET_VALUE_LOCATION)
 							{
 							{
 								spawn->SetLocation(client->GetPlayer()->GetLocation());
 								spawn->SetLocation(client->GetPlayer()->GetLocation());
@@ -3706,7 +3754,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 								}
 								}
 								default:
 								default:
 								{
 								{
-									client->GetCurrentZone()->ApplySetSpawnCommand(client, spawn, set_type, sep->arg[1]);
+									client->GetCurrentZone()->ApplySetSpawnCommand(client, spawn, set_type, sep->argplus[1]);
 									break;
 									break;
 								}
 								}
 							}
 							}
@@ -4557,7 +4605,7 @@ void Commands::Command_CancelMaintained(Client* client, Seperator* sep)
 	//	if (spell && spell->GetSpellData()->friendly_spell)  -- NOTE::You can cancel hostile maintained spells, 
 	//	if (spell && spell->GetSpellData()->friendly_spell)  -- NOTE::You can cancel hostile maintained spells, 
 		                                                     // just not spelleffects/dets - Foof
 		                                                     // just not spelleffects/dets - Foof
 		//{
 		//{
-			if (!client->GetPlayer()->GetZone()->GetSpellProcess()->DeleteCasterSpell(mEffects.spell))
+			if (!client->GetPlayer()->GetZone()->GetSpellProcess()->DeleteCasterSpell(mEffects.spell, "canceled"))
 				client->Message(CHANNEL_COLOR_RED, "The maintained spell could not be cancelled.");
 				client->Message(CHANNEL_COLOR_RED, "The maintained spell could not be cancelled.");
 	//	}
 	//	}
 		//else
 		//else

+ 1 - 0
EQ2/source/WorldServer/Commands/Commands.h

@@ -515,6 +515,7 @@ private:
 #define SPAWN_SET_VALUE_MERCHANT_MIN_LEVEL  60
 #define SPAWN_SET_VALUE_MERCHANT_MIN_LEVEL  60
 #define SPAWN_SET_VALUE_MERCHANT_MAX_LEVEL  61
 #define SPAWN_SET_VALUE_MERCHANT_MAX_LEVEL  61
 #define SPAWN_SET_VALUE_HOLIDAY_FLAG		62
 #define SPAWN_SET_VALUE_HOLIDAY_FLAG		62
+#define SPAWN_SET_SKIN_COLOR				63
 
 
 #define ZONE_SET_VALUE_EXPANSION_ID			0
 #define ZONE_SET_VALUE_EXPANSION_ID			0
 #define ZONE_SET_VALUE_NAME					1
 #define ZONE_SET_VALUE_NAME					1

+ 5 - 5
EQ2/source/WorldServer/Entity.cpp

@@ -1135,7 +1135,7 @@ void Entity::DismissPet(NPC* pet, bool from_death) {
 	// Remove the spell maintained spell
 	// Remove the spell maintained spell
 	Spell* spell = master_spell_list.GetSpell(pet->GetPetSpellID(), pet->GetPetSpellTier());
 	Spell* spell = master_spell_list.GetSpell(pet->GetPetSpellID(), pet->GetPetSpellTier());
 	if (spell)
 	if (spell)
-		GetZone()->GetSpellProcess()->DeleteCasterSpell(this, spell);
+		GetZone()->GetSpellProcess()->DeleteCasterSpell(this, spell, from_death == true ? (string)"pet_death" : (string)"canceled");
 
 
 	if (pet->GetPetType() == PET_TYPE_CHARMED) {
 	if (pet->GetPetType() == PET_TYPE_CHARMED) {
 		PetOwner->SetCharmedPet(0);
 		PetOwner->SetCharmedPet(0);
@@ -1292,7 +1292,7 @@ int32 Entity::CheckWards(Entity* attacker, int32 damage, int8 damage_type) {
 			if (!ward->keepWard) {
 			if (!ward->keepWard) {
 				hasSpellBeenRemoved = true;
 				hasSpellBeenRemoved = true;
 				RemoveWard(spell->spell->GetSpellID());
 				RemoveWard(spell->spell->GetSpellID());
-				GetZone()->GetSpellProcess()->DeleteCasterSpell(spell);
+				GetZone()->GetSpellProcess()->DeleteCasterSpell(spell, "purged");
 			}
 			}
 		}
 		}
 		else {
 		else {
@@ -1317,12 +1317,12 @@ int32 Entity::CheckWards(Entity* attacker, int32 damage, int8 damage_type) {
 			if (this->IsPlayer())
 			if (this->IsPlayer())
 			{
 			{
 				Client* client = GetZone()->GetClientBySpawn(this);
 				Client* client = GetZone()->GetClientBySpawn(this);
-				client->Message(CHANNEL_COLOR_COMBAT, "%s intercepted some of the damage intended for you!", spell->caster->GetName());
+				client->Message(CHANNEL_COMBAT, "%s intercepted some of the damage intended for you!", spell->caster->GetName());
 			}
 			}
 			if (spell->caster && spell->caster->IsPlayer())
 			if (spell->caster && spell->caster->IsPlayer())
 			{
 			{
 				Client* client = GetZone()->GetClientBySpawn(spell->caster);
 				Client* client = GetZone()->GetClientBySpawn(spell->caster);
-				client->Message(CHANNEL_COLOR_COMBAT, "YOU intercept some of the damage intended for %s!", this->GetName());
+				client->Message(CHANNEL_COMBAT, "YOU intercept some of the damage intended for %s!", this->GetName());
 			}
 			}
 
 
 			if (attacker && spell->caster)
 			if (attacker && spell->caster)
@@ -1344,7 +1344,7 @@ int32 Entity::CheckWards(Entity* attacker, int32 damage, int8 damage_type) {
 		if (shouldRemoveSpell && !hasSpellBeenRemoved)
 		if (shouldRemoveSpell && !hasSpellBeenRemoved)
 		{
 		{
 			RemoveWard(spell->spell->GetSpellID());
 			RemoveWard(spell->spell->GetSpellID());
-			GetZone()->GetSpellProcess()->DeleteCasterSpell(spell);
+			GetZone()->GetSpellProcess()->DeleteCasterSpell(spell, "purged");
 		}
 		}
 
 
 		// Reset ward pointer
 		// Reset ward pointer

+ 1 - 1
EQ2/source/WorldServer/Entity.h

@@ -472,7 +472,7 @@ public:
 	void			RangeAttack(Spawn* victim, float distance, Item* weapon, Item* ammo, bool multi_attack = false);
 	void			RangeAttack(Spawn* victim, float distance, Item* weapon, Item* ammo, bool multi_attack = false);
 	bool			SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8 damage_type, int32 low_damage, int32 high_damage, int8 crit_mod = 0, bool no_calcs = false);
 	bool			SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8 damage_type, int32 low_damage, int32 high_damage, int8 crit_mod = 0, bool no_calcs = false);
 	bool			ProcAttack(Spawn* victim, int8 damage_type, int32 low_damage, int32 high_damage, string name, string success_msg, string effect_msg);
 	bool			ProcAttack(Spawn* victim, int8 damage_type, int32 low_damage, int32 high_damage, string name, string success_msg, string effect_msg);
-	bool            SpellHeal(Spawn* target, float distance, LuaSpell* luaspell, string heal_type, int32 low_heal, int32 high_heal, int8 crit_mod = 0, bool no_calcs = false);
+	bool            SpellHeal(Spawn* target, float distance, LuaSpell* luaspell, string heal_type, int32 low_heal, int32 high_heal, int8 crit_mod = 0, bool no_calcs = false, string custom_spell_name="");
 	int8			DetermineHit(Spawn* victim, int8 damage_type, float ToHitBonus, bool spell);
 	int8			DetermineHit(Spawn* victim, int8 damage_type, float ToHitBonus, bool spell);
 	float			GetDamageTypeResistPercentage(int8 damage_type);
 	float			GetDamageTypeResistPercentage(int8 damage_type);
 	Skill*			GetSkillByWeaponType(int8 type, bool update);
 	Skill*			GetSkillByWeaponType(int8 type, bool update);

+ 212 - 7
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -37,6 +37,7 @@
 #include "Transmute.h"
 #include "Transmute.h"
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/algorithm/string/predicate.hpp>
 #include <sstream> 
 #include <sstream> 
+#include <boost/algorithm/string.hpp>
 
 
 extern MasterFactionList master_faction_list;
 extern MasterFactionList master_faction_list;
 extern WorldDatabase database;
 extern WorldDatabase database;
@@ -353,9 +354,11 @@ int EQ2Emu_lua_SpawnSet(lua_State* state) {
 	Spawn* spawn = lua_interface->GetSpawn(state);
 	Spawn* spawn = lua_interface->GetSpawn(state);
 	string variable = lua_interface->GetStringValue(state, 2);
 	string variable = lua_interface->GetStringValue(state, 2);
 	string value = lua_interface->GetStringValue(state, 3);
 	string value = lua_interface->GetStringValue(state, 3);
+	bool no_update = lua_interface->GetBooleanValue(state, 4); // send update is true by default in SetSpawnCommand, so allow user to specify 'true' to disable send update.
+	bool temporary_flag = lua_interface->GetBooleanValue(state, 5); // default false as originally designed, allow user to set temporary_flag true to not update DB
 	int32 type = commands.GetSpawnSetType(variable);
 	int32 type = commands.GetSpawnSetType(variable);
 	if (type != 0xFFFFFFFF && value.length() > 0 && spawn)
 	if (type != 0xFFFFFFFF && value.length() > 0 && spawn)
-		commands.SetSpawnCommand(0, spawn, type, value.c_str());
+		commands.SetSpawnCommand(0, spawn, type, value.c_str(), !no_update, temporary_flag);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1116,13 +1119,14 @@ int EQ2Emu_lua_SpellHeal(lua_State* state) {
 	Spawn* target = lua_interface->GetSpawn(state, 4);
 	Spawn* target = lua_interface->GetSpawn(state, 4);
 	int8 crit_mod = lua_interface->GetInt32Value(state, 5);
 	int8 crit_mod = lua_interface->GetInt32Value(state, 5);
 	bool no_calcs = lua_interface->GetInt32Value(state, 6) == 1;
 	bool no_calcs = lua_interface->GetInt32Value(state, 6) == 1;
+	string custom_spell_name = lua_interface->GetStringValue(state, 7);//custom spell name
 	lua_interface->ResetFunctionStack(state);
 	lua_interface->ResetFunctionStack(state);
 	if (caster && caster->IsEntity()) {
 	if (caster && caster->IsEntity()) {
 		bool success = false;
 		bool success = false;
 		luaspell->resisted = false;
 		luaspell->resisted = false;
 		if (target) {
 		if (target) {
 			float distance = caster->GetDistance(target, true);
 			float distance = caster->GetDistance(target, true);
-			if (((Entity*)caster)->SpellHeal(target, distance, luaspell, heal_type, min_heal, max_heal, crit_mod, no_calcs))
+			if (((Entity*)caster)->SpellHeal(target, distance, luaspell, heal_type, min_heal, max_heal, crit_mod, no_calcs, custom_spell_name))
 				success = true;
 				success = true;
 		}
 		}
 		if (luaspell->targets.size() > 0) {
 		if (luaspell->targets.size() > 0) {
@@ -1132,7 +1136,7 @@ int EQ2Emu_lua_SpellHeal(lua_State* state) {
 			for (int32 i = 0; i < luaspell->targets.size(); i++) {
 			for (int32 i = 0; i < luaspell->targets.size(); i++) {
 				if ((target = zone->GetSpawnByID(luaspell->targets[i]))) {
 				if ((target = zone->GetSpawnByID(luaspell->targets[i]))) {
 					float distance = caster->GetDistance(target, true);
 					float distance = caster->GetDistance(target, true);
-					((Entity*)caster)->SpellHeal(target, distance, luaspell, heal_type, min_heal, max_heal, crit_mod, no_calcs);
+					((Entity*)caster)->SpellHeal(target, distance, luaspell, heal_type, min_heal, max_heal, crit_mod, no_calcs, custom_spell_name);
 				}
 				}
 			}
 			}
 			luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
 			luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
@@ -1424,6 +1428,7 @@ int EQ2Emu_lua_CastSpell(lua_State* state) {
 	int32 spell_id = lua_interface->GetInt32Value(state, 2);
 	int32 spell_id = lua_interface->GetInt32Value(state, 2);
 	int8 spell_tier = lua_interface->GetInt8Value(state, 3);
 	int8 spell_tier = lua_interface->GetInt8Value(state, 3);
 	Spawn* caster = lua_interface->GetSpawn(state, 4);
 	Spawn* caster = lua_interface->GetSpawn(state, 4);
+	int16 custom_cast_time = lua_interface->GetInt16Value(state, 5);
 
 
 	if (!target) {
 	if (!target) {
 		lua_interface->LogError("%s: LUA CastSpell command error: target is not a valid spawn", lua_interface->GetScriptName(state));
 		lua_interface->LogError("%s: LUA CastSpell command error: target is not a valid spawn", lua_interface->GetScriptName(state));
@@ -1451,7 +1456,7 @@ int EQ2Emu_lua_CastSpell(lua_State* state) {
 	if (!caster)
 	if (!caster)
 		caster = target;
 		caster = target;
 
 
-	target->GetZone()->ProcessSpell(master_spell_list.GetSpell(spell_id, spell_tier), (Entity*)caster, (Entity*)target);
+	target->GetZone()->ProcessSpell(master_spell_list.GetSpell(spell_id, spell_tier), (Entity*)caster, (Entity*)target, true, false, NULL, custom_cast_time);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -2802,12 +2807,25 @@ int EQ2Emu_lua_SetStepComplete(lua_State* state) {
 	if (!lua_interface)
 	if (!lua_interface)
 		return 0;
 		return 0;
 	Spawn* player = lua_interface->GetSpawn(state);
 	Spawn* player = lua_interface->GetSpawn(state);
+	if (!player || !player->IsPlayer()) {
+		lua_interface->LogError("%s: LUA SetStepComplete command error: player is not valid", lua_interface->GetScriptName(state));
+		return 0;
+	}
 	int32 quest_id = lua_interface->GetInt32Value(state, 2);
 	int32 quest_id = lua_interface->GetInt32Value(state, 2);
+	if (quest_id <= 0) {
+		lua_interface->LogError("%s: LUA SetStepComplete command error: quest_id is not valid", lua_interface->GetScriptName(state));
+		return 0;
+	} else if ((((Player*)player)->player_quests.count(quest_id) <= 0)) {
+		lua_interface->LogError("%s: LUA SetStepComplete command error: player does not have quest", lua_interface->GetScriptName(state));
+		return 0;
+	}
 	int32 step = lua_interface->GetInt32Value(state, 3);
 	int32 step = lua_interface->GetInt32Value(state, 3);
-	if (player && player->IsPlayer() && quest_id > 0 && step > 0 && (((Player*)player)->player_quests.count(quest_id) > 0)) {
+	if (step > 0) {
 		Client* client = player->GetZone()->GetClientBySpawn(player);
 		Client* client = player->GetZone()->GetClientBySpawn(player);
 		if (client)
 		if (client)
 			client->AddPendingQuestUpdate(quest_id, step);
 			client->AddPendingQuestUpdate(quest_id, step);
+	} else {
+		lua_interface->LogError("%s: LUA SetStepComplete command error: step is not valid", lua_interface->GetScriptName(state));
 	}
 	}
 	return 0;
 	return 0;
 }
 }
@@ -2945,10 +2963,16 @@ int EQ2Emu_lua_HasQuest(lua_State* state) {
 	if (!lua_interface)
 	if (!lua_interface)
 		return 0;
 		return 0;
 	Spawn* player = lua_interface->GetSpawn(state);
 	Spawn* player = lua_interface->GetSpawn(state);
+	if(!player || !player->IsPlayer()) {
+		lua_interface->LogError("%s: LUA HasQuest command error: player is not valid", lua_interface->GetScriptName(state));
+		return 0;
+	}
 	int32 quest_id = lua_interface->GetInt32Value(state, 2);
 	int32 quest_id = lua_interface->GetInt32Value(state, 2);
-	if (player && player->IsPlayer() && quest_id > 0) {
+	if (quest_id > 0) {
 		lua_interface->SetBooleanValue(state, (((Player*)player)->player_quests.count(quest_id) > 0));
 		lua_interface->SetBooleanValue(state, (((Player*)player)->player_quests.count(quest_id) > 0));
 		return 1;
 		return 1;
+	} else {
+		lua_interface->LogError("%s: LUA HasQuest command error: quest_id is not valid", lua_interface->GetScriptName(state));
 	}
 	}
 	return 0;
 	return 0;
 }
 }
@@ -3063,8 +3087,15 @@ int EQ2Emu_lua_OfferQuest(lua_State* state) {
 		Quest* master_quest = master_quest_list.GetQuest(quest_id);
 		Quest* master_quest = master_quest_list.GetQuest(quest_id);
 		if (master_quest) {
 		if (master_quest) {
 			Client* client = player->GetZone()->GetClientBySpawn(player);
 			Client* client = player->GetZone()->GetClientBySpawn(player);
+			if (!client) {
+				lua_interface->LogError("%s: LUA OfferQuest command error: client is not set", lua_interface->GetScriptName(state));
+			}
 			Quest* quest = new Quest(master_quest);
 			Quest* quest = new Quest(master_quest);
-			if (client && quest) {				
+			if (!quest) {
+				lua_interface->LogError("%s: LUA OfferQuest command error: new Quest() failed.", lua_interface->GetScriptName(state));
+			}
+			if (client && quest) {
+				client->AddPendingQuest(quest);
 				if (npc)
 				if (npc)
 					quest->SetQuestGiver(npc->GetDatabaseID());
 					quest->SetQuestGiver(npc->GetDatabaseID());
 				else
 				else
@@ -3072,6 +3103,12 @@ int EQ2Emu_lua_OfferQuest(lua_State* state) {
 				client->AddPendingQuest(quest, forced);
 				client->AddPendingQuest(quest, forced);
 			}
 			}
 		}
 		}
+		else {
+			lua_interface->LogError("%s: LUA OfferQuest command error: failed to get quest %d", lua_interface->GetScriptName(state), quest_id);
+		}
+	}
+	else {
+		lua_interface->LogError("%s: LUA OfferQuest command error: player is not set or bad quest id %p %d", lua_interface->GetScriptName(state), player, quest_id);
 	}
 	}
 	return 0;
 	return 0;
 }
 }
@@ -10371,4 +10408,172 @@ int EQ2Emu_lua_GetTemporaryTransportID(lua_State* state) {
 		return 1;
 		return 1;
 	}
 	}
 	return 0;
 	return 0;
+}
+
+int EQ2Emu_lua_SetAlignment(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	sint32 alignment = lua_interface->GetSInt32Value(state, 2);
+	LuaSpell* spell = lua_interface->GetCurrentSpell(state);
+
+	if (!spawn) {
+		lua_interface->LogError("%s: LUA SetAlignment command error: spawn is not valid", lua_interface->GetScriptName(state));
+		return 0;
+	}
+
+	if (!spawn->IsEntity()) {
+		lua_interface->LogError("%s: LUA SetAlignment command error: spawn is not an entity", lua_interface->GetScriptName(state));
+		return 0;
+	}
+
+	if (alignment < SCHAR_MIN || alignment > SCHAR_MAX)
+	{
+		lua_interface->LogError("%s: LUA SetAlignment command error: alignment value beyond supported min: %i and max: %i", lua_interface->GetScriptName(state), SCHAR_MIN, SCHAR_MAX);
+		return 0;
+	}
+
+	if (spell && spell->targets.size() > 0) {
+		ZoneServer* zone = spell->caster->GetZone();
+		for (int8 i = 0; i < spell->targets.size(); i++) {
+			Spawn* target = zone->GetSpawnByID(spell->targets.at(i));
+			if (target->IsEntity()) {
+				((Entity*)target)->GetInfoStruct()->alignment = (sint8)alignment;
+				if (target->IsPlayer())
+					((Player*)target)->SetCharSheetChanged(true);
+			}
+		}
+	}
+	else {
+		((Entity*)spawn)->GetInfoStruct()->alignment = (sint8)alignment;
+		if (spawn->IsPlayer())
+			((Player*)spawn)->SetCharSheetChanged(true);
+	}
+	return 0;
+}
+
+int EQ2Emu_lua_GetAlignment(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+
+	if (!spawn) {
+		lua_interface->LogError("%s: LUA SetAlignment command error: spawn is not valid", lua_interface->GetScriptName(state));
+		return 0;
+	}
+
+	if (!spawn->IsEntity()) {
+		lua_interface->LogError("%s: LUA SetAlignment command error: spawn is not an entity", lua_interface->GetScriptName(state));
+		return 0;
+	}
+	
+	lua_interface->SetInt32Value(state, ((Entity*)spawn)->GetAlignment());
+	return 1;
+}
+
+
+int EQ2Emu_lua_GetSpell(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	int32 spell_id = lua_interface->GetInt32Value(state);
+	int8 spell_tier = lua_interface->GetInt8Value(state, 2);
+	if (spell_id > 0) {
+
+		if (spell_tier == 0)
+			spell_tier = 1;
+
+		Spell* spell = master_spell_list.GetSpell(spell_id, spell_tier);
+		LuaSpell* lua_spell = 0;
+		if (lua_interface)
+			lua_spell = lua_interface->GetSpell(spell->GetSpellData()->lua_script.c_str());
+
+		if (!lua_spell)
+			return 0;
+
+		lua_spell->spell = new Spell(spell);
+
+		lua_interface->SetSpellValue(state, lua_spell);
+		return 1;
+	}
+	return 0;
+}
+
+int EQ2Emu_lua_GetSpellData(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	LuaSpell* spell = lua_interface->GetSpell(state);
+	string field = lua_interface->GetStringValue(state, 2);
+
+	if (!spell) {
+		lua_interface->LogError("%s: Spell not given in GetSpellData!", lua_interface->GetScriptName(state));
+		return 0;
+	}
+	if (!spell->spell || !spell->spell->GetSpellData()) {
+		lua_interface->LogError("%s: Inner Spell or SpellData not given in GetSpellData!", lua_interface->GetScriptName(state));
+		return 0;
+	}
+
+	boost::to_lower(field);
+
+
+	return spell->spell->GetSpellData(state, field);
+}
+
+
+int EQ2Emu_lua_SetSpellData(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	LuaSpell* spell = lua_interface->GetSpell(state);
+	string field = lua_interface->GetStringValue(state, 2);
+	int8 fieldArg = 3; // field value after the initial set
+
+	if (!spell) {
+		lua_interface->LogError("%s: Spell not given in SetSpellData!", lua_interface->GetScriptName(state));
+		return 0;
+	}
+	if (!spell->spell || !spell->spell->GetSpellData()) {
+		lua_interface->LogError("%s: Inner Spell or SpellData not given in SetSpellData!", lua_interface->GetScriptName(state));
+		return 0;
+	}
+
+	boost::to_lower(field);
+
+	bool valSet = false;
+
+	spell->spell->SetSpellData(state, field, fieldArg);
+
+	return valSet;
+}
+
+
+int EQ2Emu_lua_CastCustomSpell(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	LuaSpell* spell = lua_interface->GetSpell(state);
+	Spawn* caster = lua_interface->GetSpawn(state, 2);
+	Spawn* target = lua_interface->GetSpawn(state, 3);
+
+	if (!target) {
+		lua_interface->LogError("%s: LUA CastCustomSpell command error: target is not a valid spawn", lua_interface->GetScriptName(state));
+		return 0;
+	}
+
+	if (!target->IsEntity()) {
+		lua_interface->LogError("%s: LUA CastCustomSpell command error: target (%s) is not an entity", lua_interface->GetScriptName(state), target->GetName());
+		return 0;
+	}
+
+	if (!spell) {
+		lua_interface->LogError("%s: LUA CastCustomSpell command error: spell is not valid", lua_interface->GetScriptName(state));
+		return 0;
+	}
+
+	if (caster && !caster->IsEntity()) {
+		lua_interface->LogError("%s: LUA CastSpell command error: caster (%s) is not an entity", lua_interface->GetScriptName(state), caster->GetName());
+		return 0;
+	}
+
+	target->GetZone()->ProcessSpell(NULL, (Entity*)caster, (Entity*)target, true, false, spell, 0);
+	return 0;
 }
 }

+ 8 - 0
EQ2/source/WorldServer/LuaFunctions.h

@@ -476,4 +476,12 @@ int EQ2Emu_lua_SendUpdateDefaultCommand(lua_State* state);
 int EQ2Emu_lua_SendTransporters(lua_State* state);
 int EQ2Emu_lua_SendTransporters(lua_State* state);
 int EQ2Emu_lua_SetTemporaryTransportID(lua_State* state);
 int EQ2Emu_lua_SetTemporaryTransportID(lua_State* state);
 int EQ2Emu_lua_GetTemporaryTransportID(lua_State* state);
 int EQ2Emu_lua_GetTemporaryTransportID(lua_State* state);
+
+int EQ2Emu_lua_GetAlignment(lua_State* state);
+int EQ2Emu_lua_SetAlignment(lua_State* state);
+
+int EQ2Emu_lua_GetSpell(lua_State* state);
+int EQ2Emu_lua_GetSpellData(lua_State* state);
+int EQ2Emu_lua_SetSpellData(lua_State* state);
+int EQ2Emu_lua_CastCustomSpell(lua_State* state);
 #endif
 #endif

+ 65 - 6
EQ2/source/WorldServer/LuaInterface.cpp

@@ -606,7 +606,7 @@ lua_State* LuaInterface::LoadLuaFile(const char* name) {
 	return 0;
 	return 0;
 }
 }
 
 
-void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool can_delete) {
+void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool can_delete, string reason) {
 	if(shutting_down)
 	if(shutting_down)
 		return;
 		return;
 
 
@@ -628,10 +628,15 @@ void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool
 		else
 		else
 			lua_pushlightuserdata(spell->state, 0);
 			lua_pushlightuserdata(spell->state, 0);
 
 
+		if (spell->caster && !spell->caster->Alive())
+			reason = "dead";
+
+		lua_pushstring(spell->state, (char*)reason.c_str());
+
 		MSpells.lock();
 		MSpells.lock();
 		current_spells[spell->state] = spell;
 		current_spells[spell->state] = spell;
 		MSpells.unlock();
 		MSpells.unlock();
-		lua_pcall(spell->state, 2, 0, 0);
+		lua_pcall(spell->state, 3, 0, 0);
 	}
 	}
 
 
 	spell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
 	spell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
@@ -644,6 +649,10 @@ void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool
 	}
 	}
 	spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
 	spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
 
 
+	// we need to make sure all memory is purged for a copied spell, its only used once
+	if (spell->spell->IsCopiedSpell())
+		can_delete = true;
+
 	if (can_delete) {
 	if (can_delete) {
 		AddPendingSpellDelete(spell);
 		AddPendingSpellDelete(spell);
 	}
 	}
@@ -1089,6 +1098,14 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 	lua_register(state, "SendTransporters", EQ2Emu_lua_SendTransporters);
 	lua_register(state, "SendTransporters", EQ2Emu_lua_SendTransporters);
 	lua_register(state, "SetTemporaryTransportID", EQ2Emu_lua_SetTemporaryTransportID);
 	lua_register(state, "SetTemporaryTransportID", EQ2Emu_lua_SetTemporaryTransportID);
 	lua_register(state, "GetTemporaryTransportID", EQ2Emu_lua_GetTemporaryTransportID);
 	lua_register(state, "GetTemporaryTransportID", EQ2Emu_lua_GetTemporaryTransportID);
+
+	lua_register(state, "SetAlignment", EQ2Emu_lua_SetAlignment);
+	lua_register(state, "GetAlignment", EQ2Emu_lua_GetAlignment);
+
+	lua_register(state, "GetSpell", EQ2Emu_lua_GetSpell);
+	lua_register(state, "GetSpellData", EQ2Emu_lua_GetSpellData);
+	lua_register(state, "SetSpellData", EQ2Emu_lua_SetSpellData);
+	lua_register(state, "CastCustomSpell", EQ2Emu_lua_CastCustomSpell);
 }
 }
 
 
 void LuaInterface::LogError(const char* error, ...)  {
 void LuaInterface::LogError(const char* error, ...)  {
@@ -1131,6 +1148,10 @@ void LuaInterface::DeletePendingSpells(bool all) {
 		for (del_itr = tmp_deletes.begin(); del_itr != tmp_deletes.end(); del_itr++) {
 		for (del_itr = tmp_deletes.begin(); del_itr != tmp_deletes.end(); del_itr++) {
 			spell = *del_itr;
 			spell = *del_itr;
 			spells_pending_delete.erase(spell);
 			spells_pending_delete.erase(spell);
+
+			if (spell->spell->IsCopiedSpell())
+				safe_delete(spell->spell);
+
 			safe_delete(spell);
 			safe_delete(spell);
 		}
 		}
 	}
 	}
@@ -1286,12 +1307,12 @@ Skill* LuaInterface::GetSkill(lua_State* state, int8 arg_num) {
 	Skill* ret = 0;
 	Skill* ret = 0;
 	if (lua_islightuserdata(state, arg_num)) {
 	if (lua_islightuserdata(state, arg_num)) {
 		LUAUserData* data = (LUAUserData*)lua_touserdata(state, arg_num);
 		LUAUserData* data = (LUAUserData*)lua_touserdata(state, arg_num);
-		if(!data || !data->IsCorrectlyInitialized()){
+		if (!data || !data->IsCorrectlyInitialized()) {
 			LogError("%s: GetSkill error while processing %s", GetScriptName(state), lua_tostring(state, 0));
 			LogError("%s: GetSkill error while processing %s", GetScriptName(state), lua_tostring(state, 0));
 		}
 		}
-		else if(!data->IsSkill()){
+		else if (!data->IsSkill()) {
 			lua_Debug ar;
 			lua_Debug ar;
- 			lua_getstack (state, 1, &ar);
+			lua_getstack(state, 1, &ar);
 			lua_getinfo(state, "Sln", &ar);
 			lua_getinfo(state, "Sln", &ar);
 			LogError("%s: Invalid data type used for GetSkill in %s (line %d)", GetScriptName(state), ar.source, ar.currentline);
 			LogError("%s: Invalid data type used for GetSkill in %s (line %d)", GetScriptName(state), ar.source, ar.currentline);
 		}
 		}
@@ -1301,6 +1322,25 @@ Skill* LuaInterface::GetSkill(lua_State* state, int8 arg_num) {
 	return ret;
 	return ret;
 }
 }
 
 
+LuaSpell* LuaInterface::GetSpell(lua_State* state, int8 arg_num) {
+	LuaSpell* ret = 0;
+	if (lua_islightuserdata(state, arg_num)) {
+		LUAUserData* data = (LUAUserData*)lua_touserdata(state, arg_num);
+		if (!data || !data->IsCorrectlyInitialized()) {
+			LogError("%s: GetSpell error while processing %s", GetScriptName(state), lua_tostring(state, 0));
+		}
+		else if (!data->IsSpell()) {
+			lua_Debug ar;
+			lua_getstack(state, 1, &ar);
+			lua_getinfo(state, "Sln", &ar);
+			LogError("%s: Invalid data type used for GetSpell in %s (line %d)", GetScriptName(state), ar.source, ar.currentline);
+		}
+		else
+			ret = data->spell;
+	}
+	return ret;
+}
+
 ZoneServer* LuaInterface::GetZone(lua_State* state, int8 arg_num) {
 ZoneServer* LuaInterface::GetZone(lua_State* state, int8 arg_num) {
 	ZoneServer* ret = 0;
 	ZoneServer* ret = 0;
 	if(lua_islightuserdata(state, arg_num)){
 	if(lua_islightuserdata(state, arg_num)){
@@ -1450,6 +1490,13 @@ void LuaInterface::SetZoneValue(lua_State* state, ZoneServer* zone) {
 	lua_pushlightuserdata(state, zone_wrapper);
 	lua_pushlightuserdata(state, zone_wrapper);
 }
 }
 
 
+void LuaInterface::SetSpellValue(lua_State* state, LuaSpell* spell) {
+	LUASpellWrapper* spell_wrapper = new LUASpellWrapper();
+	spell_wrapper->spell = spell;
+	AddUserDataPtr(spell_wrapper);
+	lua_pushlightuserdata(state, spell_wrapper);
+}
+
 LuaSpell* LuaInterface::GetSpell(const char* name)  {
 LuaSpell* LuaInterface::GetSpell(const char* name)  {
 	string lua_script = string(name);
 	string lua_script = string(name);
 	if (lua_script.find(".lua") == string::npos)
 	if (lua_script.find(".lua") == string::npos)
@@ -1460,7 +1507,7 @@ LuaSpell* LuaInterface::GetSpell(const char* name)  {
 		LuaSpell* spell = spells[lua_script];
 		LuaSpell* spell = spells[lua_script];
 		LuaSpell* new_spell = new LuaSpell;
 		LuaSpell* new_spell = new LuaSpell;
 		new_spell->state = spell->state;
 		new_spell->state = spell->state;
-		new_spell->file_name = spell->file_name;
+		new_spell->file_name = string(spell->file_name);
 		new_spell->timer = spell->timer;
 		new_spell->timer = spell->timer;
 		new_spell->timer.Disable();
 		new_spell->timer.Disable();
 		new_spell->resisted = false;
 		new_spell->resisted = false;
@@ -1854,6 +1901,10 @@ bool LUAUserData::IsSkill() {
 	return false;
 	return false;
 }
 }
 
 
+bool LUAUserData::IsSpell() {
+	return false;
+}
+
 LUAConversationOptionWrapper::LUAConversationOptionWrapper(){
 LUAConversationOptionWrapper::LUAConversationOptionWrapper(){
 	correctly_initialized = true;
 	correctly_initialized = true;
 }
 }
@@ -1916,4 +1967,12 @@ LUASkillWrapper::LUASkillWrapper() {
 
 
 bool LUASkillWrapper::IsSkill() {
 bool LUASkillWrapper::IsSkill() {
 	return true;
 	return true;
+}
+
+LUASpellWrapper::LUASpellWrapper() {
+	correctly_initialized = true;
+}
+
+bool LUASpellWrapper::IsSpell() {
+	return true;
 }
 }

+ 11 - 1
EQ2/source/WorldServer/LuaInterface.h

@@ -107,6 +107,7 @@ public:
 	virtual bool IsZone();
 	virtual bool IsZone();
 	virtual bool IsItem();
 	virtual bool IsItem();
 	virtual bool IsSkill();
 	virtual bool IsSkill();
+	virtual bool IsSpell();
 	bool correctly_initialized;
 	bool correctly_initialized;
 	Item* item;
 	Item* item;
 	ZoneServer* zone;
 	ZoneServer* zone;
@@ -116,6 +117,7 @@ public:
 	vector<Spawn*>* spawn_list;
 	vector<Spawn*>* spawn_list;
 	Quest* quest;
 	Quest* quest;
 	Skill* skill;
 	Skill* skill;
+	LuaSpell* spell;
 };
 };
 
 
 class LUAConversationOptionWrapper : public LUAUserData{
 class LUAConversationOptionWrapper : public LUAUserData{
@@ -167,6 +169,12 @@ public:
 	bool IsSkill();
 	bool IsSkill();
 };
 };
 
 
+class LUASpellWrapper : public LUAUserData {
+public:
+	LUASpellWrapper();
+	bool IsSpell();
+};
+
 class LuaInterface {
 class LuaInterface {
 public:
 public:
 	LuaInterface();
 	LuaInterface();
@@ -179,12 +187,13 @@ public:
 	bool			LoadSpawnScript(const char* name);
 	bool			LoadSpawnScript(const char* name);
 	bool			LoadZoneScript(string name);
 	bool			LoadZoneScript(string name);
 	bool			LoadZoneScript(const char* name);
 	bool			LoadZoneScript(const char* name);
-	void			RemoveSpell(LuaSpell* spell, bool call_remove_function = true, bool can_delete = true);
+	void			RemoveSpell(LuaSpell* spell, bool call_remove_function = true, bool can_delete = true, string reason = "");
 	Spawn*			GetSpawn(lua_State* state, int8 arg_num = 1);
 	Spawn*			GetSpawn(lua_State* state, int8 arg_num = 1);
 	Item*			GetItem(lua_State* state, int8 arg_num = 1);
 	Item*			GetItem(lua_State* state, int8 arg_num = 1);
 	Quest*			GetQuest(lua_State* state, int8 arg_num = 1);
 	Quest*			GetQuest(lua_State* state, int8 arg_num = 1);
 	ZoneServer*		GetZone(lua_State* state, int8 arg_num = 1);
 	ZoneServer*		GetZone(lua_State* state, int8 arg_num = 1);
 	Skill*			GetSkill(lua_State* state, int8 arg_num = 1);
 	Skill*			GetSkill(lua_State* state, int8 arg_num = 1);
+	LuaSpell*		GetSpell(lua_State* state, int8 arg_num = 1);
 	vector<ConversationOption>*	GetConversation(lua_State* state, int8 arg_num = 1);
 	vector<ConversationOption>*	GetConversation(lua_State* state, int8 arg_num = 1);
 	vector<Spawn*>* GetSpawnList(lua_State* state, int8 arg_num = 1);
 	vector<Spawn*>* GetSpawnList(lua_State* state, int8 arg_num = 1);
 	vector<OptionWindowOption>* GetOptionWindow(lua_State* state, int8 arg_num = 1);
 	vector<OptionWindowOption>* GetOptionWindow(lua_State* state, int8 arg_num = 1);
@@ -209,6 +218,7 @@ public:
 	void			SetQuestValue(lua_State* state, Quest* quest);
 	void			SetQuestValue(lua_State* state, Quest* quest);
 	void			SetZoneValue(lua_State* state, ZoneServer* zone);
 	void			SetZoneValue(lua_State* state, ZoneServer* zone);
 	void			SetSpawnListValue(lua_State* state, vector<Spawn*>* spawnList);
 	void			SetSpawnListValue(lua_State* state, vector<Spawn*>* spawnList);
+	void			SetSpellValue(lua_State* state, LuaSpell* spell);
 	void			SetConversationValue(lua_State* state, vector<ConversationOption>* conversation);
 	void			SetConversationValue(lua_State* state, vector<ConversationOption>* conversation);
 	void			SetOptionWindowValue(lua_State* state, vector<OptionWindowOption>* optionWindow);
 	void			SetOptionWindowValue(lua_State* state, vector<OptionWindowOption>* optionWindow);
 
 

+ 6 - 1
EQ2/source/WorldServer/Spawn.cpp

@@ -2865,6 +2865,7 @@ void Spawn::ResetMovement(){
 	for(itr = movement_loop.begin(); itr != movement_loop.end(); itr++){
 	for(itr = movement_loop.begin(); itr != movement_loop.end(); itr++){
 		safe_delete(*itr);
 		safe_delete(*itr);
 	}
 	}
+	movement_loop.clear();
 	MMovementLoop.unlock();
 	MMovementLoop.unlock();
 	resume_movement = true;
 	resume_movement = true;
 	movement_index = 0;
 	movement_index = 0;
@@ -2941,7 +2942,11 @@ void Spawn::AddRunningLocation(float x, float y, float z, float speed, float dis
 	if(speed == 0)
 	if(speed == 0)
 		return;
 		return;
 
 
-	((Entity*)this)->SetSpeed(speed);
+	if ( IsEntity() )
+		((Entity*)this)->SetSpeed(speed);
+	else
+		this->SetSpeed(speed);
+
 	MovementLocation* current_location = 0;
 	MovementLocation* current_location = 0;
 
 
 	float distance = GetDistance(x, y, z, distance_away != 0);
 	float distance = GetDistance(x, y, z, distance_away != 0);

+ 64 - 44
EQ2/source/WorldServer/SpellProcess.cpp

@@ -132,7 +132,7 @@ void SpellProcess::Process(){
 				if (spell->spell->GetSpellData()->call_frequency > 0 && !ProcessSpell(spell, false))
 				if (spell->spell->GetSpellData()->call_frequency > 0 && !ProcessSpell(spell, false))
 					active_spells.Remove(spell, true, 2000);
 					active_spells.Remove(spell, true, 2000);
 				else if ((spell->timer.GetDuration() * spell->num_calls) >= spell->spell->GetSpellData()->duration1 * 100)
 				else if ((spell->timer.GetDuration() * spell->num_calls) >= spell->spell->GetSpellData()->duration1 * 100)
-					DeleteCasterSpell(spell);
+					DeleteCasterSpell(spell, "expired");
 			}
 			}
 			else
 			else
 				CheckRemoveTargetFromSpell(spell);
 				CheckRemoveTargetFromSpell(spell);
@@ -151,7 +151,7 @@ void SpellProcess::Process(){
 
 
 		itr = tmpList.begin();
 		itr = tmpList.begin();
 		while (itr != tmpList.end()) {
 		while (itr != tmpList.end()) {
-			DeleteCasterSpell(*itr);
+			DeleteCasterSpell(*itr, "canceled");
 			itr++;
 			itr++;
 		}
 		}
 	}
 	}
@@ -342,7 +342,7 @@ void SpellProcess::CheckInterrupt(InterruptStruct* interrupt){
 		entity->GetZone()->SendSpellFailedPacket(client, interrupt->error_code);
 		entity->GetZone()->SendSpellFailedPacket(client, interrupt->error_code);
 }
 }
 
 
-bool SpellProcess::DeleteCasterSpell(Spawn* caster, Spell* spell){
+bool SpellProcess::DeleteCasterSpell(Spawn* caster, Spell* spell, string reason){
 
 
 	bool ret = false;
 	bool ret = false;
 	// need to use size(true) to get pending updates to the list as well
 	// need to use size(true) to get pending updates to the list as well
@@ -352,7 +352,7 @@ bool SpellProcess::DeleteCasterSpell(Spawn* caster, Spell* spell){
 		while (itr.Next()){
 		while (itr.Next()){
 			lua_spell = itr->value;
 			lua_spell = itr->value;
 			if (lua_spell->spell == spell && lua_spell->caster == caster) {
 			if (lua_spell->spell == spell && lua_spell->caster == caster) {
-				ret = DeleteCasterSpell(lua_spell);
+				ret = DeleteCasterSpell(lua_spell, reason);
 				break;
 				break;
 			}
 			}
 		}
 		}
@@ -360,7 +360,7 @@ bool SpellProcess::DeleteCasterSpell(Spawn* caster, Spell* spell){
 	return ret;
 	return ret;
 }
 }
 
 
-bool SpellProcess::DeleteCasterSpell(LuaSpell* spell){
+bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason){
 	bool ret = false;
 	bool ret = false;
 	Spawn* target = 0;
 	Spawn* target = 0;
 	if(spell) {
 	if(spell) {
@@ -403,7 +403,7 @@ bool SpellProcess::DeleteCasterSpell(LuaSpell* spell){
 			ret = true;
 			ret = true;
 		}
 		}
 		if(lua_interface)
 		if(lua_interface)
-			lua_interface->RemoveSpell(spell, true, SpellScriptTimersHasSpell(spell));	
+			lua_interface->RemoveSpell(spell, true, SpellScriptTimersHasSpell(spell), reason);
 	}
 	}
 	return ret;
 	return ret;
 }
 }
@@ -783,20 +783,25 @@ Spawn* SpellProcess::GetSpellTarget(Entity* caster){
 	return target;
 	return target;
 }
 }
 
 
-void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, Spawn* target, bool lock, bool harvest_spell)
+void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, Spawn* target, bool lock, bool harvest_spell, LuaSpell* customSpell, int16 custom_cast_time)
 {
 {
-	if(spell && caster)
+	if((customSpell != 0 || spell != 0) && caster)
 	{
 	{
 
 
 		Client* client = 0;
 		Client* client = 0;
-		int8 target_type = spell->GetSpellData()->target_type;
 		//int16 version = 0;
 		//int16 version = 0;
 
 
 		LuaSpell* lua_spell = 0;
 		LuaSpell* lua_spell = 0;
 
 
-		if(lua_interface)
+		if (customSpell)
+		{
+			lua_spell = customSpell;
+			spell = lua_spell->spell;
+		}
+		else if(lua_interface)
 			lua_spell = lua_interface->GetSpell(spell->GetSpellData()->lua_script.c_str());
 			lua_spell = lua_interface->GetSpell(spell->GetSpellData()->lua_script.c_str());
 
 
+		// this will only hit if customSpell is null and we go through the lua_interface
 		if(!lua_spell)
 		if(!lua_spell)
 		{
 		{
 			string lua_script = spell->GetSpellData()->lua_script;
 			string lua_script = spell->GetSpellData()->lua_script;
@@ -814,6 +819,8 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		if (!target)
 		if (!target)
 			target = caster;
 			target = caster;
 
 
+		int8 target_type = spell->GetSpellData()->target_type;
+
 		lua_spell->caster = caster;
 		lua_spell->caster = caster;
 		lua_spell->spell = spell;
 		lua_spell->spell = spell;
 		int32 target_id = target->GetID();
 		int32 target_id = target->GetID();
@@ -848,7 +855,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		//If this spell is the toggle cast type and is being toggled off, do this now
 		//If this spell is the toggle cast type and is being toggled off, do this now
 		if (spell->GetSpellData()->cast_type == SPELL_CAST_TYPE_TOGGLE)
 		if (spell->GetSpellData()->cast_type == SPELL_CAST_TYPE_TOGGLE)
 		{
 		{
-			bool ret_val = DeleteCasterSpell(caster, spell);
+			bool ret_val = DeleteCasterSpell(caster, spell, "purged");
 
 
 			if (ret_val)
 			if (ret_val)
 			{
 			{
@@ -862,7 +869,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				}
 				}
 
 
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-				safe_delete(lua_spell);
+				DeleteSpell(lua_spell);
 				return;
 				return;
 			}
 			}
 		}
 		}
@@ -889,7 +896,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 					// make sure to release the lock before we return out
 					// make sure to release the lock before we return out
 					zone->GetTradeskillMgr()->ReleaseReadLock(__FUNCTION__, __LINE__);
 					zone->GetTradeskillMgr()->ReleaseReadLock(__FUNCTION__, __LINE__);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-					safe_delete(lua_spell);
+					DeleteSpell(lua_spell);
 					return;
 					return;
 				}
 				}
 				// need to make sure the lock is released if the if passed
 				// need to make sure the lock is released if the if passed
@@ -900,7 +907,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot cast a tradeskill spell (%s) while not crafting.", caster->GetName(), spell->GetName());
 				LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot cast a tradeskill spell (%s) while not crafting.", caster->GetName(), spell->GetName());
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_ONLY_WHEN_CRAFTING);
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_ONLY_WHEN_CRAFTING);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-				safe_delete(lua_spell);
+				DeleteSpell(lua_spell);
 				return;
 				return;
 			}
 			}
 		}
 		}
@@ -910,7 +917,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 			LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot see target %s.", caster->GetName(), target->GetName());
 			LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot see target %s.", caster->GetName(), target->GetName());
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_CANT_SEE_TARGET);
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_CANT_SEE_TARGET);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-			safe_delete(lua_spell);
+			DeleteSpell(lua_spell);
 			return;
 			return;
 		}
 		}
 
 
@@ -919,7 +926,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 			LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot cast (mezzed or stunned).", caster->GetName());
 			LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot cast (mezzed or stunned).", caster->GetName());
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_CANNOT_CAST_STUNNED);
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_CANNOT_CAST_STUNNED);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-			safe_delete(lua_spell);
+			DeleteSpell(lua_spell);
 			return;
 			return;
 		}
 		}
 
 
@@ -928,7 +935,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 			LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot cast (stifled).", caster->GetName());
 			LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot cast (stifled).", caster->GetName());
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_CANNOT_CAST_STIFFLED);
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_CANNOT_CAST_STIFFLED);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-			safe_delete(lua_spell);
+			DeleteSpell(lua_spell);
 			return;
 			return;
 		}
 		}
 
 
@@ -937,7 +944,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 			LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot cast (feared).", caster->GetName());
 			LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot cast (feared).", caster->GetName());
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_CANNOT_CAST_FEARED);
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_CANNOT_CAST_FEARED);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-			safe_delete(lua_spell);
+			DeleteSpell(lua_spell);
 			return;
 			return;
 		}
 		}
 
 
@@ -946,7 +953,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 			LogWrite(SPELL__DEBUG, 1, "Spell", "Queuing spell for %s.", caster->GetName());
 			LogWrite(SPELL__DEBUG, 1, "Spell", "Queuing spell for %s.", caster->GetName());
 			CheckSpellQueue(spell, caster);
 			CheckSpellQueue(spell, caster);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-			safe_delete(lua_spell);
+			DeleteSpell(lua_spell);
 			return;
 			return;
 		}
 		}
 
 
@@ -959,7 +966,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				LogWrite(SPELL__DEBUG, 1, "Spell", "%s: No target.", spell->GetName());
 				LogWrite(SPELL__DEBUG, 1, "Spell", "%s: No target.", spell->GetName());
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-				safe_delete(lua_spell);
+				DeleteSpell(lua_spell);
 				return;
 				return;
 			}
 			}
 
 
@@ -996,7 +1003,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Too far.", spell->GetName());
 				LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Too far.", spell->GetName());
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_TOO_FAR_AWAY);
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_TOO_FAR_AWAY);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-				safe_delete(lua_spell);
+				DeleteSpell(lua_spell);
 				return;
 				return;
 			}
 			}
 
 
@@ -1005,7 +1012,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Too close.", spell->GetName());
 				LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Too close.", spell->GetName());
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_TOO_CLOSE);
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_TOO_CLOSE);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-				safe_delete(lua_spell);
+				DeleteSpell(lua_spell);
 				return;
 				return;
 			}
 			}
 		}
 		}
@@ -1021,7 +1028,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: No target or not groundspawn.", spell->GetName());
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: No target or not groundspawn.", spell->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-					safe_delete(lua_spell);
+					DeleteSpell(lua_spell);
 					return;
 					return;
 				}
 				}
 			}
 			}
@@ -1046,7 +1053,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: No target.", spell->GetName());
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: No target.", spell->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-					safe_delete(lua_spell);
+					DeleteSpell(lua_spell);
 					return;
 					return;
 				}
 				}
 				/*if (target->appearance.attackable) {
 				/*if (target->appearance.attackable) {
@@ -1064,7 +1071,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 					{
 					{
 						zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_A_FRIEND);
 						zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_A_FRIEND);
 						lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 						lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-						safe_delete(lua_spell);
+						DeleteSpell(lua_spell);
 						return;
 						return;
 					}
 					}
 				}
 				}
@@ -1075,7 +1082,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				{
 				{
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_A_FRIEND);
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_A_FRIEND);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-					safe_delete(lua_spell);
+					DeleteSpell(lua_spell);
 					return;
 					return;
 				}
 				}
 			}
 			}
@@ -1092,7 +1099,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: No target.", spell->GetName());
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: No target.", spell->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-					safe_delete(lua_spell);
+					DeleteSpell(lua_spell);
 					return;
 					return;
 				}
 				}
 
 
@@ -1101,7 +1108,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Not an Enemy (Target: %s).", spell->GetName(), target->GetName());
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Not an Enemy (Target: %s).", spell->GetName(), target->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-					safe_delete(lua_spell);
+					DeleteSpell(lua_spell);
 					return;
 					return;
 				}
 				}
 
 
@@ -1110,7 +1117,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Target is not alive (Target: %s).", spell->GetName(), target->GetName());
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Target is not alive (Target: %s).", spell->GetName(), target->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ALIVE);
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ALIVE);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-					safe_delete(lua_spell);
+					DeleteSpell(lua_spell);
 					return;
 					return;
 				}
 				}
 
 
@@ -1119,7 +1126,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Target is invulnerable (Target: %s).", spell->GetName(), target->GetName());
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Target is invulnerable (Target: %s).", spell->GetName(), target->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_TARGET_INVULNERABLE);
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_TARGET_INVULNERABLE);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-					safe_delete(lua_spell);
+					DeleteSpell(lua_spell);
 					return;
 					return;
 				}
 				}
 
 
@@ -1131,7 +1138,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 						LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Target (%s) is player and not attackable.", spell->GetName(), target->GetName());
 						LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Target (%s) is player and not attackable.", spell->GetName(), target->GetName());
 						zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
 						zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
 						lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 						lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-						safe_delete(lua_spell);
+						DeleteSpell(lua_spell);
 						return;
 						return;
 					}
 					}
 				}
 				}
@@ -1140,7 +1147,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Target (%s) is casters pet and not attackable by caster.", spell->GetName(), target->GetName());
 					LogWrite(SPELL__DEBUG, 1, "Spell", "%s: Target (%s) is casters pet and not attackable by caster.", spell->GetName(), target->GetName());
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
 					zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-					safe_delete(lua_spell);
+					DeleteSpell(lua_spell);
 					return;
 					return;
 				}
 				}
 			}
 			}
@@ -1150,7 +1157,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		{
 		{
 			LogWrite(SPELL__ERROR, 0, "Spell", "SpellProcess::ProcessSpell Unable to find any spell targets for spell '%s'.", spell->GetName());
 			LogWrite(SPELL__ERROR, 0, "Spell", "SpellProcess::ProcessSpell Unable to find any spell targets for spell '%s'.", spell->GetName());
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-			safe_delete(lua_spell);
+			DeleteSpell(lua_spell);
 			return;
 			return;
 		}
 		}
 
 
@@ -1160,13 +1167,13 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 			{
 			{
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_DEAD);
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_DEAD);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-				safe_delete(lua_spell);
+				DeleteSpell(lua_spell);
 				return;
 				return;
 			}
 			}
 			if(target->IsPlayer() && zone->GetClientBySpawn(target)->GetCurrentRez()->active){
 			if(target->IsPlayer() && zone->GetClientBySpawn(target)->GetCurrentRez()->active){
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_ALREADY_CAST);
 				zone->SendSpellFailedPacket(client, SPELL_ERROR_ALREADY_CAST);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-				safe_delete(lua_spell);
+				DeleteSpell(lua_spell);
 				return;
 				return;
 			}
 			}
 		}
 		}
@@ -1175,7 +1182,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		{
 		{
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_POWER);
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_POWER);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-			safe_delete(lua_spell);
+			DeleteSpell(lua_spell);
 			return;
 			return;
 		}
 		}
 
 
@@ -1183,7 +1190,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		{ 
 		{ 
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_HEALTH);
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_HEALTH);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-			safe_delete(lua_spell);
+			DeleteSpell(lua_spell);
 			return; 
 			return; 
 		}
 		}
 
 
@@ -1191,7 +1198,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		{
 		{
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_SAVAGERY);
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_SAVAGERY);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-			safe_delete(lua_spell);
+			DeleteSpell(lua_spell);
 			return;
 			return;
 		}
 		}
 
 
@@ -1199,7 +1206,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		{
 		{
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_DISSONANCE);
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_DISSONANCE);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-			safe_delete(lua_spell);
+			DeleteSpell(lua_spell);
 			return;
 			return;
 		}
 		}
 
 
@@ -1207,7 +1214,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		{
 		{
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_CONC);
 			zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_CONC);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 			lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-			safe_delete(lua_spell);
+			DeleteSpell(lua_spell);
 			return;
 			return;
 		}
 		}
 
 
@@ -1223,7 +1230,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 				if (!result) {
 				if (!result) {
 					zone->SendSpellFailedPacket(client, error);
 					zone->SendSpellFailedPacket(client, error);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 					lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-					safe_delete(lua_spell);
+					DeleteSpell(lua_spell);
 					return;
 					return;
 				}
 				}
 			}
 			}
@@ -1235,6 +1242,9 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		else
 		else
 			LogWrite(SPELL__DEBUG, 1, "Spell", "Unable to do precast check as there was no lua_interface");
 			LogWrite(SPELL__DEBUG, 1, "Spell", "Unable to do precast check as there was no lua_interface");
 		
 		
+		if (custom_cast_time > 0)
+			spell->GetSpellData()->cast_time = custom_cast_time;
+
 		//Apply casting speed mod
 		//Apply casting speed mod
 		spell->ModifyCastTime(caster);
 		spell->ModifyCastTime(caster);
 
 
@@ -1265,7 +1275,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 			if(!CastProcessedSpell(lua_spell))
 			if(!CastProcessedSpell(lua_spell))
 			{
 			{
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
 				lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
-				safe_delete(lua_spell);
+				DeleteSpell(lua_spell);
 				return;
 				return;
 			}
 			}
 		}
 		}
@@ -1643,7 +1653,7 @@ void SpellProcess::RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, boo
 			if (spell->spell->GetSpellData()->persist_though_death)
 			if (spell->spell->GetSpellData()->persist_though_death)
 				continue;
 				continue;
 			if(spell->caster == spawn){
 			if(spell->caster == spawn){
-				DeleteCasterSpell(spell);
+				DeleteCasterSpell(spell, "expired");
 				continue;
 				continue;
 			}
 			}
 
 
@@ -2324,7 +2334,7 @@ void SpellProcess::CheckRemoveTargetFromSpell(LuaSpell* spell, bool allow_delete
 		safe_delete(remove_targets);
 		safe_delete(remove_targets);
 		MRemoveTargetList.releasewritelock(__FUNCTION__, __LINE__);
 		MRemoveTargetList.releasewritelock(__FUNCTION__, __LINE__);
 		if (should_delete)
 		if (should_delete)
-			DeleteCasterSpell(spell);
+			DeleteCasterSpell(spell, "purged");
 	}
 	}
 }
 }
 
 
@@ -2429,4 +2439,14 @@ void SpellProcess::AddSpellCancel(LuaSpell* spell){
 	MSpellCancelList.writelock(__FUNCTION__, __LINE__);
 	MSpellCancelList.writelock(__FUNCTION__, __LINE__);
 	SpellCancelList.push_back(spell);
 	SpellCancelList.push_back(spell);
 	MSpellCancelList.releasewritelock(__FUNCTION__, __LINE__);
 	MSpellCancelList.releasewritelock(__FUNCTION__, __LINE__);
+}
+
+void SpellProcess::DeleteSpell(LuaSpell* spell)
+{
+	RemoveSpellFromQueue(spell->spell, spell->caster);
+
+	if (spell->spell->IsCopiedSpell())
+		safe_delete(spell->spell);
+
+	safe_delete(spell);
 }
 }

+ 4 - 3
EQ2/source/WorldServer/SpellProcess.h

@@ -171,7 +171,7 @@ public:
 	/// <param name='target'>The target(Spawn) of the spell</param>
 	/// <param name='target'>The target(Spawn) of the spell</param>
 	/// <param name='lock'>??? not currently used</param>
 	/// <param name='lock'>??? not currently used</param>
 	/// <param name='harvest_spell'>Is this a harvest spell?</param>
 	/// <param name='harvest_spell'>Is this a harvest spell?</param>
-	void ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, Spawn* target = 0, bool lock = true, bool harvest_spell = false);
+	void ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, Spawn* target = 0, bool lock = true, bool harvest_spell = false, LuaSpell* customSpell = 0, int16 custom_cast_time = 0);
 
 
 	/// <summary>Cast an EntityCommand (right click menu)</summary>
 	/// <summary>Cast an EntityCommand (right click menu)</summary>
 	/// <param name='zone'>The current ZoneServer</param>
 	/// <param name='zone'>The current ZoneServer</param>
@@ -254,11 +254,11 @@ public:
 	/// <summary>Remove the given spell for the given caster from the SpellProcess</summary>
 	/// <summary>Remove the given spell for the given caster from the SpellProcess</summary>
 	/// <param name='caster'>The spawn to remove the spell for</param>
 	/// <param name='caster'>The spawn to remove the spell for</param>
 	/// <param name='spell'>The spell to remove</param>
 	/// <param name='spell'>The spell to remove</param>
-	bool DeleteCasterSpell(Spawn* caster, Spell* spell);
+	bool DeleteCasterSpell(Spawn* caster, Spell* spell, string reason = "");
 
 
 	/// <summary>Remove the given spell from the ZpellProcess</summary>
 	/// <summary>Remove the given spell from the ZpellProcess</summary>
 	/// <param name='spell'>LuaSpell to remove</param>
 	/// <param name='spell'>LuaSpell to remove</param>
-	bool DeleteCasterSpell(LuaSpell* spell);
+	bool DeleteCasterSpell(LuaSpell* spell, string reason="");
 
 
 	/// <summary>Interrupt the spell</summary>
 	/// <summary>Interrupt the spell</summary>
 	/// <param name='interrupt'>InterruptStruct that contains all the info</param>
 	/// <param name='interrupt'>InterruptStruct that contains all the info</param>
@@ -379,6 +379,7 @@ public:
 
 
 	void AddSpellCancel(LuaSpell* spell);
 	void AddSpellCancel(LuaSpell* spell);
 
 
+	void DeleteSpell(LuaSpell* spell);
 private:
 private:
 	/// <summary>Sends the spell data to the lua script</summary>
 	/// <summary>Sends the spell data to the lua script</summary>
 	/// <param name='spell'>LuaSpell to call the lua script for</param>
 	/// <param name='spell'>LuaSpell to call the lua script for</param>

+ 858 - 1
EQ2/source/WorldServer/Spells.cpp

@@ -24,12 +24,14 @@
 #include "Traits/Traits.h"
 #include "Traits/Traits.h"
 #include "AltAdvancement/AltAdvancement.h"
 #include "AltAdvancement/AltAdvancement.h"
 #include <cmath>
 #include <cmath>
+#include "LuaInterface.h"
 
 
 extern ConfigReader configReader;
 extern ConfigReader configReader;
 extern WorldDatabase database;
 extern WorldDatabase database;
 extern MasterTraitList master_trait_list;
 extern MasterTraitList master_trait_list;
 extern MasterAAList master_aa_list;
 extern MasterAAList master_aa_list;
 extern MasterSpellList master_spell_list;
 extern MasterSpellList master_spell_list;
+extern LuaInterface* lua_interface;
 
 
 Spell::Spell(){
 Spell::Spell(){
 	spell = new SpellData;
 	spell = new SpellData;
@@ -38,6 +40,127 @@ Spell::Spell(){
 	damage_spell = false;
 	damage_spell = false;
 	control_spell = false;
 	control_spell = false;
 	offense_spell = false;
 	offense_spell = false;
+	copied_spell = false;
+	MSpellInfo.SetName("Spell::MSpellInfo");
+}
+
+Spell::Spell(Spell* host_spell)
+{
+	copied_spell = true;
+
+	spell = new SpellData;
+
+	if (host_spell->GetSpellData())
+	{
+		spell->affect_only_group_members = host_spell->GetSpellData()->affect_only_group_members;
+		spell->call_frequency = host_spell->GetSpellData()->call_frequency;
+		spell->can_effect_raid = host_spell->GetSpellData()->can_effect_raid;
+		spell->casting_flags = host_spell->GetSpellData()->casting_flags;
+		spell->cast_time = host_spell->GetSpellData()->cast_time;
+		spell->cast_type = host_spell->GetSpellData()->cast_type;
+		spell->cast_while_moving = host_spell->GetSpellData()->cast_while_moving;
+		spell->class_skill = host_spell->GetSpellData()->class_skill;
+		spell->control_effect_type = host_spell->GetSpellData()->control_effect_type;
+		spell->description = EQ2_16BitString(host_spell->GetSpellData()->description);
+		spell->det_type = host_spell->GetSpellData()->det_type;
+		spell->display_spell_tier = host_spell->GetSpellData()->display_spell_tier;
+
+		spell->dissonance_req = host_spell->GetSpellData()->dissonance_req;
+		spell->dissonance_req_percent = host_spell->GetSpellData()->dissonance_req_percent;
+		spell->dissonance_upkeep = host_spell->GetSpellData()->dissonance_upkeep;
+		spell->duration1 = host_spell->GetSpellData()->duration1;
+		spell->duration2 = host_spell->GetSpellData()->duration2;
+		spell->duration_until_cancel = host_spell->GetSpellData()->duration_until_cancel;
+		spell->effect_message = string(host_spell->GetSpellData()->effect_message);
+		spell->fade_message = string(host_spell->GetSpellData()->fade_message);
+
+		spell->friendly_spell = host_spell->GetSpellData()->friendly_spell;
+		spell->group_spell = host_spell->GetSpellData()->group_spell;
+
+		spell->hit_bonus = host_spell->GetSpellData()->hit_bonus;
+
+		spell->hp_req = host_spell->GetSpellData()->hp_req;
+		spell->hp_req_percent = host_spell->GetSpellData()->hp_req_percent;
+		spell->hp_upkeep = host_spell->GetSpellData()->hp_upkeep;
+
+		spell->icon = host_spell->GetSpellData()->icon;
+		spell->icon_backdrop = host_spell->GetSpellData()->icon_backdrop;
+
+		spell->icon_heroic_op = host_spell->GetSpellData()->icon_heroic_op;
+
+		spell->id = host_spell->GetSpellData()->id;
+
+		spell->incurable = host_spell->GetSpellData()->incurable;
+		spell->interruptable = host_spell->GetSpellData()->interruptable;
+		spell->is_aa = host_spell->GetSpellData()->is_aa;
+
+		spell->is_active = host_spell->GetSpellData()->is_active;
+		spell->linked_timer = host_spell->GetSpellData()->linked_timer;
+		spell->lua_script = string(host_spell->GetSpellData()->lua_script);
+
+		spell->mastery_skill = host_spell->GetSpellData()->mastery_skill;
+		spell->max_aoe_targets = host_spell->GetSpellData()->max_aoe_targets;
+
+		spell->min_range = host_spell->GetSpellData()->min_range;
+		spell->name = EQ2_8BitString(host_spell->GetSpellData()->name);
+		spell->not_maintained = host_spell->GetSpellData()->not_maintained;
+		spell->num_levels = host_spell->GetSpellData()->num_levels;
+		spell->persist_though_death = host_spell->GetSpellData()->persist_though_death;
+		spell->power_by_level = host_spell->GetSpellData()->power_by_level;
+		spell->power_req = host_spell->GetSpellData()->power_req;
+		spell->power_req_percent = host_spell->GetSpellData()->power_req_percent;
+		spell->power_upkeep = host_spell->GetSpellData()->power_upkeep;
+		spell->radius = host_spell->GetSpellData()->radius;
+		spell->range = host_spell->GetSpellData()->range;
+		spell->recast = host_spell->GetSpellData()->recast;
+		spell->recovery = host_spell->GetSpellData()->recovery;
+		spell->req_concentration = host_spell->GetSpellData()->req_concentration;
+		spell->resistibility = host_spell->GetSpellData()->resistibility;
+		spell->savagery_req = host_spell->GetSpellData()->savagery_req;
+		spell->savagery_req_percent = host_spell->GetSpellData()->savagery_req_percent;
+		spell->savagery_upkeep = host_spell->GetSpellData()->savagery_upkeep;
+		spell->savage_bar = host_spell->GetSpellData()->savage_bar;
+		spell->savage_bar_slot = host_spell->GetSpellData()->savage_bar_slot;
+		spell->soe_spell_crc = host_spell->GetSpellData()->soe_spell_crc;
+		spell->spell_book_type = host_spell->GetSpellData()->spell_book_type;
+		spell->spell_name_crc = host_spell->GetSpellData()->spell_name_crc;
+		spell->spell_type = host_spell->GetSpellData()->spell_type;
+		spell->spell_visual = host_spell->GetSpellData()->spell_visual;
+		spell->success_message = string(host_spell->GetSpellData()->success_message);
+		spell->target_type = host_spell->GetSpellData()->target_type;
+		spell->tier = host_spell->GetSpellData()->tier;
+		spell->ts_loc_index = host_spell->GetSpellData()->ts_loc_index;
+		spell->type = host_spell->GetSpellData()->type;
+	}
+
+	heal_spell = host_spell->IsHealSpell();
+	buff_spell = host_spell->IsBuffSpell();
+	damage_spell = host_spell->IsDamageSpell();;
+	control_spell = host_spell->IsControlSpell();
+	offense_spell = host_spell->IsOffenseSpell();
+
+	host_spell->LockSpellInfo();
+	std::vector<LevelArray*>::iterator itr;
+	for (itr = host_spell->levels.begin(); itr != host_spell->levels.end(); itr++)
+	{
+		LevelArray* lvlArray = *itr;
+		AddSpellLevel(lvlArray->adventure_class, lvlArray->tradeskill_class, lvlArray->spell_level);
+	}
+
+	std::vector<SpellDisplayEffect*>::iterator sdeitr;
+	for (sdeitr = host_spell->effects.begin(); sdeitr != host_spell->effects.end(); sdeitr++)
+	{
+		SpellDisplayEffect* sde = *sdeitr;
+		AddSpellEffect(sde->percentage, sde->subbullet, sde->description);
+	}
+
+	vector<LUAData*>::iterator luaitr;
+	for (luaitr = host_spell->lua_data.begin(); luaitr != host_spell->lua_data.end(); luaitr++) {
+		LUAData* data = *luaitr;
+		AddSpellLuaData(data->type, data->int_value, data->int_value2, data->float_value, data->float_value2, data->bool_value, string(data->string_value), string(data->string_value2), string(data->string_helper));
+	}
+	host_spell->UnlockSpellInfo();
+
 	MSpellInfo.SetName("Spell::MSpellInfo");
 	MSpellInfo.SetName("Spell::MSpellInfo");
 }
 }
 
 
@@ -48,6 +171,7 @@ Spell::Spell(SpellData* in_spell){
 	damage_spell = false;
 	damage_spell = false;
 	control_spell = false;
 	control_spell = false;
 	offense_spell = false;
 	offense_spell = false;
+	copied_spell = false;
 	MSpellInfo.SetName("Spell::MSpellInfo");
 	MSpellInfo.SetName("Spell::MSpellInfo");
 }
 }
 
 
@@ -1158,6 +1282,735 @@ SpellData* Spell::GetSpellData(){
 	return spell;
 	return spell;
 }
 }
 
 
+bool Spell::GetSpellData(lua_State* state, std::string field)
+{
+	if (!lua_interface)
+		return false;
+
+	bool valSet = false;
+
+	if (field == "spell_book_type")
+	{
+		lua_interface->SetInt32Value(state, GetSpellData()->spell_book_type);
+		valSet = true;
+	}
+	else if (field == "icon")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->icon);
+		valSet = true;
+	}
+	else if (field == "icon_heroic_op")
+	{
+		lua_interface->SetInt32Value(state, GetSpellData()->icon_heroic_op);
+		valSet = true;
+	}
+	else if (field == "icon_backdrop")
+	{
+		lua_interface->SetInt32Value(state, GetSpellData()->icon_backdrop);
+		valSet = true;
+	}
+	else if (field == "type")
+	{
+		lua_interface->SetInt32Value(state, GetSpellData()->type);
+		valSet = true;
+	}
+	else if (field == "class_skill")
+	{
+		lua_interface->SetInt32Value(state, GetSpellData()->class_skill);
+		valSet = true;
+	}
+	else if (field == "mastery_skill")
+	{
+		lua_interface->SetInt32Value(state, GetSpellData()->mastery_skill);
+		valSet = true;
+	}
+	else if (field == "ts_loc_index")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->ts_loc_index);
+		valSet = true;
+	}
+	else if (field == "num_levels")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->num_levels);
+		valSet = true;
+	}
+	else if (field == "tier")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->tier);
+		valSet = true;
+	}
+	else if (field == "hp_req")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->hp_req);
+		valSet = true;
+	}
+	else if (field == "hp_upkeep")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->hp_upkeep);
+		valSet = true;
+	}
+	else if (field == "power_req")
+	{
+		lua_interface->SetFloatValue(state, GetSpellData()->power_req);
+		valSet = true;
+	}
+	else if (field == "power_by_level")
+	{
+		lua_interface->SetBooleanValue(state, GetSpellData()->power_by_level);
+		valSet = true;
+	}
+	else if (field == "power_upkeep")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->power_upkeep);
+		valSet = true;
+	}
+	else if (field == "savagery_req")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->savagery_req);
+		valSet = true;
+	}
+	else if (field == "savagery_upkeep")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->savagery_upkeep);
+		valSet = true;
+	}
+	else if (field == "dissonance_req")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->dissonance_req);
+		valSet = true;
+	}
+	else if (field == "dissonance_upkeep")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->dissonance_upkeep);
+		valSet = true;
+	}
+	else if (field == "target_type")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->target_type);
+		valSet = true;
+	}
+	else if (field == "cast_time")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->cast_time);
+		valSet = true;
+	}
+	else if (field == "recovery")
+	{
+		lua_interface->SetFloatValue(state, GetSpellData()->recovery);
+		valSet = true;
+	}
+	else if (field == "recast")
+	{
+		lua_interface->SetFloatValue(state, GetSpellData()->recast);
+		valSet = true;
+	}
+	else if (field == "linked_timer")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->linked_timer);
+		valSet = true;
+	}
+	else if (field == "radius")
+	{
+		lua_interface->SetFloatValue(state, GetSpellData()->radius);
+		valSet = true;
+	}
+	else if (field == "max_aoe_targets")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->max_aoe_targets);
+		valSet = true;
+	}
+	else if (field == "friendly_spell")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->friendly_spell);
+		valSet = true;
+	}
+	else if (field == "req_concentration")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->req_concentration);
+		valSet = true;
+	}
+	else if (field == "range")
+	{
+		lua_interface->SetFloatValue(state, GetSpellData()->range);
+		valSet = true;
+	}
+	else if (field == "duration1")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->duration1);
+		valSet = true;
+	}
+	else if (field == "duration2")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->duration2);
+		valSet = true;
+	}
+	else if (field == "resistibility")
+	{
+		lua_interface->SetFloatValue(state, GetSpellData()->resistibility);
+		valSet = true;
+	}
+	else if (field == "duration_until_cancel")
+	{
+		lua_interface->SetBooleanValue(state, GetSpellData()->duration_until_cancel);
+		valSet = true;
+	}
+	else if (field == "power_req_percent")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->power_req_percent);
+		valSet = true;
+	}
+	else if (field == "hp_req_percent")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->hp_req_percent);
+		valSet = true;
+	}
+	else if (field == "savagery_req_percent")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->savagery_req_percent);
+		valSet = true;
+	}
+	else if (field == "dissonance_req_percent")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->dissonance_req_percent);
+		valSet = true;
+	}
+	else if (field == "name")
+	{
+		lua_interface->SetStringValue(state, GetSpellData()->name.data.c_str());
+		valSet = true;
+	}
+	else if (field == "description")
+	{
+		lua_interface->SetStringValue(state, GetSpellData()->description.data.c_str());
+		valSet = true;
+	}
+	else if (field == "success_message")
+	{
+		lua_interface->SetStringValue(state, GetSpellData()->success_message.c_str());
+		valSet = true;
+	}
+	else if (field == "fade_message")
+	{
+		lua_interface->SetStringValue(state, GetSpellData()->fade_message.c_str());
+		valSet = true;
+	}
+	else if (field == "cast_type")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->cast_type);
+		valSet = true;
+	}
+	else if (field == "lua_script")
+	{
+		lua_interface->SetStringValue(state, GetSpellData()->lua_script.c_str());
+		valSet = true;
+	}
+	else if (field == "interruptable")
+	{
+		lua_interface->SetBooleanValue(state, GetSpellData()->interruptable);
+		valSet = true;
+	}
+	else if (field == "spell_visual")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->spell_visual);
+		valSet = true;
+	}
+	else if (field == "effect_message")
+	{
+		lua_interface->SetStringValue(state, GetSpellData()->effect_message.c_str());
+		valSet = true;
+	}
+	else if (field == "min_range")
+	{
+		lua_interface->SetFloatValue(state, GetSpellData()->min_range);
+		valSet = true;
+	}
+	else if (field == "can_effect_raid")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->can_effect_raid);
+		valSet = true;
+	}
+	else if (field == "affect_only_group_members")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->affect_only_group_members);
+		valSet = true;
+	}
+	else if (field == "group_spell")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->group_spell);
+		valSet = true;
+	}
+	else if (field == "hit_bonus")
+	{
+		lua_interface->SetFloatValue(state, GetSpellData()->hit_bonus);
+		valSet = true;
+	}
+	else if (field == "display_spell_tier")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->display_spell_tier);
+		valSet = true;
+	}
+	else if (field == "is_active")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->is_active);
+		valSet = true;
+	}
+	else if (field == "det_type")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->det_type);
+		valSet = true;
+	}
+	else if (field == "incurable")
+	{
+		lua_interface->SetBooleanValue(state, GetSpellData()->incurable);
+		valSet = true;
+	}
+	else if (field == "control_effect_type")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->control_effect_type);
+		valSet = true;
+	}
+	else if (field == "casting_flags")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->casting_flags);
+		valSet = true;
+	}
+	else if (field == "cast_while_moving")
+	{
+		lua_interface->SetBooleanValue(state, GetSpellData()->cast_while_moving);
+		valSet = true;
+	}
+	else if (field == "persist_though_death")
+	{
+		lua_interface->SetBooleanValue(state, GetSpellData()->persist_though_death);
+		valSet = true;
+	}
+	else if (field == "not_maintained")
+	{
+		lua_interface->SetBooleanValue(state, GetSpellData()->not_maintained);
+		valSet = true;
+	}
+	else if (field == "is_aa")
+	{
+		lua_interface->SetBooleanValue(state, GetSpellData()->is_aa);
+		valSet = true;
+	}
+	else if (field == "savage_bar")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->savage_bar);
+		valSet = true;
+	}
+	else if (field == "savage_bar_slot")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->savage_bar_slot);
+		valSet = true;
+	}
+	else if (field == "soe_spell_crc")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->soe_spell_crc);
+		valSet = true;
+	}
+	else if (field == "spell_type")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->spell_type);
+		valSet = true;
+	}
+	else if (field == "spell_name_crc")
+	{
+		lua_interface->SetSInt32Value(state, GetSpellData()->spell_name_crc);
+		valSet = true;
+	}
+
+	return valSet;
+}
+
+bool Spell::SetSpellData(lua_State* state, std::string field, int8 fieldArg)
+{
+	if (!lua_interface)
+		return false;
+
+	bool valSet = false;
+
+	if (field == "spell_book_type")
+	{
+		int32 spell_book_type = lua_interface->GetInt32Value(state, fieldArg);
+		GetSpellData()->spell_book_type = spell_book_type;
+		valSet = true;
+	}
+	else if (field == "icon")
+	{
+		sint16 icon = lua_interface->GetSInt32Value(state, fieldArg);
+		GetSpellData()->icon = icon;
+		valSet = true;
+	}
+	else if (field == "icon_heroic_op")
+	{
+		int16 icon_heroic_op = lua_interface->GetInt16Value(state, fieldArg);
+		GetSpellData()->icon_heroic_op = icon_heroic_op;
+		valSet = true;
+	}
+	else if (field == "icon_backdrop")
+	{
+		int16 icon_backdrop = lua_interface->GetInt16Value(state, fieldArg);
+		GetSpellData()->icon_backdrop = icon_backdrop;
+		valSet = true;
+	}
+	else if (field == "type")
+	{
+		int16 type = lua_interface->GetInt16Value(state, fieldArg);
+		GetSpellData()->type = type;
+		valSet = true;
+	}
+	else if (field == "class_skill")
+	{
+		int32 class_skill = lua_interface->GetInt32Value(state, fieldArg);
+		GetSpellData()->class_skill = class_skill;
+		valSet = true;
+	}
+	else if (field == "mastery_skill")
+	{
+		int32 mastery_skill = lua_interface->GetInt32Value(state, fieldArg);
+		GetSpellData()->mastery_skill = mastery_skill;
+		valSet = true;
+	}
+	else if (field == "ts_loc_index")
+	{
+		int8 ts_loc_index = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->ts_loc_index = ts_loc_index;
+		valSet = true;
+	}
+	else if (field == "num_levels")
+	{
+		int8 num_levels = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->num_levels = num_levels;
+		valSet = true;
+	}
+	else if (field == "tier")
+	{
+		int8 tier = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->tier = tier;
+		valSet = true;
+	}
+	else if (field == "hp_req")
+	{
+		int16 hp_req = lua_interface->GetInt16Value(state, fieldArg);
+		GetSpellData()->hp_req = hp_req;
+		valSet = true;
+	}
+	else if (field == "hp_upkeep")
+	{
+		int16 hp_upkeep = lua_interface->GetInt16Value(state, fieldArg);
+		GetSpellData()->hp_upkeep = hp_upkeep;
+		valSet = true;
+	}
+	else if (field == "power_req")
+	{
+		float power_req = lua_interface->GetFloatValue(state, fieldArg);
+		GetSpellData()->power_req = power_req;
+		valSet = true;
+	}
+	else if (field == "power_by_level")
+	{
+		bool power_by_level = lua_interface->GetBooleanValue(state, fieldArg);
+		GetSpellData()->power_by_level = power_by_level;
+		valSet = true;
+	}
+	else if (field == "power_upkeep")
+	{
+		int16 power_upkeep = lua_interface->GetInt16Value(state, fieldArg);
+		GetSpellData()->power_upkeep = power_upkeep;
+		valSet = true;
+	}
+	else if (field == "savagery_req")
+	{
+		int16 savagery_req = lua_interface->GetInt16Value(state, fieldArg);
+		GetSpellData()->savagery_req = savagery_req;
+		valSet = true;
+	}
+	else if (field == "savagery_upkeep")
+	{
+		int16 savagery_upkeep = lua_interface->GetInt16Value(state, fieldArg);
+		GetSpellData()->savagery_upkeep = savagery_upkeep;
+		valSet = true;
+	}
+	else if (field == "dissonance_req")
+	{
+		int16 dissonance_req = lua_interface->GetInt16Value(state, fieldArg);
+		GetSpellData()->dissonance_req = dissonance_req;
+		valSet = true;
+	}
+	else if (field == "dissonance_upkeep")
+	{
+		int16 dissonance_upkeep = lua_interface->GetInt16Value(state, fieldArg);
+		GetSpellData()->dissonance_upkeep = dissonance_upkeep;
+		valSet = true;
+	}
+	else if (field == "target_type")
+	{
+		int16 target_type = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->target_type = target_type;
+		valSet = true;
+	}
+	else if (field == "cast_time")
+	{
+		int16 cast_time = lua_interface->GetInt16Value(state, fieldArg);
+		GetSpellData()->cast_time = cast_time;
+		valSet = true;
+	}
+	else if (field == "recovery")
+	{
+		float recovery = lua_interface->GetFloatValue(state, fieldArg);
+		GetSpellData()->recovery = recovery;
+		valSet = true;
+	}
+	else if (field == "recast")
+	{
+		float recast = lua_interface->GetFloatValue(state, fieldArg);
+		GetSpellData()->recast = recast;
+		valSet = true;
+	}
+	else if (field == "linked_timer")
+	{
+		int32 linked_timer = lua_interface->GetInt32Value(state, fieldArg);
+		GetSpellData()->linked_timer = linked_timer;
+		valSet = true;
+	}
+	else if (field == "radius")
+	{
+		float radius = lua_interface->GetFloatValue(state, fieldArg);
+		GetSpellData()->radius = radius;
+		valSet = true;
+	}
+	else if (field == "max_aoe_targets")
+	{
+		int16 max_aoe_targets = lua_interface->GetInt16Value(state, fieldArg);
+		GetSpellData()->max_aoe_targets = max_aoe_targets;
+		valSet = true;
+	}
+	else if (field == "friendly_spell")
+	{
+		int8 friendly_spell = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->friendly_spell = friendly_spell;
+		valSet = true;
+	}
+	else if (field == "req_concentration")
+	{
+		int16 req_concentration = lua_interface->GetInt16Value(state, fieldArg);
+		GetSpellData()->req_concentration = req_concentration;
+		valSet = true;
+	}
+	else if (field == "range")
+	{
+		float range = lua_interface->GetFloatValue(state, fieldArg);
+		GetSpellData()->range = range;
+		valSet = true;
+	}
+	else if (field == "duration1")
+	{
+		sint32 duration = lua_interface->GetSInt32Value(state, fieldArg);
+		GetSpellData()->duration1 = duration;
+		valSet = true;
+	}
+	else if (field == "duration2")
+	{
+		sint32 duration = lua_interface->GetSInt32Value(state, fieldArg);
+		GetSpellData()->duration2 = duration;
+		valSet = true;
+	}
+	else if (field == "resistibility")
+	{
+		float resistibility = lua_interface->GetFloatValue(state, fieldArg);
+		GetSpellData()->resistibility = resistibility;
+		valSet = true;
+	}
+	else if (field == "duration_until_cancel")
+	{
+		bool duration_until_cancel = lua_interface->GetBooleanValue(state, fieldArg);
+		GetSpellData()->duration_until_cancel = duration_until_cancel;
+		valSet = true;
+	}
+	else if (field == "power_req_percent")
+	{
+		int8 power_req_percent = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->power_req_percent = power_req_percent;
+		valSet = true;
+	}
+	else if (field == "hp_req_percent")
+	{
+		int8 hp_req_percent = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->hp_req_percent = hp_req_percent;
+		valSet = true;
+	}
+	else if (field == "savagery_req_percent")
+	{
+		int8 savagery_req_percent = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->savagery_req_percent = savagery_req_percent;
+		valSet = true;
+	}
+	else if (field == "dissonance_req_percent")
+	{
+		int8 dissonance_req_percent = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->dissonance_req_percent = dissonance_req_percent;
+		valSet = true;
+	}
+	else if (field == "name")
+	{
+		string name = lua_interface->GetStringValue(state, fieldArg);
+		GetSpellData()->name.data = name;
+		valSet = true;
+	}
+	else if (field == "description")
+	{
+		string description = lua_interface->GetStringValue(state, fieldArg);
+		GetSpellData()->description.data = description;
+		valSet = true;
+	}
+	else if (field == "success_message")
+	{
+		string success_message = lua_interface->GetStringValue(state, fieldArg);
+		GetSpellData()->success_message = success_message;
+		valSet = true;
+	}
+	else if (field == "fade_message")
+	{
+		string fade_message = lua_interface->GetStringValue(state, fieldArg);
+		GetSpellData()->fade_message = fade_message;
+		valSet = true;
+	}
+	else if (field == "cast_type")
+	{
+		int8 cast_type = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->cast_type = cast_type;
+		valSet = true;
+	}
+	else if (field == "cast_type")
+	{
+		int32 call_frequency = lua_interface->GetInt32Value(state, fieldArg);
+		GetSpellData()->call_frequency = call_frequency;
+		valSet = true;
+	}
+	else if (field == "interruptable")
+	{
+		bool interruptable = lua_interface->GetBooleanValue(state, fieldArg);
+		GetSpellData()->interruptable = interruptable;
+		valSet = true;
+	}
+	else if (field == "spell_visual")
+	{
+		int32 spell_visual = lua_interface->GetInt32Value(state, fieldArg);
+		GetSpellData()->spell_visual = spell_visual;
+		valSet = true;
+	}
+	else if (field == "effect_message")
+	{
+		string effect_message = lua_interface->GetStringValue(state, fieldArg);
+		GetSpellData()->effect_message = effect_message;
+		valSet = true;
+	}
+	else if (field == "min_range")
+	{
+		float min_range = lua_interface->GetFloatValue(state, fieldArg);
+		GetSpellData()->min_range = min_range;
+		valSet = true;
+	}
+	else if (field == "can_effect_raid")
+	{
+		int8 can_effect_raid = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->can_effect_raid = can_effect_raid;
+		valSet = true;
+	}
+	else if (field == "affect_only_group_members")
+	{
+		int8 affect_only_group_members = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->affect_only_group_members = affect_only_group_members;
+		valSet = true;
+	}
+	else if (field == "group_spell")
+	{
+		int8 group_spell = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->group_spell = group_spell;
+		valSet = true;
+	}
+	else if (field == "hit_bonus")
+	{
+		float hit_bonus = lua_interface->GetFloatValue(state, fieldArg);
+		GetSpellData()->hit_bonus = hit_bonus;
+		valSet = true;
+	}
+	else if (field == "display_spell_tier")
+	{
+		int8 display_spell_tier = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->display_spell_tier = display_spell_tier;
+		valSet = true;
+	}
+	else if (field == "is_active")
+	{
+		int8 is_active = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->is_active = is_active;
+		valSet = true;
+	}
+	else if (field == "det_type")
+	{
+		int8 det_type = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->det_type = det_type;
+		valSet = true;
+	}
+	else if (field == "incurable")
+	{
+		bool incurable = lua_interface->GetBooleanValue(state, fieldArg);
+		GetSpellData()->incurable = incurable;
+		valSet = true;
+	}
+	else if (field == "control_effect_type")
+	{
+		int8 control_effect_type = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->control_effect_type = control_effect_type;
+		valSet = true;
+	}
+	else if (field == "casting_flags")
+	{
+		int32 casting_flags = lua_interface->GetInt32Value(state, fieldArg);
+		GetSpellData()->casting_flags = casting_flags;
+		valSet = true;
+	}
+	else if (field == "cast_while_moving")
+	{
+		bool cast_while_moving = lua_interface->GetBooleanValue(state, fieldArg);
+		GetSpellData()->cast_while_moving = cast_while_moving;
+		valSet = true;
+	}
+	else if (field == "persist_though_death")
+	{
+		bool persist_though_death = lua_interface->GetBooleanValue(state, fieldArg);
+		GetSpellData()->persist_though_death = persist_though_death;
+		valSet = true;
+	}
+	else if (field == "not_maintained")
+	{
+		bool not_maintained = lua_interface->GetBooleanValue(state, fieldArg);
+		GetSpellData()->not_maintained = not_maintained;
+		valSet = true;
+	}
+	else if (field == "is_aa")
+	{
+		bool is_aa = lua_interface->GetBooleanValue(state, fieldArg);
+		GetSpellData()->is_aa = is_aa;
+		valSet = true;
+	}
+	else if (field == "savage_bar")
+	{
+		int8 savage_bar = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->savage_bar = savage_bar;
+		valSet = true;
+	}
+	else if (field == "spell_type")
+	{
+		int8 spell_type = lua_interface->GetInt8Value(state, fieldArg);
+		GetSpellData()->spell_type = spell_type;
+		valSet = true;
+	}
+
+	return valSet;
+}
 int16 Spell::GetSpellIcon(){
 int16 Spell::GetSpellIcon(){
 	if (spell)
 	if (spell)
 		return spell->icon;
 		return spell->icon;
@@ -1191,10 +2044,14 @@ bool Spell::IsControlSpell(){
 	return control_spell;
 	return control_spell;
 }
 }
 
 
-bool Spell::IsOffenseSpell(){
+bool Spell::IsOffenseSpell() {
 	return offense_spell;
 	return offense_spell;
 }
 }
 
 
+bool Spell::IsCopiedSpell() {
+	return copied_spell;
+}
+
 void Spell::ModifyCastTime(Entity* caster){
 void Spell::ModifyCastTime(Entity* caster){
 	int16 cast_time = spell->cast_time;
 	int16 cast_time = spell->cast_time;
 	float cast_speed = caster->GetInfoStruct()->casting_speed;
 	float cast_speed = caster->GetInfoStruct()->casting_speed;

+ 10 - 1
EQ2/source/WorldServer/Spells.h

@@ -28,6 +28,8 @@
 #include "../common/Mutex.h"
 #include "../common/Mutex.h"
 #include "AltAdvancement/AltAdvancement.h"
 #include "AltAdvancement/AltAdvancement.h"
 
 
+#include "../LUA/lua.hpp"
+
 #define SPELL_TARGET_SELF			0
 #define SPELL_TARGET_SELF			0
 #define SPELL_TARGET_ENEMY			1
 #define SPELL_TARGET_ENEMY			1
 #define SPELL_TARGET_GROUP_AE		2
 #define SPELL_TARGET_GROUP_AE		2
@@ -289,6 +291,7 @@ public:
 	~Spell();
 	~Spell();
 	Spell();
 	Spell();
 	Spell(SpellData* in_spell);
 	Spell(SpellData* in_spell);
+	Spell(Spell* host_spell);
 	EQ2Packet* SerializeSpell(Client* client, bool display, bool trait_display = false, int8 packet_type = 0, int8 sub_packet_type = 0, const char* struct_name = 0);
 	EQ2Packet* SerializeSpell(Client* client, bool display, bool trait_display = false, int8 packet_type = 0, int8 sub_packet_type = 0, const char* struct_name = 0);
 	EQ2Packet* SerializeSpecialSpell(Client* client, bool display, int8 packet_type = 0, int8 sub_packet_type = 0);
 	EQ2Packet* SerializeSpecialSpell(Client* client, bool display, int8 packet_type = 0, int8 sub_packet_type = 0);
 	EQ2Packet* SerializeAASpell(Client* client,int8 tier, AltAdvanceData* data, bool display, bool trait_display = false, int8 packet_type = 0, int8 sub_packet_type = 0, const char* struct_name = 0);
 	EQ2Packet* SerializeAASpell(Client* client,int8 tier, AltAdvanceData* data, bool display, bool trait_display = false, int8 packet_type = 0, int8 sub_packet_type = 0, const char* struct_name = 0);
@@ -313,6 +316,8 @@ public:
 	int16 GetSavageryRequired(Spawn* spawn);
 	int16 GetSavageryRequired(Spawn* spawn);
 	int16 GetDissonanceRequired(Spawn* spawn);
 	int16 GetDissonanceRequired(Spawn* spawn);
 	SpellData* GetSpellData();
 	SpellData* GetSpellData();
+	bool GetSpellData(lua_State* state, std::string field);
+	bool SetSpellData(lua_State* state, std::string field, int8 fieldArg);
 	bool ScribeAllowed(Player* player);
 	bool ScribeAllowed(Player* player);
 	vector<LUAData*>* GetLUAData();
 	vector<LUAData*>* GetLUAData();
 	vector <LevelArray*>* GetSpellLevels();
 	vector <LevelArray*>* GetSpellLevels();
@@ -324,6 +329,7 @@ public:
 	bool IsDamageSpell();
 	bool IsDamageSpell();
 	bool IsControlSpell();
 	bool IsControlSpell();
 	bool IsOffenseSpell();
 	bool IsOffenseSpell();
+	bool IsCopiedSpell();
 	void ModifyCastTime(Entity* caster);
 	void ModifyCastTime(Entity* caster);
 	bool CastWhileStunned();
 	bool CastWhileStunned();
 	bool CastWhileMezzed();
 	bool CastWhileMezzed();
@@ -331,15 +337,18 @@ public:
 	bool CastWhileFeared();
 	bool CastWhileFeared();
 
 
 
 
-
 	vector<SpellDisplayEffect*> effects;
 	vector<SpellDisplayEffect*> effects;
 	vector<LUAData*> lua_data;
 	vector<LUAData*> lua_data;
+
+	void LockSpellInfo() { MSpellInfo.lock(); }
+	void UnlockSpellInfo() { MSpellInfo.unlock(); }
 private:
 private:
 	bool heal_spell;
 	bool heal_spell;
 	bool buff_spell;
 	bool buff_spell;
 	bool damage_spell;
 	bool damage_spell;
 	bool control_spell;
 	bool control_spell;
 	bool offense_spell;
 	bool offense_spell;
+	bool copied_spell;
 
 
 	SpellData* spell;
 	SpellData* spell;
 	
 	

+ 1 - 0
EQ2/source/WorldServer/Zone/mob_movement_manager.h

@@ -86,6 +86,7 @@ public:
 		MobListMutex.writelock();
 		MobListMutex.writelock();
 		RunningCommandProcess = status;
 		RunningCommandProcess = status;
 		MobListMutex.releasewritelock();
 		MobListMutex.releasewritelock();
+		return true;
 	}
 	}
 private:
 private:
 	MobMovementManager(const MobMovementManager&);
 	MobMovementManager(const MobMovementManager&);

+ 3 - 3
EQ2/source/WorldServer/zoneserver.cpp

@@ -3680,7 +3680,7 @@ void ZoneServer::SetSpawnCommand(int32 spawn_id, int8 type, char* value, Client*
 	LogWrite(MISC__TODO, 1, "TODO", "%s does nothing!\n%s, %i", __FUNCTION__, __FILE__, __LINE__);
 	LogWrite(MISC__TODO, 1, "TODO", "%s does nothing!\n%s, %i", __FUNCTION__, __FILE__, __LINE__);
 }
 }
 
 
-void ZoneServer::ApplySetSpawnCommand(Client* client, Spawn* target, int8 type, char* value){
+void ZoneServer::ApplySetSpawnCommand(Client* client, Spawn* target, int8 type, const char* value){
 	// This will apply the /spawn set command to all the spawns in the zone with the same DB ID, we do not want to set
 	// This will apply the /spawn set command to all the spawns in the zone with the same DB ID, we do not want to set
 	// location values (x, y, z, heading, grid) for all spawns in the zone with the same DB ID, only the targeted spawn
 	// location values (x, y, z, heading, grid) for all spawns in the zone with the same DB ID, only the targeted spawn
 	if(type == SPAWN_SET_VALUE_SPAWNENTRY_SCRIPT || type == SPAWN_SET_VALUE_SPAWNLOCATION_SCRIPT || (type >= SPAWN_SET_VALUE_X && type <= SPAWN_SET_VALUE_LOCATION) ||
 	if(type == SPAWN_SET_VALUE_SPAWNENTRY_SCRIPT || type == SPAWN_SET_VALUE_SPAWNLOCATION_SCRIPT || (type >= SPAWN_SET_VALUE_X && type <= SPAWN_SET_VALUE_LOCATION) ||
@@ -5599,9 +5599,9 @@ Spell* ZoneServer::GetSpell(Entity* caster){
 	return spell;
 	return spell;
 }
 }
 
 
-void ZoneServer::ProcessSpell(Spell* spell, Entity* caster, Spawn* target, bool lock, bool harvest_spell){
+void ZoneServer::ProcessSpell(Spell* spell, Entity* caster, Spawn* target, bool lock, bool harvest_spell, LuaSpell* customSpell, int16 custom_cast_time){
 	if(spellProcess)
 	if(spellProcess)
-		spellProcess->ProcessSpell(this, spell, caster, target, lock, harvest_spell);
+		spellProcess->ProcessSpell(this, spell, caster, target, lock, harvest_spell, customSpell, custom_cast_time);
 }
 }
 
 
 void ZoneServer::ProcessEntityCommand(EntityCommand* entity_command, Entity* caster, Spawn* target, bool lock) {
 void ZoneServer::ProcessEntityCommand(EntityCommand* entity_command, Entity* caster, Spawn* target, bool lock) {

+ 2 - 2
EQ2/source/WorldServer/zoneserver.h

@@ -258,7 +258,7 @@ public:
 	
 	
 	int16	SetSpawnTargetable(Spawn* spawn, float distance);
 	int16	SetSpawnTargetable(Spawn* spawn, float distance);
 	int16	SetSpawnTargetable(int32 spawn_id);
 	int16	SetSpawnTargetable(int32 spawn_id);
-	void	ApplySetSpawnCommand(Client* client, Spawn* target, int8 type, char* value);
+	void	ApplySetSpawnCommand(Client* client, Spawn* target, int8 type, const char* value);
 	void	SetSpawnCommand(Spawn* spawn, int8 type, char* value, Client* client = 0);
 	void	SetSpawnCommand(Spawn* spawn, int8 type, char* value, Client* client = 0);
 	void	SetSpawnCommand(int32 spawn_id, int8 type, char* value, Client* client = 0);
 	void	SetSpawnCommand(int32 spawn_id, int8 type, char* value, Client* client = 0);
 	void	AddLoot(NPC* npc);
 	void	AddLoot(NPC* npc);
@@ -368,7 +368,7 @@ public:
 	void	RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, bool delete_recast = true);
 	void	RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, bool delete_recast = true);
 	void	Interrupted(Entity* caster, Spawn* interruptor, int16 error_code, bool cancel = false, bool from_movement = false);
 	void	Interrupted(Entity* caster, Spawn* interruptor, int16 error_code, bool cancel = false, bool from_movement = false);
 	Spell*	GetSpell(Entity* caster);
 	Spell*	GetSpell(Entity* caster);
-	void	ProcessSpell(Spell* spell, Entity* caster, Spawn* target = 0, bool lock = true, bool harvest_spell = false);
+	void	ProcessSpell(Spell* spell, Entity* caster, Spawn* target = 0, bool lock = true, bool harvest_spell = false, LuaSpell* customSpell = 0, int16 custom_cast_time = 0);
 	void	ProcessEntityCommand(EntityCommand* entity_command, Entity* caster, Spawn* target, bool lock = true);
 	void	ProcessEntityCommand(EntityCommand* entity_command, Entity* caster, Spawn* target, bool lock = true);
 	void	AddPlayerTracking(Player* player);
 	void	AddPlayerTracking(Player* player);
 	void	RemovePlayerTracking(Player* player, int8 mode);
 	void	RemovePlayerTracking(Player* player, int8 mode);

+ 2 - 2
EQ2/source/common/ConfigReader.cpp

@@ -212,9 +212,9 @@ void ConfigReader::loadDataStruct(PacketStruct* packet, XMLNode parentNode, bool
 					vector<DataStruct*>* structs = substruct_packet->getStructs();
 					vector<DataStruct*>* structs = substruct_packet->getStructs();
 					DataStruct* ds = 0;
 					DataStruct* ds = 0;
 					int i = 0;
 					int i = 0;
-					char tmp[10] = {0};
+					char tmp[12] = {0};
 					for(i=0;i<num_size;i++){
 					for(i=0;i<num_size;i++){
-						sprintf(tmp,"%i",i);
+						snprintf(tmp, sizeof(tmp)-1, "%i", i);
 						for(itr=structs->begin();itr!=structs->end();itr++) {
 						for(itr=structs->begin();itr!=structs->end();itr++) {
 							ds = *itr;
 							ds = *itr;
 							string new_name;
 							string new_name;

+ 1 - 1
EQ2/source/common/servertalk.h

@@ -637,7 +637,7 @@ struct ZoneUpdate_Struct{
 };
 };
 
 
 struct ZoneUpdateList_Struct{
 struct ZoneUpdateList_Struct{
-	sint16	total_updates;
+	uint16	total_updates;
 	char data[0];
 	char data[0];
 };
 };
 
 

+ 2 - 2
EQ2/source/common/version.h

@@ -38,9 +38,9 @@
 #endif
 #endif
 
 
 #if defined(LOGIN)
 #if defined(LOGIN)
-#define CURRENT_VERSION	"0.8.1-virgo1"
+#define CURRENT_VERSION	"0.8.1-virgo2"
 #elif defined(WORLD)
 #elif defined(WORLD)
-#define CURRENT_VERSION	"0.8.1-virgo1"
+#define CURRENT_VERSION	"0.8.1-virgo2"
 #else
 #else
 #define CURRENT_VERSION	"0.7.3-dev"
 #define CURRENT_VERSION	"0.7.3-dev"
 #endif
 #endif

+ 112 - 0
cmake/FindMySQL.cmake

@@ -0,0 +1,112 @@
+#[==[
+Provides the following variables:
+
+  * `MySQL_INCLUDE_DIRS`: Include directories necessary to use MySQL.
+  * `MySQL_LIBRARIES`: Libraries necessary to use MySQL.
+  * A `MySQL::MySQL` imported target.
+#]==]
+
+# No .pc files are shipped with MySQL on Windows.
+set(_MySQL_use_pkgconfig 0)
+if (NOT WIN32)
+  find_package(PkgConfig)
+  if (PkgConfig_FOUND)
+    set(_MySQL_use_pkgconfig 1)
+  endif ()
+endif ()
+
+if (_MySQL_use_pkgconfig)
+  pkg_check_modules(_libmariadb "libmariadb" QUIET IMPORTED_TARGET)
+  unset(_mysql_target)
+  if (_libmariadb_FOUND)
+    set(_mysql_target "_libmariadb")
+  else ()
+    pkg_check_modules(_mariadb "mariadb" QUIET IMPORTED_TARGET)
+    if (NOT _mariadb_FOUND)
+      pkg_check_modules(_mysql "mysql" QUIET IMPORTED_TARGET)
+      if (_mysql_FOUND)
+        set(_mysql_target "_mysql")
+      endif ()
+    else ()
+      set(_mysql_target "_mariadb")
+      if (_mariadb_VERSION VERSION_LESS 10.4)
+        get_property(_include_dirs
+          TARGET    "PkgConfig::_mariadb"
+          PROPERTY  "INTERFACE_INCLUDE_DIRECTORIES")
+        # Remove "${prefix}/mariadb/.." from the interface since it breaks other
+        # projects.
+        list(FILTER _include_dirs EXCLUDE REGEX "\\.\\.")
+        set_property(TARGET "PkgConfig::_mariadb"
+          PROPERTY
+            "INTERFACE_INCLUDE_DIRECTORIES" "${_include_dirs}")
+        unset(_include_dirs)
+      endif ()
+    endif ()
+  endif ()
+
+  set(MySQL_FOUND 0)
+  if (_mysql_target)
+    set(MySQL_FOUND 1)
+    set(MySQL_INCLUDE_DIRS ${${_mysql_target}_INCLUDE_DIRS})
+    set(MySQL_LIBRARIES ${${_mysql_target}_LINK_LIBRARIES})
+    if (NOT TARGET MySQL::MySQL)
+      add_library(MySQL::MySQL INTERFACE IMPORTED)
+      target_link_libraries(MySQL::MySQL
+        INTERFACE "PkgConfig::${_mysql_target}")
+    endif ()
+  endif ()
+  unset(_mysql_target)
+else ()
+  set(_MySQL_mariadb_versions 10.2 10.3)
+  set(_MySQL_versions 5.0)
+  set(_MySQL_paths)
+  foreach (_MySQL_version IN LISTS _MySQL_mariadb_versions)
+    list(APPEND _MySQL_paths
+      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MariaDB ${_MySQL_version};INSTALLDIR]"
+      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MariaDB ${_MySQL_version} (x64);INSTALLDIR]")
+  endforeach ()
+  foreach (_MySQL_version IN LISTS _MySQL_versions)
+    list(APPEND _MySQL_paths
+      "C:/Program Files/MySQL/MySQL Server ${_MySQL_version}/lib/opt"
+      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server ${_MySQL_version};Location]"
+      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server ${_MySQL_version};Location]")
+  endforeach ()
+  unset(_MySQL_version)
+  unset(_MySQL_versions)
+  unset(_MySQL_mariadb_versions)
+
+  find_path(MySQL_INCLUDE_DIR
+    NAMES mysql.h
+    PATHS
+      "C:/Program Files/MySQL/include"
+      "C:/MySQL/include"
+      ${_MySQL_paths}
+    PATH_SUFFIXES include include/mysql
+    DOC "Location of mysql.h")
+  mark_as_advanced(MySQL_INCLUDE_DIR)
+  find_library(MySQL_LIBRARY
+    NAMES libmariadb mysql libmysql mysqlclient
+    PATHS
+      "C:/Program Files/MySQL/lib"
+      "C:/MySQL/lib/debug"
+      ${_MySQL_paths}
+    PATH_SUFFIXES lib lib/opt
+    DOC "Location of the mysql library")
+  mark_as_advanced(MySQL_LIBRARY)
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(MySQL
+    REQUIRED_VARS MySQL_INCLUDE_DIR MySQL_LIBRARY)
+
+  if (MySQL_FOUND)
+    set(MySQL_INCLUDE_DIRS "${MySQL_INCLUDE_DIR}")
+    set(MySQL_LIBRARIES "${MySQL_LIBRARY}")
+    if (NOT TARGET MySQL::MySQL)
+      add_library(MySQL::MySQL UNKNOWN IMPORTED)
+      set_target_properties(MySQL::MySQL PROPERTIES
+      IMPORTED_LOCATION "${MySQL_LIBRARY}"
+        INTERFACE_INCLUDE_DIRECTORIES "${MySQL_INCLUDE_DIR}")
+    endif ()
+  endif ()
+endif ()
+unset(_MySQL_use_pkgconfig)

BIN
server/EQ2Login__Debug64.exe


BIN
server/EQ2World__Debug_x64.exe


+ 9 - 9
server/SpawnScripts/ThunderingSteppes/Brianna.lua

@@ -31,7 +31,12 @@ function hailed(NPC, Spawn)
 		PlayFlavor(NPC, "voiceover/english/voice_emotes/greetings/greetings_3_1009.mp3", "", "", 0, 0, Spawn)
 		PlayFlavor(NPC, "voiceover/english/voice_emotes/greetings/greetings_3_1009.mp3", "", "", 0, 0, Spawn)
 	end
 	end
 	
 	
-	if HasQuest(Spawn, WatchYourStepinTheTSPartIII) and GetQuestStep(Spawn, WatchYourStepinTheTSPartIII) == 3 then
+	if HasQuest(Spawn, SuppliesForBrianna) and GetQuestStep(Spawn, SuppliesForBrianna) == 2 then
+		-- turn in SuppliesForBrianna
+		AddConversationOption(conversation, "Yes right here.", "dlg_1_1")
+		AddConversationOption(conversation, "Um, I have some but not for you.")
+		StartConversation(conversation, NPC, Spawn, "Well, do you have the supplies?")
+	elseif HasQuest(Spawn, WatchYourStepinTheTSPartIII) and GetQuestStep(Spawn, WatchYourStepinTheTSPartIII) == 2 then
 		-- start SuppliesForBrianna
 		-- start SuppliesForBrianna
 		AddConversationOption(conversation, "No, I'm here to deliver a package to you.", "dlg_0_1")
 		AddConversationOption(conversation, "No, I'm here to deliver a package to you.", "dlg_0_1")
 		AddConversationOption(conversation, "Oh okay. Thanks anyways.")
 		AddConversationOption(conversation, "Oh okay. Thanks anyways.")
@@ -40,11 +45,6 @@ function hailed(NPC, Spawn)
 		-- on SuppliesForBrianna or HidesForBrianna but not ready for turn in
 		-- on SuppliesForBrianna or HidesForBrianna but not ready for turn in
 		AddConversationOption(conversation, "No but I have my best people working on it.")
 		AddConversationOption(conversation, "No but I have my best people working on it.")
 		StartConversation(conversation, NPC, Spawn, "Well, did you bring the supplies yet?")
 		StartConversation(conversation, NPC, Spawn, "Well, did you bring the supplies yet?")
-	elseif HasQuest(Spawn, SuppliesForBrianna) and GetQuestStep(Spawn, SuppliesForBrianna) == 2 then
-		-- turn in SuppliesForBrianna
-		AddConversationOption(conversation, "Yes right here.", "dlg_1_1")
-		AddConversationOption(conversation, "Um, I have some but not for you.")
-		StartConversation(conversation, NPC, Spawn, "Well, do you have the supplies?")
 	elseif HasCompletedQuest(Spawn, SuppliesForBrianna) and not HasQuest(Spawn, HidesForBrianna) and not HasCompletedQuest(Spawn, HidesForBrianna) then
 	elseif HasCompletedQuest(Spawn, SuppliesForBrianna) and not HasQuest(Spawn, HidesForBrianna) and not HasCompletedQuest(Spawn, HidesForBrianna) then
 		-- start HidesForBrianna
 		-- start HidesForBrianna
 		AddConversationOption(conversation, "Yes I am.", "dlg_2_1")
 		AddConversationOption(conversation, "Yes I am.", "dlg_2_1")
@@ -79,7 +79,7 @@ function dlg_0_2(NPC, Spawn)
 end
 end
 
 
 function dlg_0_3(NPC, Spawn)
 function dlg_0_3(NPC, Spawn)
-	OfferQuest(NPC, Player, SuppliesForBrianna)
+	OfferQuest(NPC, Spawn, SuppliesForBrianna)
 end
 end
 
 
 function dlg_1_1(NPC, Spawn)
 function dlg_1_1(NPC, Spawn)
@@ -102,7 +102,7 @@ function dlg_2_1(NPC, Spawn)
 end
 end
 
 
 function dlg_2_2(NPC, Spawn)
 function dlg_2_2(NPC, Spawn)
-	OfferQuest(NPC, Player, HidesForBrianna)
+	OfferQuest(NPC, Spawn, HidesForBrianna)
 end
 end
 
 
 function dlg_3_1(NPC, Spawn)
 function dlg_3_1(NPC, Spawn)
@@ -112,4 +112,4 @@ function dlg_3_1(NPC, Spawn)
 	SetStepComplete(Spawn, HidesForBrianna, 2)
 	SetStepComplete(Spawn, HidesForBrianna, 2)
 	AddConversationOption(conversation, "Thanks.")
 	AddConversationOption(conversation, "Thanks.")
 	StartConversation(conversation, NPC, Spawn, "Great, these look like it's the first time they have ever seen the sun. Thanks for your help. I don't have anything else for you today, but check back again.")
 	StartConversation(conversation, NPC, Spawn, "Great, these look like it's the first time they have ever seen the sun. Thanks for your help. I don't have anything else for you today, but check back again.")
-end
+end

+ 3 - 3
server/SpawnScripts/ThunderingSteppes/Jacques.lua

@@ -133,7 +133,7 @@ function dlg_3_1(NPC, Spawn)
 end
 end
 
 
 function dlg_4_1(NPC, Spawn)
 function dlg_4_1(NPC, Spawn)
-	OfferQuest(NPC, Player, WatchYourStepInTheTSPartIII)
+	OfferQuest(NPC, Spawn, WatchYourStepInTheTSPartIII)
 end
 end
 	
 	
 function dlg_4_2(NPC, Spawn)
 function dlg_4_2(NPC, Spawn)
@@ -154,5 +154,5 @@ function dlg_5_1(NPC, Spawn)
 end
 end
 
 
 function dlg_6_1(NPC, Spawn)
 function dlg_6_1(NPC, Spawn)
-	OfferQuest(NPC, Player, WatchYourStepInTheTSPartIV)
-end
+	OfferQuest(NPC, Spawn, WatchYourStepInTheTSPartIV)
+end