Browse Source

CustomSpell additions SetSpellDisplayEffect(idx, field, value) and GetSpellDisplayEffect((idx, field) added

- Allows custom spell_display_effects
- Spell examine is now unique to the spell cast, we have to create a unique spell id that is temporarily used by the custom spell to accomplish this

eg.

Spell = GetSpell(90044, 1)
SetSpellDisplayEffect(Spell, 0, "description", "Applies Painbringer.  Lasts for millions of seconds!")
CastCustomSpell(Spell, Player, Player)

result: https://cdn.discordapp.com/attachments/684934458738212962/758297380302225418/unknown.png
image 3 years ago
parent
commit
9a78416ac6

+ 81 - 0
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -10492,6 +10492,8 @@ int EQ2Emu_lua_GetSpell(lua_State* state) {
 
 		lua_spell->spell = new Spell(spell);
 
+		lua_interface->AddCustomSpell(lua_spell);
+
 		lua_interface->SetSpellValue(state, lua_spell);
 		return 1;
 	}
@@ -10675,6 +10677,85 @@ int EQ2Emu_lua_GetSpellDataIndex(lua_State* state) {
 }
 
 
+int EQ2Emu_lua_SetSpellDisplayEffect(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	LuaSpell* spell = lua_interface->GetSpell(state);
+	int8 idx = lua_interface->GetInt32Value(state, 2);
+	string field = lua_interface->GetStringValue(state, 3);
+
+	boost::to_lower(field);
+
+	if (!spell) {
+		lua_interface->LogError("%s: Spell not given in SetSpellDisplayEffect!", lua_interface->GetScriptName(state));
+		return 0;
+	}
+	if (!spell->spell || !spell->spell->GetSpellData()) {
+		lua_interface->LogError("%s: Inner Spell or SpellData not given in SetSpellDisplayEffect!", lua_interface->GetScriptName(state));
+		return 0;
+	}
+
+	if (spell->spell->effects.size() <= idx)
+	{
+		lua_interface->LogError("%s: lua_data size %i <= %i (idx passed) SetSpellDisplayEffect!", lua_interface->GetScriptName(state), spell->spell->lua_data.size(), idx);
+		return 0;
+	}
+
+	// do we need to lock? eh probably not this should only be used before use of the custom spell
+	SpellDisplayEffect* effect = spell->spell->effects[idx];
+
+	if (field == "description")
+		effect->description = string(lua_interface->GetStringValue(state, 4));
+	else if (field == "bullet")
+		effect->subbullet = lua_interface->GetInt8Value(state, 4);
+	else if (field == "percentage")
+		effect->percentage = lua_interface->GetInt8Value(state, 4);
+	else // no match
+		return 0;
+
+
+	return 1;
+}
+
+int EQ2Emu_lua_GetSpellDisplayEffect(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	LuaSpell* spell = lua_interface->GetSpell(state);
+	int8 idx = lua_interface->GetInt32Value(state, 2);
+	string field = lua_interface->GetStringValue(state, 3);
+
+	boost::to_lower(field);
+
+	if (!spell) {
+		lua_interface->LogError("%s: Spell not given in GetSpellDisplayEffect!", lua_interface->GetScriptName(state));
+		return 0;
+	}
+	if (!spell->spell || !spell->spell->GetSpellData()) {
+		lua_interface->LogError("%s: Inner Spell or SpellData not given in GetSpellDisplayEffect!", lua_interface->GetScriptName(state));
+		return 0;
+	}
+
+	if (spell->spell->effects.size() <= idx)
+	{
+		lua_interface->LogError("%s: lua_data size %i <= %i (idx passed) GetSpellDisplayEffect!", lua_interface->GetScriptName(state), spell->spell->lua_data.size(), idx);
+		return 0;
+	}
+
+	// do we need to lock? eh probably not this should only be used before use of the custom spell
+	SpellDisplayEffect* effect = spell->spell->effects[idx];
+
+	if (field == "description")
+		lua_interface->SetStringValue(state, effect->description.c_str());
+	else if (field == "bullet")
+		lua_interface->SetInt32Value(state, effect->subbullet);
+	else if (field == "percentage")
+		lua_interface->SetInt32Value(state, effect->percentage);
+	else // no match
+		return 0;
+
+
+	return 1;
+}
 int EQ2Emu_lua_CastCustomSpell(lua_State* state) {
 	if (!lua_interface)
 		return 0;

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

@@ -487,4 +487,7 @@ int EQ2Emu_lua_CastCustomSpell(lua_State* state);
 
 int EQ2Emu_lua_SetSpellDataIndex(lua_State* state);
 int EQ2Emu_lua_GetSpellDataIndex(lua_State* state);
+
+int EQ2Emu_lua_SetSpellDisplayEffect(lua_State* state);
+int EQ2Emu_lua_GetSpellDisplayEffect(lua_State* state);
 #endif

+ 50 - 0
EQ2/source/WorldServer/LuaInterface.cpp

@@ -47,6 +47,8 @@ LuaInterface::LuaInterface() {
 	MLUAUserData.SetName("LuaInterface::MLUAUserData");
 	MLUAMain.SetName("LuaInterface::MLUAMain");
 	MItemScripts.SetName("LuaInterface::MItemScripts");
+	MSpellDelete.SetName("LuaInterface::MSpellDelete");
+	MCustomSpell.SetName("LuaInterface::MCustomSpell");
 	user_data_timer = new Timer(20000);
 	user_data_timer->Start();
 	spell_delete_timer = new Timer(5000);
@@ -1109,6 +1111,9 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 
 	lua_register(state, "SetSpellDataIndex", EQ2Emu_lua_SetSpellDataIndex);
 	lua_register(state, "GetSpellDataIndex", EQ2Emu_lua_GetSpellDataIndex);
+
+	lua_register(state, "SetSpellDisplayEffect", EQ2Emu_lua_SetSpellDisplayEffect);
+	lua_register(state, "GetSpellDisplayEffect", EQ2Emu_lua_GetSpellDisplayEffect);
 }
 
 void LuaInterface::LogError(const char* error, ...)  {
@@ -1153,7 +1158,10 @@ void LuaInterface::DeletePendingSpells(bool all) {
 			spells_pending_delete.erase(spell);
 
 			if (spell->spell->IsCopiedSpell())
+			{
+				RemoveCustomSpell(spell->spell->GetSpellID());
 				safe_delete(spell->spell);
+			}
 
 			safe_delete(spell);
 		}
@@ -1856,6 +1864,48 @@ void LuaInterface::AddPendingSpellDelete(LuaSpell* spell) {
 	MSpellDelete.unlock();
 }
 
+void LuaInterface::AddCustomSpell(LuaSpell* spell)
+{
+	MCustomSpell.writelock();
+	custom_spells[spell->spell->GetSpellID()] = spell;
+	MCustomSpell.releasewritelock();
+}
+
+void LuaInterface::RemoveCustomSpell(int32 id)
+{
+	MCustomSpell.writelock();
+	map<int32, LuaSpell*>::iterator itr = custom_spells.find(id);
+	if (itr != custom_spells.end())
+	{
+		custom_spells.erase(itr);
+		custom_free_spell_ids.push_front(id);
+	}
+	MCustomSpell.releasewritelock();
+}
+
+// prior to calling FindCustomSpell you should call FindCustomSpellLock and after FindCustomSpellUnlock
+LuaSpell* LuaInterface::FindCustomSpell(int32 id)
+{
+	LuaSpell* spell = 0;
+	map<int32, LuaSpell*>::iterator itr = custom_spells.find(id);
+	if (itr != custom_spells.end())
+		spell = itr->second;
+	return spell;
+}
+
+int32 LuaInterface::GetFreeCustomSpellID()
+{ 
+	int32 id = 0;
+	MCustomSpell.writelock();
+	if (!custom_free_spell_ids.empty())
+	{
+		id = custom_free_spell_ids.front();
+		custom_free_spell_ids.pop_front();
+	}
+	MCustomSpell.releasewritelock();
+	return id;
+}
+
 LUAUserData::LUAUserData(){
 	correctly_initialized = false;
 	quest = 0;

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

@@ -271,6 +271,15 @@ public:
 	void			SetSpawnScriptsReloading(bool val) { spawn_scripts_reloading = val; }
 
 	void			AddPendingSpellDelete(LuaSpell* spell);
+
+	void			AddCustomSpell(LuaSpell* spell);
+	void			RemoveCustomSpell(int32 id);
+
+	void			FindCustomSpellLock() { MCustomSpell.readlock(); }
+	void			FindCustomSpellUnlock() { MCustomSpell.releasereadlock(); }
+	LuaSpell*		FindCustomSpell(int32 id);
+
+	int32			GetFreeCustomSpellID();
 private:
 	bool			shutting_down;
 	bool			spawn_scripts_reloading;
@@ -292,6 +301,9 @@ private:
 	map<string, map<lua_State*, bool> > spawn_scripts;
 	map<string, map<lua_State*, bool> > zone_scripts;
 
+	map<int32, LuaSpell*> custom_spells;
+	std::deque<int32> custom_free_spell_ids;
+
 	map<lua_State*, string> item_inverse_scripts;
 	map<lua_State*, string> spawn_inverse_scripts;
 	map<lua_State*, string> zone_inverse_scripts;
@@ -309,5 +321,6 @@ private:
 	Mutex			MLUAUserData;
 	Mutex			MLUAMain;
 	Mutex			MSpellDelete;
+	Mutex			MCustomSpell;
 };
 #endif

+ 3 - 0
EQ2/source/WorldServer/SpellProcess.cpp

@@ -2446,7 +2446,10 @@ void SpellProcess::DeleteSpell(LuaSpell* spell)
 	RemoveSpellFromQueue(spell->spell, spell->caster);
 
 	if (spell->spell->IsCopiedSpell())
+	{
+		lua_interface->RemoveCustomSpell(spell->spell->GetSpellID());
 		safe_delete(spell->spell);
+	}
 
 	safe_delete(spell);
 }

+ 69 - 2
EQ2/source/WorldServer/Spells.cpp

@@ -52,6 +52,15 @@ Spell::Spell(Spell* host_spell)
 
 	if (host_spell->GetSpellData())
 	{
+		// try inheriting an existing custom spell id, otherwise obtain the new highest number on the spell list
+		int32 tmpid = lua_interface->GetFreeCustomSpellID();
+		if (tmpid)
+			spell->id = tmpid;
+		else
+		{
+			spell->id = master_spell_list.GetNewMaxSpellID();
+		}
+
 		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;
@@ -88,8 +97,6 @@ Spell::Spell(Spell* host_spell)
 
 		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;
@@ -2096,6 +2103,7 @@ bool Spell::ScribeAllowed(Player* player){
 }
 
 MasterSpellList::MasterSpellList(){
+	max_spell_id = 0;
 	MMasterSpellList.SetName("MasterSpellList::MMasterSpellList");
 }
 
@@ -2122,6 +2130,10 @@ void MasterSpellList::AddSpell(int32 id, int8 tier, Spell* spell){
 	spell_list[id][tier] = spell;
 	spell_name_map[spell->GetName()] = spell;
 	spell_soecrc_map[spell->GetSpellData()->soe_spell_crc] = spell;
+
+	if (id > max_spell_id)
+		max_spell_id = id;
+
 	MMasterSpellList.unlock();
 }
 
@@ -2147,12 +2159,50 @@ Spell* MasterSpellList::GetSpellByCRC(int32 spell_crc){
 
 EQ2Packet* MasterSpellList::GetSpellPacket(int32 id, int8 tier, Client* client, bool display, int8 packet_type){
 	Spell* spell = GetSpell(id, tier);
+
+	// if we can't find it on the master spell list, see if it is a custom spell
+	if (!spell)
+	{
+		lua_interface->FindCustomSpellLock();
+		LuaSpell* tmpSpell = lua_interface->FindCustomSpell(id);
+		EQ2Packet* pack = 0;
+		if (tmpSpell)
+		{
+			spell = tmpSpell->spell;
+			pack = spell->SerializeSpell(client, display, packet_type);
+		}
+
+		lua_interface->FindCustomSpellUnlock();
+		return pack;
+	}
+
 	if(spell)
 		return spell->SerializeSpell(client, display, packet_type);
 	return 0;
 }
 EQ2Packet* MasterSpellList::GetAASpellPacket(int32 id, int8 tier, Client* client, bool display, int8 packet_type) {
 	Spell* spell = GetSpell(id, (tier == 0 ? 1 : tier));
+
+	// if we can't find it on the master spell list, see if it is a custom spell
+	if (!spell)
+	{
+		lua_interface->FindCustomSpellLock();
+		LuaSpell* tmpSpell = lua_interface->FindCustomSpell(id);
+		EQ2Packet* pack = 0;
+		if (tmpSpell)
+		{
+			spell = tmpSpell->spell;
+			// TODO: this isn't a tested thing yet... need to add custom spells to alt advancement?
+			AltAdvanceData* data = master_aa_list.GetAltAdvancement(id);
+
+			if(data)
+				pack = spell->SerializeAASpell(client, tier, data, display, false, packet_type);
+		}
+
+		lua_interface->FindCustomSpellUnlock();
+		return pack;
+	}
+
 	//Spell* spell2= GetSpell(id, (tier +1));
 	AltAdvanceData* data = master_aa_list.GetAltAdvancement(id);
 	if (spell)
@@ -2162,6 +2212,23 @@ EQ2Packet* MasterSpellList::GetAASpellPacket(int32 id, int8 tier, Client* client
 
 EQ2Packet* MasterSpellList::GetSpecialSpellPacket(int32 id, int8 tier, Client* client, bool display, int8 packet_type){
 	Spell* spell = GetSpell(id, tier);
+
+	// if we can't find it on the master spell list, see if it is a custom spell
+	if (!spell)
+	{
+		lua_interface->FindCustomSpellLock();
+		LuaSpell* tmpSpell = lua_interface->FindCustomSpell(id);
+		EQ2Packet* pack = 0;
+		if (tmpSpell)
+		{
+			spell = tmpSpell->spell;
+			pack = spell->SerializeSpecialSpell(client, display, packet_type, 0x81);
+		}
+
+		lua_interface->FindCustomSpellUnlock();
+		return pack;
+	}
+
 	if(spell)
 		return spell->SerializeSpecialSpell(client, display, packet_type, 0x81);
 	return 0;

+ 9 - 0
EQ2/source/WorldServer/Spells.h

@@ -388,6 +388,14 @@ public:
 	/// <param name='error_value'>Value for the error</param>
 	void AddSpellError(int16 version, int8 error_index, int16 error_value);
 
+	int32 GetNewMaxSpellID() { 
+		int32 id = 0;
+		MMasterSpellList.lock();
+		max_spell_id++;
+		id = max_spell_id;
+		MMasterSpellList.unlock();
+		return id; 
+	}
 private:
 	/// <summary>Helper function that gets the closest version in the spell_errors map that is less then or equal to the given version</summary>
 	/// <param name='version'>Client version</param>
@@ -395,6 +403,7 @@ private:
 	int16 GetClosestVersion(int16 version);
 	// map <version, map<error_index, error_value> >
 	map<int16, map<int8, int16> > spell_errors;
+	int32 max_spell_id;
 };
 #endif
 

+ 38 - 3
EQ2/source/WorldServer/client.cpp

@@ -2504,8 +2504,20 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
 			trait_display = false;
 		}
 
+		// if we can't find it on the master spell list, see if it is a custom spell
+		if (!spell)
+		{
+			lua_interface->FindCustomSpellLock();
+			LuaSpell* tmpSpell = lua_interface->FindCustomSpell(id);
+			if (tmpSpell)
+				spell = tmpSpell->spell;
+			lua_interface->FindCustomSpellUnlock();
+		}
+
 		if (spell && sent_spell_details.count(id) == 0) {
-			sent_spell_details[id] = true;
+			if (!spell->IsCopiedSpell())
+				sent_spell_details[id] = true;
+
 			EQ2Packet* app = spell->SerializeSpell(this, display, trait_display);
 			//DumpPacket(app);
 			QueuePacket(app);
@@ -2620,8 +2632,21 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
 		if (effect) {
 			int8 tier = effect->tier;
 			Spell* spell = master_spell_list.GetSpell(id, tier);
+
+			// if we can't find it on the master spell list, see if it is a custom spell
+			if (!spell)
+			{
+				lua_interface->FindCustomSpellLock();
+				LuaSpell* tmpSpell = lua_interface->FindCustomSpell(id);
+				EQ2Packet* pack = 0;
+				if (tmpSpell)
+					spell = tmpSpell->spell;
+				lua_interface->FindCustomSpellUnlock();
+			}
+
 			if (spell && sent_spell_details.count(id) == 0) {
-				sent_spell_details[id] = true;
+				if (!spell->IsCopiedSpell())
+					sent_spell_details[id] = true;
 				int8 type = 0;
 				if (version <= 283)
 					type = 1;
@@ -2664,6 +2689,15 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
 		if (!spell)
 			spell = master_spell_list.GetSpell(id, 1);
 
+		if (!spell)
+		{
+			lua_interface->FindCustomSpellLock();
+			LuaSpell* tmpSpell = lua_interface->FindCustomSpell(id);
+			if (tmpSpell)
+				spell = tmpSpell->spell;
+			lua_interface->FindCustomSpellUnlock();
+		}
+
 		if (!spell)
 		{
 			LogWrite(WORLD__ERROR, 0, "WORLD", "FAILED Examine Info Request-> Spell ID: %u, tier: %i", id, tier);
@@ -2671,7 +2705,8 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
 		}
 
 		//if (spell && sent_spell_details.count(spell->GetSpellID()) == 0) {
-		sent_spell_details[spell->GetSpellID()] = true;
+		if (!spell->IsCopiedSpell())
+			sent_spell_details[spell->GetSpellID()] = true;
 		//	EQ2Packet* app = spell->SerializeAASpell(this,tier, data, false, GetItemPacketType(GetVersion()), 0x04);
 		EQ2Packet* app = master_spell_list.GetAASpellPacket(id, tier, this, false, 0x4F);//0x45 change version to match client
 		/////////////////////////////////////////GetAASpellPacket(int32 id, int8 tier, Client* client, bool display, int8 packet_type) {