From 9a78416ac6d375c022f0bf7a8a3b4104577dfdc8 Mon Sep 17 00:00:00 2001 From: image <> Date: Wed, 23 Sep 2020 08:49:36 -0400 Subject: [PATCH] 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 --- EQ2/source/WorldServer/LuaFunctions.cpp | 81 +++++++++++++++++++++++++ EQ2/source/WorldServer/LuaFunctions.h | 3 + EQ2/source/WorldServer/LuaInterface.cpp | 50 +++++++++++++++ EQ2/source/WorldServer/LuaInterface.h | 13 ++++ EQ2/source/WorldServer/SpellProcess.cpp | 3 + EQ2/source/WorldServer/Spells.cpp | 71 +++++++++++++++++++++- EQ2/source/WorldServer/Spells.h | 9 +++ EQ2/source/WorldServer/client.cpp | 41 ++++++++++++- 8 files changed, 266 insertions(+), 5 deletions(-) diff --git a/EQ2/source/WorldServer/LuaFunctions.cpp b/EQ2/source/WorldServer/LuaFunctions.cpp index d3b9a7ff5..c6a271e2f 100644 --- a/EQ2/source/WorldServer/LuaFunctions.cpp +++ b/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; diff --git a/EQ2/source/WorldServer/LuaFunctions.h b/EQ2/source/WorldServer/LuaFunctions.h index 6cc7e9c79..7506f4edc 100644 --- a/EQ2/source/WorldServer/LuaFunctions.h +++ b/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 \ No newline at end of file diff --git a/EQ2/source/WorldServer/LuaInterface.cpp b/EQ2/source/WorldServer/LuaInterface.cpp index d074c6124..a630d076c 100644 --- a/EQ2/source/WorldServer/LuaInterface.cpp +++ b/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; diff --git a/EQ2/source/WorldServer/LuaInterface.h b/EQ2/source/WorldServer/LuaInterface.h index ca0533918..d75dfa811 100644 --- a/EQ2/source/WorldServer/LuaInterface.h +++ b/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 diff --git a/EQ2/source/WorldServer/SpellProcess.cpp b/EQ2/source/WorldServer/SpellProcess.cpp index 64dc5673c..77f659b9a 100644 --- a/EQ2/source/WorldServer/SpellProcess.cpp +++ b/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); } \ No newline at end of file diff --git a/EQ2/source/WorldServer/Spells.cpp b/EQ2/source/WorldServer/Spells.cpp index 73eba8709..e971e907a 100644 --- a/EQ2/source/WorldServer/Spells.cpp +++ b/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; diff --git a/EQ2/source/WorldServer/Spells.h b/EQ2/source/WorldServer/Spells.h index 0653b4e08..81b530402 100644 --- a/EQ2/source/WorldServer/Spells.h +++ b/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 diff --git a/EQ2/source/WorldServer/client.cpp b/EQ2/source/WorldServer/client.cpp index 528fae908..9866b7c20 100644 --- a/EQ2/source/WorldServer/client.cpp +++ b/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) {