فهرست منبع

LUA additions

Fix #196 - LUA custom spell support (for things like item scripts)
Fix #195 - Alignment LUA functions
CastSpell now has a cast_time argument extended to it
image 4 سال پیش
والد
کامیت
900a32b259

+ 171 - 1
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -36,6 +36,7 @@
 #include "ClientPacketFunctions.h"
 #include "Transmute.h"
 #include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string.hpp>
 
 extern WorldDatabase database;
 extern LuaInterface* lua_interface;
@@ -1206,6 +1207,7 @@ int EQ2Emu_lua_CastSpell(lua_State* state) {
 	int32 spell_id = lua_interface->GetInt32Value(state, 2);
 	int8 spell_tier = lua_interface->GetInt8Value(state, 3);
 	Spawn* caster = lua_interface->GetSpawn(state, 4);
+	int16 custom_cast_time = lua_interface->GetInt16Value(state, 5);
 
 	if (!target) {
 		lua_interface->LogError("%s: LUA CastSpell command error: target is not a valid spawn", lua_interface->GetScriptName(state));
@@ -1233,7 +1235,7 @@ int EQ2Emu_lua_CastSpell(lua_State* state) {
 	if (!caster)
 		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;
 }
 
@@ -9948,4 +9950,172 @@ int EQ2Emu_lua_GetTemporaryTransportID(lua_State* state) {
 		return 1;
 	}
 	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

@@ -448,4 +448,12 @@ int EQ2Emu_lua_SendUpdateDefaultCommand(lua_State* state);
 int EQ2Emu_lua_SendTransporters(lua_State* state);
 int EQ2Emu_lua_SetTemporaryTransportID(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

+ 55 - 4
EQ2/source/WorldServer/LuaInterface.cpp

@@ -649,6 +649,10 @@ void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool
 	}
 	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) {
 		AddPendingSpellDelete(spell);
 	}
@@ -1076,6 +1080,14 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 	lua_register(state, "SendTransporters", EQ2Emu_lua_SendTransporters);
 	lua_register(state, "SetTemporaryTransportID", EQ2Emu_lua_SetTemporaryTransportID);
 	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, ...)  {
@@ -1118,6 +1130,7 @@ void LuaInterface::DeletePendingSpells(bool all) {
 		for (del_itr = tmp_deletes.begin(); del_itr != tmp_deletes.end(); del_itr++) {
 			spell = *del_itr;
 			spells_pending_delete.erase(spell);
+
 			safe_delete(spell);
 		}
 	}
@@ -1254,12 +1267,12 @@ Skill* LuaInterface::GetSkill(lua_State* state, int8 arg_num) {
 	Skill* ret = 0;
 	if (lua_islightuserdata(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));
 		}
-		else if(!data->IsSkill()){
+		else if (!data->IsSkill()) {
 			lua_Debug ar;
- 			lua_getstack (state, 1, &ar);
+			lua_getstack(state, 1, &ar);
 			lua_getinfo(state, "Sln", &ar);
 			LogError("%s: Invalid data type used for GetSkill in %s (line %d)", GetScriptName(state), ar.source, ar.currentline);
 		}
@@ -1269,6 +1282,25 @@ Skill* LuaInterface::GetSkill(lua_State* state, int8 arg_num) {
 	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* ret = 0;
 	if(lua_islightuserdata(state, arg_num)){
@@ -1411,6 +1443,13 @@ void LuaInterface::SetZoneValue(lua_State* state, ZoneServer* zone) {
 	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)  {
 	string lua_script = string(name);
 	if (lua_script.find(".lua") == string::npos)
@@ -1421,7 +1460,7 @@ LuaSpell* LuaInterface::GetSpell(const char* name)  {
 		LuaSpell* spell = spells[lua_script];
 		LuaSpell* new_spell = new LuaSpell;
 		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.Disable();
 		new_spell->resisted = false;
@@ -1794,6 +1833,10 @@ bool LUAUserData::IsSkill() {
 	return false;
 }
 
+bool LUAUserData::IsSpell() {
+	return false;
+}
+
 LUAConversationOptionWrapper::LUAConversationOptionWrapper(){
 	correctly_initialized = true;
 }
@@ -1848,4 +1891,12 @@ LUASkillWrapper::LUASkillWrapper() {
 
 bool LUASkillWrapper::IsSkill() {
 	return true;
+}
+
+LUASpellWrapper::LUASpellWrapper() {
+	correctly_initialized = true;
+}
+
+bool LUASpellWrapper::IsSpell() {
+	return true;
 }

+ 10 - 0
EQ2/source/WorldServer/LuaInterface.h

@@ -106,6 +106,7 @@ public:
 	virtual bool IsZone();
 	virtual bool IsItem();
 	virtual bool IsSkill();
+	virtual bool IsSpell();
 	bool correctly_initialized;
 	Item* item;
 	ZoneServer* zone;
@@ -114,6 +115,7 @@ public:
 	vector<OptionWindowOption>* option_window_option;
 	Quest* quest;
 	Skill* skill;
+	LuaSpell* spell;
 };
 
 class LUAConversationOptionWrapper : public LUAUserData{
@@ -158,6 +160,12 @@ public:
 	bool IsSkill();
 };
 
+class LUASpellWrapper : public LUAUserData {
+public:
+	LUASpellWrapper();
+	bool IsSpell();
+};
+
 class LuaInterface {
 public:
 	LuaInterface();
@@ -176,6 +184,7 @@ public:
 	Quest*			GetQuest(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);
+	LuaSpell*		GetSpell(lua_State* state, int8 arg_num = 1);
 	vector<ConversationOption>*	GetConversation(lua_State* state, int8 arg_num = 1);
 	vector<OptionWindowOption>* GetOptionWindow(lua_State* state, int8 arg_num = 1);
 	int8			GetInt8Value(lua_State* state, int8 arg_num = 1);
@@ -198,6 +207,7 @@ public:
 	void			SetItemValue(lua_State* state, Item* item);
 	void			SetQuestValue(lua_State* state, Quest* quest);
 	void			SetZoneValue(lua_State* state, ZoneServer* zone);
+	void			SetSpellValue(lua_State* state, LuaSpell* spell);
 	void			SetConversationValue(lua_State* state, vector<ConversationOption>* conversation);
 	void			SetOptionWindowValue(lua_State* state, vector<OptionWindowOption>* optionWindow);
 

+ 14 - 4
EQ2/source/WorldServer/SpellProcess.cpp

@@ -783,20 +783,25 @@ Spawn* SpellProcess::GetSpellTarget(Entity* caster){
 	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;
-		int8 target_type = spell->GetSpellData()->target_type;
 		//int16 version = 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());
 
+		// this will only hit if customSpell is null and we go through the lua_interface
 		if(!lua_spell)
 		{
 			string lua_script = spell->GetSpellData()->lua_script;
@@ -814,6 +819,8 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		if (!target)
 			target = caster;
 
+		int8 target_type = spell->GetSpellData()->target_type;
+
 		lua_spell->caster = caster;
 		lua_spell->spell = spell;
 		int32 target_id = target->GetID();
@@ -1235,6 +1242,9 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
 		else
 			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
 		spell->ModifyCastTime(caster);
 

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

@@ -171,7 +171,7 @@ public:
 	/// <param name='target'>The target(Spawn) of the spell</param>
 	/// <param name='lock'>??? not currently used</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>
 	/// <param name='zone'>The current ZoneServer</param>

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

@@ -24,12 +24,14 @@
 #include "Traits/Traits.h"
 #include "AltAdvancement/AltAdvancement.h"
 #include <cmath>
+#include "LuaInterface.h"
 
 extern ConfigReader configReader;
 extern WorldDatabase database;
 extern MasterTraitList master_trait_list;
 extern MasterAAList master_aa_list;
 extern MasterSpellList master_spell_list;
+extern LuaInterface* lua_interface;
 
 Spell::Spell(){
 	spell = new SpellData;
@@ -38,6 +40,34 @@ Spell::Spell(){
 	damage_spell = false;
 	control_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())
+		memcpy(spell, host_spell->GetSpellData(), sizeof(SpellData));
+
+	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);
+	}
+	host_spell->UnlockSpellInfo();
+
 	MSpellInfo.SetName("Spell::MSpellInfo");
 }
 
@@ -48,6 +78,7 @@ Spell::Spell(SpellData* in_spell){
 	damage_spell = false;
 	control_spell = false;
 	offense_spell = false;
+	copied_spell = false;
 	MSpellInfo.SetName("Spell::MSpellInfo");
 }
 
@@ -1158,6 +1189,735 @@ SpellData* Spell::GetSpellData(){
 	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(){
 	if (spell)
 		return spell->icon;
@@ -1191,10 +1951,14 @@ bool Spell::IsControlSpell(){
 	return control_spell;
 }
 
-bool Spell::IsOffenseSpell(){
+bool Spell::IsOffenseSpell() {
 	return offense_spell;
 }
 
+bool Spell::IsCopiedSpell() {
+	return copied_spell;
+}
+
 void Spell::ModifyCastTime(Entity* caster){
 	int16 cast_time = spell->cast_time;
 	float cast_speed = caster->GetInfoStruct()->casting_speed;

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

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

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

@@ -5617,9 +5617,9 @@ Spell* ZoneServer::GetSpell(Entity* caster){
 	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)
-		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) {

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

@@ -368,7 +368,7 @@ public:
 	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);
 	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	AddPlayerTracking(Player* player);
 	void	RemovePlayerTracking(Player* player, int8 mode);

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

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