diff --git a/src/ElunaLuaEngine_SC.cpp b/src/ElunaLuaEngine_SC.cpp index 520239f2ae..9016ab89ab 100644 --- a/src/ElunaLuaEngine_SC.cpp +++ b/src/ElunaLuaEngine_SC.cpp @@ -695,7 +695,7 @@ class Eluna_PlayerScript : public PlayerScript void OnSpellCast(Player* player, Spell* spell, bool skipCheck) override { - sEluna->OnSpellCast(player, spell, skipCheck); + sEluna->OnPlayerSpellCast(player, spell, skipCheck); } void OnLogin(Player* player) override @@ -855,6 +855,21 @@ class Eluna_SpellSC : public SpellSC { sEluna->OnDummyEffect(caster, spellID, effIndex, itemTarget); } + + void OnSpellCastCancel(Spell* spell, Unit* caster, SpellInfo const* spellInfo, bool bySelf) override + { + sEluna->OnSpellCastCancel(caster, spell, spellInfo, bySelf); + } + + void OnSpellCast(Spell* spell, Unit* caster, SpellInfo const* spellInfo, bool skipCheck) override + { + sEluna->OnSpellCast(caster, spell, spellInfo, skipCheck); + } + + void OnSpellPrepare(Spell* spell, Unit* caster, SpellInfo const* spellInfo) override + { + sEluna->OnSpellPrepare(caster, spell, spellInfo); + } }; class Eluna_UnitScript : public UnitScript diff --git a/src/LuaEngine/Hooks.h b/src/LuaEngine/Hooks.h index a5fd28ba16..e4a7175c22 100644 --- a/src/LuaEngine/Hooks.h +++ b/src/LuaEngine/Hooks.h @@ -86,6 +86,7 @@ namespace Hooks REGTYPE_BG, REGTYPE_MAP, REGTYPE_INSTANCE, + REGTYPE_SPELL, REGTYPE_COUNT }; @@ -365,6 +366,14 @@ namespace Hooks INSTANCE_EVENT_ON_CHECK_ENCOUNTER_IN_PROGRESS = 7, // (event, instance_data, map) INSTANCE_EVENT_COUNT }; + + enum SpellEvents + { + SPELL_EVENT_ON_PREPARE = 1, // (caster, spell, spellInfo) + SPELL_EVENT_ON_CAST = 2, // (caster, spell, spellInfo, skipCheck) + SPELL_EVENT_ON_CAST_CANCEL = 3, // (caster, spell, spellInfo, bySelf) + SPELL_EVENT_COUNT + }; }; #endif // _HOOKS_H diff --git a/src/LuaEngine/LuaEngine.cpp b/src/LuaEngine/LuaEngine.cpp index db1c72047d..f23d0cc305 100644 --- a/src/LuaEngine/LuaEngine.cpp +++ b/src/LuaEngine/LuaEngine.cpp @@ -185,6 +185,7 @@ ItemGossipBindings(NULL), PlayerGossipBindings(NULL), MapEventBindings(NULL), InstanceEventBindings(NULL), +SpellEventBindings(NULL), CreatureUniqueBindings(NULL) { @@ -283,6 +284,7 @@ void Eluna::CreateBindStores() PlayerGossipBindings = new BindingMap< EntryKey >(L); MapEventBindings = new BindingMap< EntryKey >(L); InstanceEventBindings = new BindingMap< EntryKey >(L); + SpellEventBindings = new BindingMap< EntryKey >(L); CreatureUniqueBindings = new BindingMap< UniqueObjectKey >(L); } @@ -306,6 +308,7 @@ void Eluna::DestroyBindStores() delete BGEventBindings; delete MapEventBindings; delete InstanceEventBindings; + delete SpellEventBindings; delete CreatureUniqueBindings; @@ -326,6 +329,7 @@ void Eluna::DestroyBindStores() BGEventBindings = NULL; MapEventBindings = NULL; InstanceEventBindings = NULL; + SpellEventBindings = NULL; CreatureUniqueBindings = NULL; } @@ -1195,6 +1199,22 @@ int Eluna::Register(lua_State* L, uint8 regtype, uint32 entry, ObjectGuid guid, return 1; // Stack: callback } break; + case Hooks::REGTYPE_SPELL: + if (event_id < Hooks::SPELL_EVENT_COUNT) + { + if (!sSpellMgr->GetSpellInfo(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a spell with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + + auto key = EntryKey((Hooks::SpellEvents)event_id, entry); + bindingID = SpellEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, SpellEventBindings); + return 1; // Stack: callback + } + break; } luaL_unref(L, LUA_REGISTRYINDEX, functionRef); std::ostringstream oss; diff --git a/src/LuaEngine/LuaEngine.h b/src/LuaEngine/LuaEngine.h index af0a5d50e1..2f37a4a516 100644 --- a/src/LuaEngine/LuaEngine.h +++ b/src/LuaEngine/LuaEngine.h @@ -261,6 +261,7 @@ class ELUNA_GAME_API Eluna BindingMap< EntryKey >* PlayerGossipBindings; BindingMap< EntryKey >* MapEventBindings; BindingMap< EntryKey >* InstanceEventBindings; + BindingMap< EntryKey >* SpellEventBindings; BindingMap< UniqueObjectKey >* CreatureUniqueBindings; @@ -470,7 +471,7 @@ class ELUNA_GAME_API Eluna bool OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Player* pReceiver); void OnEmote(Player* pPlayer, uint32 emote); void OnTextEmote(Player* pPlayer, uint32 textEmote, uint32 emoteNum, ObjectGuid guid); - void OnSpellCast(Player* pPlayer, Spell* pSpell, bool skipCheck); + void OnPlayerSpellCast(Player* pPlayer, Spell* pSpell, bool skipCheck); void OnLogin(Player* pPlayer); void OnLogout(Player* pPlayer); void OnCreate(Player* pPlayer); @@ -581,6 +582,11 @@ class ELUNA_GAME_API Eluna #endif void OnBGCreate(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId); void OnBGDestroy(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId); + + /* Spell */ + void OnSpellPrepare(Unit* caster, Spell* spell, SpellInfo const* spellInfo); + void OnSpellCast(Unit* caster, Spell* spell, SpellInfo const* spellInfo, bool skipCheck); + void OnSpellCastCancel(Unit* caster, Spell* spell, SpellInfo const* spellInfo, bool bySelf); }; template<> Unit* Eluna::CHECKOBJ(lua_State* L, int narg, bool error); template<> Object* Eluna::CHECKOBJ(lua_State* L, int narg, bool error); diff --git a/src/LuaEngine/LuaFunctions.cpp b/src/LuaEngine/LuaFunctions.cpp index a4c75b0779..2b7a9d6182 100644 --- a/src/LuaEngine/LuaFunctions.cpp +++ b/src/LuaEngine/LuaFunctions.cpp @@ -60,6 +60,7 @@ luaL_Reg GlobalMethods[] = { "RegisterBGEvent", &LuaGlobalFunctions::RegisterBGEvent }, { "RegisterMapEvent", &LuaGlobalFunctions::RegisterMapEvent }, { "RegisterInstanceEvent", &LuaGlobalFunctions::RegisterInstanceEvent }, + { "RegisterSpellEvent", &LuaGlobalFunctions::RegisterSpellEvent }, { "ClearBattleGroundEvents", &LuaGlobalFunctions::ClearBattleGroundEvents }, { "ClearCreatureEvents", &LuaGlobalFunctions::ClearCreatureEvents }, @@ -77,6 +78,7 @@ luaL_Reg GlobalMethods[] = { "ClearServerEvents", &LuaGlobalFunctions::ClearServerEvents }, { "ClearMapEvents", &LuaGlobalFunctions::ClearMapEvents }, { "ClearInstanceEvents", &LuaGlobalFunctions::ClearInstanceEvents }, + { "ClearSpellEvents", &LuaGlobalFunctions::ClearSpellEvents }, // Getters { "GetLuaEngine", &LuaGlobalFunctions::GetLuaEngine }, diff --git a/src/LuaEngine/hooks/PlayerHooks.cpp b/src/LuaEngine/hooks/PlayerHooks.cpp index e437a2a856..62a5d63159 100644 --- a/src/LuaEngine/hooks/PlayerHooks.cpp +++ b/src/LuaEngine/hooks/PlayerHooks.cpp @@ -328,7 +328,7 @@ void Eluna::OnTextEmote(Player* pPlayer, uint32 textEmote, uint32 emoteNum, Obje CallAllFunctions(PlayerEventBindings, key); } -void Eluna::OnSpellCast(Player* pPlayer, Spell* pSpell, bool skipCheck) +void Eluna::OnPlayerSpellCast(Player* pPlayer, Spell* pSpell, bool skipCheck) { START_HOOK(PLAYER_EVENT_ON_SPELL_CAST); Push(pPlayer); diff --git a/src/LuaEngine/hooks/SpellHooks.cpp b/src/LuaEngine/hooks/SpellHooks.cpp new file mode 100644 index 0000000000..cb65107bf1 --- /dev/null +++ b/src/LuaEngine/hooks/SpellHooks.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 - 2016 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaIncludes.h" +#include "ElunaTemplate.h" + +using namespace Hooks; + +#define START_HOOK(EVENT, ENTRY) \ + if (!IsEnabled())\ + return;\ + auto key = EntryKey(EVENT, ENTRY);\ + if (!SpellEventBindings->HasBindingsFor(key))\ + return;\ + LOCK_ELUNA + +#define START_HOOK_WITH_RETVAL(EVENT, ENTRY, RETVAL) \ + if (!IsEnabled())\ + return RETVAL;\ + auto key = EntryKey(EVENT, ENTRY);\ + if (!SpellEventBindings->HasBindingsFor(key))\ + return RETVAL;\ + LOCK_ELUNA + +void Eluna::OnSpellCastCancel(Unit* caster, Spell* spell, SpellInfo const* spellInfo, bool bySelf) +{ + START_HOOK(SPELL_EVENT_ON_CAST_CANCEL, spellInfo->Id); + Push(caster); + Push(spell); + Push(spellInfo); + Push(bySelf); + + CallAllFunctions(SpellEventBindings, key); +} + +void Eluna::OnSpellCast(Unit* caster, Spell* spell, SpellInfo const* spellInfo, bool skipCheck) +{ + START_HOOK(SPELL_EVENT_ON_CAST, spellInfo->Id); + Push(caster); + Push(spell); + Push(spellInfo); + Push(skipCheck); + + CallAllFunctions(SpellEventBindings, key); +} + +void Eluna::OnSpellPrepare(Unit* caster, Spell* spell, SpellInfo const* spellInfo) +{ + START_HOOK(SPELL_EVENT_ON_PREPARE, spellInfo->Id); + Push(caster); + Push(spell); + Push(spellInfo); + + CallAllFunctions(SpellEventBindings, key); +} \ No newline at end of file diff --git a/src/LuaEngine/methods/GlobalMethods.h b/src/LuaEngine/methods/GlobalMethods.h index 33d7d5b1e4..5a0dc9934c 100644 --- a/src/LuaEngine/methods/GlobalMethods.h +++ b/src/LuaEngine/methods/GlobalMethods.h @@ -1267,6 +1267,29 @@ namespace LuaGlobalFunctions return RegisterEntryHelper(L, Hooks::REGTYPE_GAMEOBJECT); } + /** + * Registers a [Spell] event handler. + * + *
+     * enum SpellEvents
+     * {
+     *     SPELL_EVENT_ON_PREPARE                          = 1, // (caster, spell, spellInfo)
+     *     SPELL_EVENT_ON_CAST                             = 2, // (caster, spell, spellInfo, skipCheck)
+     *     SPELL_EVENT_ON_CAST_CANCEL                      = 3, // (caster, spell, spellInfo, bySelf)
+     *     SPELL_EVENT_COUNT
+     * };
+     * 
+ * + * @param uint32 entry : [Spell] entry Id + * @param uint32 event : event ID, refer to SpellEvents above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + */ + int RegisterSpellEvent(lua_State* L) + { + return RegisterEntryHelper(L, Hooks::REGTYPE_SPELL); + } + /** * Reloads the Lua engine. */ @@ -3479,6 +3502,40 @@ namespace LuaGlobalFunctions return 0; } + /** + * Unbinds event handlers for either all of a [Spell]'s events, or one type of event. + * + * If `event_type` is `nil`, all the [Spell]'s event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * + * @proto (entry) + * @proto (entry, event_type) + * @param uint32 entry : the ID of a [Spell]s + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterSpellEvent] + */ + int ClearSpellEvents(lua_State* L) + { + typedef EntryKey Key; + + if (lua_isnoneornil(L, 2)) + { + uint32 entry = Eluna::CHECKVAL(L, 1); + + Eluna* E = Eluna::GetEluna(L); + for (uint32 i = 1; i < Hooks::SPELL_EVENT_COUNT; ++i) + E->SpellEventBindings->Clear(Key((Hooks::SpellEvents)i, entry)); + } + else + { + uint32 entry = Eluna::CHECKVAL(L, 1); + uint32 event_type = Eluna::CHECKVAL(L, 2); + Eluna::GetEluna(L)->SpellEventBindings->Clear(Key((Hooks::SpellEvents)event_type, entry)); + } + return 0; + } + #ifdef AZEROTHCORE /** * Gets the faction which is the current owner of Halaa in Nagrand