From 313596f86e8baeca9620afbcada631f757701bfc Mon Sep 17 00:00:00 2001 From: iThorgrim <125808072+iThorgrim@users.noreply.github.com> Date: Wed, 17 Sep 2025 11:54:52 +0200 Subject: [PATCH] Feat(AllCreatureEvent): Add RegisterAllCreatureEvent and support for CreatureTemplate object. (#312) --- src/ElunaLuaEngine_SC.cpp | 12 ++++ src/LuaEngine/Hooks.h | 10 +++ src/LuaEngine/LuaEngine.cpp | 23 +++++++ src/LuaEngine/LuaEngine.h | 47 ++++++++------ src/LuaEngine/LuaFunctions.cpp | 7 ++- src/LuaEngine/hooks/AllCreatureHooks.cpp | 78 ++++++++++++++++++++++++ src/LuaEngine/methods/GlobalMethods.h | 50 +++++++++++++++ 7 files changed, 207 insertions(+), 20 deletions(-) create mode 100644 src/LuaEngine/hooks/AllCreatureHooks.cpp diff --git a/src/ElunaLuaEngine_SC.cpp b/src/ElunaLuaEngine_SC.cpp index a3329c7..bdf2dcd 100644 --- a/src/ElunaLuaEngine_SC.cpp +++ b/src/ElunaLuaEngine_SC.cpp @@ -57,6 +57,7 @@ public: void OnCreatureAddWorld(Creature* creature) override { sEluna->OnAddToWorld(creature); + sEluna->OnAllCreatureAddToWorld(creature); if (creature->IsGuardian() && creature->ToTempSummon() && creature->ToTempSummon()->GetSummonerGUID().IsPlayer()) sEluna->OnPetAddedToWorld(creature->ToTempSummon()->GetSummonerUnit()->ToPlayer(), creature); @@ -65,6 +66,7 @@ public: void OnCreatureRemoveWorld(Creature* creature) override { sEluna->OnRemoveFromWorld(creature); + sEluna->OnAllCreatureRemoveFromWorld(creature); } bool CanCreatureQuestAccept(Player* player, Creature* creature, Quest const* quest) override @@ -92,6 +94,16 @@ public: return nullptr; } + + void OnCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature) override + { + sEluna->OnAllCreatureSelectLevel(cinfo, creature); + } + + void OnBeforeCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature, uint8& level) override + { + sEluna->OnAllCreatureBeforeSelectLevel(cinfo, creature, level); + } }; class Eluna_AllGameObjectScript : public AllGameObjectScript diff --git a/src/LuaEngine/Hooks.h b/src/LuaEngine/Hooks.h index 553c495..a2f1b9e 100644 --- a/src/LuaEngine/Hooks.h +++ b/src/LuaEngine/Hooks.h @@ -88,6 +88,7 @@ namespace Hooks REGTYPE_INSTANCE, REGTYPE_TICKET, REGTYPE_SPELL, + REGTYPE_ALL_CREATURE, REGTYPE_COUNT }; @@ -398,6 +399,15 @@ namespace Hooks SPELL_EVENT_ON_CAST_CANCEL = 3, // (event, caster, spell, bySelf) SPELL_EVENT_COUNT }; + + enum AllCreatureEvents + { + ALL_CREATURE_EVENT_ON_ADD = 1, // (event, creature) + ALL_CREATURE_EVENT_ON_REMOVE = 2, // (event, creature) + ALL_CREATURE_EVENT_ON_SELECT_LEVEL = 3, // (event, creature_template, creature) + ALL_CREATURE_EVENT_ON_BEFORE_SELECT_LEVEL = 4, // (event, creature_template, creature, level) - Can return the new level + ALL_CREATURE_EVENT_COUNT + }; }; #endif // _HOOKS_H diff --git a/src/LuaEngine/LuaEngine.cpp b/src/LuaEngine/LuaEngine.cpp index c6a2b37..3b14538 100644 --- a/src/LuaEngine/LuaEngine.cpp +++ b/src/LuaEngine/LuaEngine.cpp @@ -194,6 +194,7 @@ GuildEventBindings(NULL), GroupEventBindings(NULL), VehicleEventBindings(NULL), BGEventBindings(NULL), +AllCreatureEventBindings(NULL), PacketEventBindings(NULL), CreatureEventBindings(NULL), @@ -298,6 +299,7 @@ void Eluna::CreateBindStores() VehicleEventBindings = new BindingMap< EventKey >(L); BGEventBindings = new BindingMap< EventKey >(L); TicketEventBindings = new BindingMap< EventKey >(L); + AllCreatureEventBindings = new BindingMap< EventKey >(L); PacketEventBindings = new BindingMap< EntryKey >(L); CreatureEventBindings = new BindingMap< EntryKey >(L); @@ -321,6 +323,7 @@ void Eluna::DestroyBindStores() delete GuildEventBindings; delete GroupEventBindings; delete VehicleEventBindings; + delete AllCreatureEventBindings; delete PacketEventBindings; delete CreatureEventBindings; @@ -342,6 +345,7 @@ void Eluna::DestroyBindStores() GuildEventBindings = NULL; GroupEventBindings = NULL; VehicleEventBindings = NULL; + AllCreatureEventBindings = NULL; PacketEventBindings = NULL; CreatureEventBindings = NULL; @@ -1034,6 +1038,11 @@ void Eluna::Push(lua_State* luastate, SpellEntry const& spell) Push(luastate, &spell); } +void Eluna::Push(lua_State* luastate, CreatureTemplate const* creatureTemplate) +{ + Push(luastate, creatureTemplate); +} + std::string Eluna::FormatQuery(lua_State* L, const char* query) { int numArgs = lua_gettop(L); @@ -1476,6 +1485,7 @@ int Eluna::Register(lua_State* L, uint8 regtype, uint32 entry, ObjectGuid guid, return 1; // Stack: callback } break; + case Hooks::REGTYPE_MAP: if (event_id < Hooks::INSTANCE_EVENT_COUNT) { @@ -1485,6 +1495,7 @@ int Eluna::Register(lua_State* L, uint8 regtype, uint32 entry, ObjectGuid guid, return 1; // Stack: callback } break; + case Hooks::REGTYPE_INSTANCE: if (event_id < Hooks::INSTANCE_EVENT_COUNT) { @@ -1494,6 +1505,7 @@ int Eluna::Register(lua_State* L, uint8 regtype, uint32 entry, ObjectGuid guid, return 1; // Stack: callback } break; + case Hooks::REGTYPE_TICKET: if (event_id < Hooks::TICKET_EVENT_COUNT) { @@ -1503,6 +1515,7 @@ 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) { @@ -1519,6 +1532,16 @@ int Eluna::Register(lua_State* L, uint8 regtype, uint32 entry, ObjectGuid guid, return 1; // Stack: callback } break; + + case Hooks::REGTYPE_ALL_CREATURE: + if (event_id < Hooks::ALL_CREATURE_EVENT_COUNT) + { + auto key = EventKey((Hooks::AllCreatureEvents)event_id); + bindingID = AllCreatureEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, AllCreatureEventBindings); + 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 4f38563..e904760 100644 --- a/src/LuaEngine/LuaEngine.h +++ b/src/LuaEngine/LuaEngine.h @@ -226,6 +226,7 @@ private: void Push(const std::string& value) { Push(L, value); ++push_counter; } void Push(const char* value) { Push(L, value); ++push_counter; } void Push(ObjectGuid const value) { Push(L, value); ++push_counter; } + void Push(const CreatureTemplate* value) { Push(L, value); ++push_counter; } template void Push(T const* ptr) { Push(L, ptr); ++push_counter; } @@ -238,25 +239,26 @@ public: QueryCallbackProcessor queryProcessor; EventEmitter OnError; - BindingMap< EventKey >* ServerEventBindings; - BindingMap< EventKey >* PlayerEventBindings; - BindingMap< EventKey >* GuildEventBindings; - BindingMap< EventKey >* GroupEventBindings; - BindingMap< EventKey >* VehicleEventBindings; - BindingMap< EventKey >* BGEventBindings; + BindingMap< EventKey >* ServerEventBindings; + BindingMap< EventKey >* PlayerEventBindings; + BindingMap< EventKey >* GuildEventBindings; + BindingMap< EventKey >* GroupEventBindings; + BindingMap< EventKey >* VehicleEventBindings; + BindingMap< EventKey >* BGEventBindings; + BindingMap< EventKey >* AllCreatureEventBindings; - BindingMap< EntryKey >* PacketEventBindings; - BindingMap< EntryKey >* CreatureEventBindings; - BindingMap< EntryKey >* CreatureGossipBindings; - BindingMap< EntryKey >* GameObjectEventBindings; - BindingMap< EntryKey >* GameObjectGossipBindings; - BindingMap< EntryKey >* ItemEventBindings; - BindingMap< EntryKey >* ItemGossipBindings; - BindingMap< EntryKey >* PlayerGossipBindings; - BindingMap< EntryKey >* MapEventBindings; - BindingMap< EntryKey >* InstanceEventBindings; - BindingMap< EventKey >* TicketEventBindings; - BindingMap< EntryKey >* SpellEventBindings; + BindingMap< EntryKey >* PacketEventBindings; + BindingMap< EntryKey >* CreatureEventBindings; + BindingMap< EntryKey >* CreatureGossipBindings; + BindingMap< EntryKey >* GameObjectEventBindings; + BindingMap< EntryKey >* GameObjectGossipBindings; + BindingMap< EntryKey >* ItemEventBindings; + BindingMap< EntryKey >* ItemGossipBindings; + BindingMap< EntryKey >* PlayerGossipBindings; + BindingMap< EntryKey >* MapEventBindings; + BindingMap< EntryKey >* InstanceEventBindings; + BindingMap< EventKey >* TicketEventBindings; + BindingMap< EntryKey >* SpellEventBindings; BindingMap< UniqueObjectKey >* CreatureUniqueBindings; @@ -299,12 +301,13 @@ public: static void Push(lua_State* luastate, ObjectGuid const guid); static void Push(lua_State* luastate, GemPropertiesEntry const& gemProperties); static void Push(lua_State* luastate, SpellEntry const& spell); + static void Push(lua_State* luastate, CreatureTemplate const* creatureTemplate); template static void Push(lua_State* luastate, T const* ptr) { ElunaTemplate::Push(luastate, ptr); } - + static std::string FormatQuery(lua_State* L, const char* query); bool ExecuteCall(int params, int res); @@ -588,6 +591,12 @@ public: 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); + + /* AllCreature */ + void OnAllCreatureAddToWorld(Creature* creature); + void OnAllCreatureRemoveFromWorld(Creature* creature); + void OnAllCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature); + void OnAllCreatureBeforeSelectLevel(const CreatureTemplate* cinfo, Creature* creature, uint8& level); }; 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 d10201d..b37bc91 100644 --- a/src/LuaEngine/LuaFunctions.cpp +++ b/src/LuaEngine/LuaFunctions.cpp @@ -70,6 +70,8 @@ luaL_Reg GlobalMethods[] = { "RegisterInstanceEvent", &LuaGlobalFunctions::RegisterInstanceEvent }, { "RegisterTicketEvent", &LuaGlobalFunctions::RegisterTicketEvent }, { "RegisterSpellEvent", &LuaGlobalFunctions::RegisterSpellEvent }, + { "RegisterAllCreatureEvent", &LuaGlobalFunctions::RegisterAllCreatureEvent }, + { "ClearBattleGroundEvents", &LuaGlobalFunctions::ClearBattleGroundEvents }, { "ClearCreatureEvents", &LuaGlobalFunctions::ClearCreatureEvents }, @@ -89,6 +91,7 @@ luaL_Reg GlobalMethods[] = { "ClearInstanceEvents", &LuaGlobalFunctions::ClearInstanceEvents }, { "ClearTicketEvents", &LuaGlobalFunctions::ClearTicketEvents }, { "ClearSpellEvents", &LuaGlobalFunctions::ClearSpellEvents }, + { "ClearAllCreatureEvents", &LuaGlobalFunctions::ClearAllCreatureEvents }, // Getters { "GetLuaEngine", &LuaGlobalFunctions::GetLuaEngine }, @@ -1931,7 +1934,7 @@ void RegisterFunctions(Eluna* E) ElunaTemplate::Register(E, "Ticket"); ElunaTemplate::SetMethods(E, TicketMethods); - + ElunaTemplate::Register(E, "SpellInfo"); ElunaTemplate::SetMethods(E, SpellInfoMethods); @@ -1941,6 +1944,8 @@ void RegisterFunctions(Eluna* E) ElunaTemplate::Register(E, "SpellEntry"); ElunaTemplate::SetMethods(E, SpellEntryMethods); + ElunaTemplate::Register(E, "CreatureTemplate"); + ElunaTemplate::Register(E, "long long", true); ElunaTemplate::Register(E, "unsigned long long", true); diff --git a/src/LuaEngine/hooks/AllCreatureHooks.cpp b/src/LuaEngine/hooks/AllCreatureHooks.cpp new file mode 100644 index 0000000..4f75197 --- /dev/null +++ b/src/LuaEngine/hooks/AllCreatureHooks.cpp @@ -0,0 +1,78 @@ +/* + * 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) \ + if (!ElunaConfig::GetInstance().IsElunaEnabled())\ + return;\ + auto key = EventKey(EVENT);\ + if (!AllCreatureEventBindings->HasBindingsFor(key))\ + return;\ + LOCK_ELUNA + +#define START_HOOK_WITH_RETVAL(EVENT, RETVAL) \ + if (!ElunaConfig::GetInstance().IsElunaEnabled())\ + return RETVAL;\ + auto key = EventKey(EVENT);\ + if (!AllCreatureEventBindings->HasBindingsFor(key))\ + return RETVAL;\ + LOCK_ELUNA + +void Eluna::OnAllCreatureAddToWorld(Creature* creature) +{ + START_HOOK(ALL_CREATURE_EVENT_ON_ADD); + Push(creature); + CallAllFunctions(AllCreatureEventBindings, key); +} + +void Eluna::OnAllCreatureRemoveFromWorld(Creature* creature) +{ + START_HOOK(ALL_CREATURE_EVENT_ON_REMOVE); + Push(creature); + CallAllFunctions(AllCreatureEventBindings, key); +} + +void Eluna::OnAllCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature) +{ + START_HOOK(ALL_CREATURE_EVENT_ON_SELECT_LEVEL); + Push(cinfo); + Push(creature); + CallAllFunctions(AllCreatureEventBindings, key); +} + +void Eluna::OnAllCreatureBeforeSelectLevel(const CreatureTemplate* cinfo, Creature* creature, uint8& level) +{ + START_HOOK(ALL_CREATURE_EVENT_ON_BEFORE_SELECT_LEVEL); + Push(cinfo); + Push(creature); + Push(level); + int levelIndex = lua_gettop(L); + int n = SetupStack(AllCreatureEventBindings, key, 3); + + while (n > 0) + { + int r = CallOneFunction(n--, 3, 1); + + if (lua_isnumber(L, r)) + { + level = CHECKVAL(L, r); + // Update the stack for subsequent calls. + ReplaceArgument(level, levelIndex); + } + + lua_pop(L, 1); + } + + CleanUpStack(3); +} \ No newline at end of file diff --git a/src/LuaEngine/methods/GlobalMethods.h b/src/LuaEngine/methods/GlobalMethods.h index ab14865..462b241 100644 --- a/src/LuaEngine/methods/GlobalMethods.h +++ b/src/LuaEngine/methods/GlobalMethods.h @@ -1336,6 +1336,29 @@ namespace LuaGlobalFunctions return RegisterEntryHelper(L, Hooks::REGTYPE_SPELL); } + /** + * Registers a [Creature] event handler. It used AllCreatureScript so this don't need creature entry as a key. + * + *
+     * enum AllCreatureEvents
+     * {
+     *     ALL_CREATURE_EVENT_ON_ADD                       = 1, // (event, creature)
+     *     ALL_CREATURE_EVENT_ON_REMOVE                    = 2, // (event, creature)
+     *     ALL_CREATURE_EVENT_ON_SELECT_LEVEL              = 3, // (event, creature_template, creature)
+     *     ALL_CREATURE_EVENT_ON_BEFORE_SELECT_LEVEL       = 4, // (event, creature_template, creature, level) - Can return the new level
+     *     ALL_CREATURE_EVENT_COUNT
+     * };
+     * 
+ * + * @param uint32 event : event ID, refer to AllCreatureEvents 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 RegisterAllCreatureEvent(lua_State* L) + { + return RegisterEventHelper(L, Hooks::REGTYPE_ALL_CREATURE); + } + /** * Reloads the Lua engine. */ @@ -3302,6 +3325,33 @@ namespace LuaGlobalFunctions return 0; } + /** + * Unbinds event handlers for either all [Creature] events, or one type of [Creature] event. + * + * If `event_type` is `nil`, all [Creature] event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * @proto () + * @proto (event_type) + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterAllCreatureEvent] + */ + int ClearAllCreatureEvents(lua_State* L) + { + typedef EventKey Key; + + if (lua_isnoneornil(L, 1)) + { + Eluna::GetEluna(L)->AllCreatureEventBindings->Clear(); + } + else + { + uint32 event_type = Eluna::CHECKVAL(L, 1); + Eluna::GetEluna(L)->AllCreatureEventBindings->Clear(Key((Hooks::AllCreatureEvents)event_type)); + } + return 0; + } + /** * Gets the faction which is the current owner of Halaa in Nagrand * 0 = Alliance