aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/updates/world/master/2023_07_09_01_world.sql6
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp16
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp106
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.h5
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.cpp12
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.h28
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp2
-rw-r--r--src/server/game/Events/GameEventSender.cpp5
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp69
-rw-r--r--src/server/game/Globals/ObjectMgr.h13
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp28
-rw-r--r--src/server/game/Scripting/ScriptMgr.h18
12 files changed, 244 insertions, 64 deletions
diff --git a/sql/updates/world/master/2023_07_09_01_world.sql b/sql/updates/world/master/2023_07_09_01_world.sql
new file mode 100644
index 00000000000..1107b0db29b
--- /dev/null
+++ b/sql/updates/world/master/2023_07_09_01_world.sql
@@ -0,0 +1,6 @@
+DROP TABLE IF EXISTS `event_script_names`;
+CREATE TABLE `event_script_names` (
+ `Id` int unsigned NOT NULL,
+ `ScriptName` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`Id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 372ba77f711..3918f415f9d 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -1242,10 +1242,26 @@ public:
}
};
+class SmartEventTrigger : public EventScript
+{
+public:
+ SmartEventTrigger() : EventScript("SmartEventTrigger") { }
+
+ void OnTrigger(WorldObject* object, WorldObject* invoker, uint32 eventId) override
+ {
+ TC_LOG_DEBUG("scripts.ai", "Event {} is using SmartEventTrigger script", eventId);
+ SmartScript script;
+ // Set invoker as BaseObject if there isn't target for GameEvents::Trigger
+ script.OnInitialize(Coalesce<WorldObject>(object, invoker), nullptr, nullptr, nullptr, eventId);
+ script.ProcessEventsFor(SMART_EVENT_SEND_EVENT_TRIGGER, invoker->ToUnit(), 0, 0, false, nullptr, invoker->ToGameObject());
+ }
+};
+
void AddSC_SmartScripts()
{
new SmartTrigger();
new SmartAreaTriggerEntityScript();
new SmartScene();
new SmartQuest();
+ new SmartEventTrigger();
}
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index 47c5dd279fd..b27e0847c3f 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -54,6 +54,7 @@ SmartScript::SmartScript()
areaTrigger = nullptr;
sceneTemplate = nullptr;
quest = nullptr;
+ event = 0;
mEventPhase = 0;
mPathId = 0;
mTextTimer = 0;
@@ -217,6 +218,7 @@ void SmartScript::ResetBaseObject()
me = m;
go = nullptr;
areaTrigger = nullptr;
+ player = nullptr;
}
}
@@ -227,6 +229,7 @@ void SmartScript::ResetBaseObject()
me = nullptr;
go = o;
areaTrigger = nullptr;
+ player = nullptr;
}
}
}
@@ -3169,6 +3172,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
case SMART_EVENT_FOLLOW_COMPLETED:
case SMART_EVENT_ON_SPELLCLICK:
case SMART_EVENT_ON_DESPAWN:
+ case SMART_EVENT_SEND_EVENT_TRIGGER:
ProcessAction(e, unit, var0, var1, bvar, spell, gob);
break;
case SMART_EVENT_GOSSIP_HELLO:
@@ -3910,7 +3914,7 @@ void SmartScript::RetryLater(SmartScriptHolder& e, bool ignoreChanceRoll)
e.runOnce = false;
}
-void SmartScript::FillScript(SmartAIEventList e, WorldObject* obj, AreaTriggerEntry const* at, SceneTemplate const* scene, Quest const* quest)
+void SmartScript::FillScript(SmartAIEventList e, WorldObject* obj, AreaTriggerEntry const* at, SceneTemplate const* scene, Quest const* quest, uint32 event)
{
if (e.empty())
{
@@ -3922,6 +3926,8 @@ void SmartScript::FillScript(SmartAIEventList e, WorldObject* obj, AreaTriggerEn
TC_LOG_DEBUG("scripts.ai", "SmartScript: EventMap for SceneId {} is empty but is using SmartScript.", scene->SceneId);
if (quest)
TC_LOG_DEBUG("scripts.ai", "SmartScript: EventMap for Quest {} is empty but is using SmartScript.", quest->GetQuestId());
+ if (event)
+ TC_LOG_DEBUG("scripts.ai", "SmartScript: EventMap for Event {} is empty but is using SmartScript.", event);
return;
}
for (SmartScriptHolder& scriptholder : e)
@@ -3969,43 +3975,49 @@ void SmartScript::FillScript(SmartAIEventList e, WorldObject* obj, AreaTriggerEn
void SmartScript::GetScript()
{
SmartAIEventList e;
- if (me)
- {
- e = sSmartScriptMgr->GetScript(-((int32)me->GetSpawnId()), mScriptType);
- if (e.empty())
- e = sSmartScriptMgr->GetScript((int32)me->GetEntry(), mScriptType);
- FillScript(std::move(e), me, nullptr, nullptr, nullptr);
- }
- else if (go)
- {
- e = sSmartScriptMgr->GetScript(-((int32)go->GetSpawnId()), mScriptType);
- if (e.empty())
- e = sSmartScriptMgr->GetScript((int32)go->GetEntry(), mScriptType);
- FillScript(std::move(e), go, nullptr, nullptr, nullptr);
- }
- else if (trigger)
- {
- e = sSmartScriptMgr->GetScript((int32)trigger->ID, mScriptType);
- FillScript(std::move(e), nullptr, trigger, nullptr, nullptr);
- }
- else if (areaTrigger)
- {
- e = sSmartScriptMgr->GetScript((int32)areaTrigger->GetEntry(), mScriptType);
- FillScript(std::move(e), areaTrigger, nullptr, nullptr, nullptr);
- }
- else if (sceneTemplate)
- {
- e = sSmartScriptMgr->GetScript(sceneTemplate->SceneId, mScriptType);
- FillScript(std::move(e), nullptr, nullptr, sceneTemplate, nullptr);
- }
- else if (quest)
+
+ // We must use script type to avoid ambiguities
+ switch (mScriptType)
{
- e = sSmartScriptMgr->GetScript(quest->GetQuestId(), mScriptType);
- FillScript(std::move(e), nullptr, nullptr, nullptr, quest);
+ case SMART_SCRIPT_TYPE_CREATURE:
+ e = sSmartScriptMgr->GetScript(-((int32)me->GetSpawnId()), mScriptType);
+ if (e.empty())
+ e = sSmartScriptMgr->GetScript((int32)me->GetEntry(), mScriptType);
+ FillScript(std::move(e), me, nullptr, nullptr, nullptr, 0);
+ break;
+ case SMART_SCRIPT_TYPE_GAMEOBJECT:
+ e = sSmartScriptMgr->GetScript(-((int32)go->GetSpawnId()), mScriptType);
+ if (e.empty())
+ e = sSmartScriptMgr->GetScript((int32)go->GetEntry(), mScriptType);
+ FillScript(std::move(e), go, nullptr, nullptr, nullptr, 0);
+ break;
+ case SMART_SCRIPT_TYPE_AREATRIGGER_ENTITY:
+ case SMART_SCRIPT_TYPE_AREATRIGGER_ENTITY_SERVERSIDE:
+ e = sSmartScriptMgr->GetScript((int32)areaTrigger->GetEntry(), mScriptType);
+ FillScript(std::move(e), areaTrigger, nullptr, nullptr, nullptr, 0);
+ break;
+ case SMART_SCRIPT_TYPE_AREATRIGGER:
+ e = sSmartScriptMgr->GetScript((int32)trigger->ID, mScriptType);
+ FillScript(std::move(e), nullptr, trigger, nullptr, nullptr, 0);
+ break;
+ case SMART_SCRIPT_TYPE_SCENE:
+ e = sSmartScriptMgr->GetScript(sceneTemplate->SceneId, mScriptType);
+ FillScript(std::move(e), nullptr, nullptr, sceneTemplate, nullptr, 0);
+ break;
+ case SMART_SCRIPT_TYPE_QUEST:
+ e = sSmartScriptMgr->GetScript(quest->GetQuestId(), mScriptType);
+ FillScript(std::move(e), nullptr, nullptr, nullptr, quest, 0);
+ break;
+ case SMART_SCRIPT_TYPE_EVENT:
+ e = sSmartScriptMgr->GetScript((int32)event, mScriptType);
+ FillScript(std::move(e), nullptr, nullptr, nullptr, nullptr, event);
+ break;
+ default:
+ break;
}
}
-void SmartScript::OnInitialize(WorldObject* obj, AreaTriggerEntry const* at, SceneTemplate const* scene, Quest const* qst)
+void SmartScript::OnInitialize(WorldObject* obj, AreaTriggerEntry const* at, SceneTemplate const* scene, Quest const* qst, uint32 evnt)
{
if (at)
{
@@ -4049,6 +4061,32 @@ void SmartScript::OnInitialize(WorldObject* obj, AreaTriggerEntry const* at, Sce
TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Quest with id {}, triggered by player {}", qst->GetQuestId(), player->GetGUID().ToString());
}
+ else if (evnt)
+ {
+ mScriptType = SMART_SCRIPT_TYPE_EVENT;
+ event = evnt;
+
+ if (obj->IsPlayer())
+ {
+ player = obj->ToPlayer();
+ TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Event {}, triggered by player {}", event, player->GetGUID().ToString());
+ }
+ else if (obj->IsCreature())
+ {
+ me = obj->ToCreature();
+ TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Event {}, triggered by creature {}", event, me->GetEntry());
+ }
+ else if (obj->IsGameObject())
+ {
+ go = obj->ToGameObject();
+ TC_LOG_DEBUG("scripts.ai", "SmartScript::OnInitialize: source is Event {}, triggered by gameobject {}", event, go->GetEntry());
+ }
+ else
+ {
+ TC_LOG_ERROR("misc", "SmartScript::OnInitialize: source is Event {}, missing trigger WorldObject", event);
+ return;
+ }
+ }
else if (obj) // Handle object based scripts
{
switch (obj->GetTypeId())
diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h
index 7a92199c7a8..6e3068e5f38 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.h
+++ b/src/server/game/AI/SmartScripts/SmartScript.h
@@ -38,9 +38,9 @@ class TC_GAME_API SmartScript
SmartScript();
~SmartScript();
- void OnInitialize(WorldObject* obj, AreaTriggerEntry const* at = nullptr, SceneTemplate const* scene = nullptr, Quest const* qst = nullptr);
+ void OnInitialize(WorldObject* obj, AreaTriggerEntry const* at = nullptr, SceneTemplate const* scene = nullptr, Quest const* qst = nullptr, uint32 evnt = 0);
void GetScript();
- void FillScript(SmartAIEventList e, WorldObject* obj, AreaTriggerEntry const* at, SceneTemplate const* scene, Quest const* quest);
+ void FillScript(SmartAIEventList e, WorldObject* obj, AreaTriggerEntry const* at, SceneTemplate const* scene, Quest const* quest, uint32 event = 0);
void ProcessEventsFor(SMART_EVENT e, Unit* unit = nullptr, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, SpellInfo const* spell = nullptr, GameObject* gob = nullptr, std::string const& varString = "");
void ProcessEvent(SmartScriptHolder& e, Unit* unit = nullptr, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, SpellInfo const* spell = nullptr, GameObject* gob = nullptr, std::string const& varString = "");
@@ -122,6 +122,7 @@ class TC_GAME_API SmartScript
AreaTrigger* areaTrigger;
SceneTemplate const* sceneTemplate;
Quest const* quest;
+ uint32 event;
SmartScriptType mScriptType;
uint32 mEventPhase;
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
index 9ae7999e86f..b20f669bcd8 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -137,6 +137,15 @@ void SmartAIMgr::LoadSmartAIFromDB()
}
break;
}
+ case SMART_SCRIPT_TYPE_EVENT:
+ {
+ if (!sObjectMgr->IsValidEvent((uint32)temp.entryOrGuid))
+ {
+ TC_LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: Event id ({}) does not exist, skipped loading.", uint32(temp.entryOrGuid));
+ continue;
+ }
+ break;
+ }
case SMART_SCRIPT_TYPE_QUEST:
{
if (!sObjectMgr->GetQuestTemplate((uint32)temp.entryOrGuid))
@@ -451,6 +460,7 @@ SmartScriptHolder& SmartAIMgr::FindLinkedEvent(SmartAIEventList& list, uint32 li
case SMART_EVENT_SCENE_TRIGGER:
case SMART_EVENT_SCENE_CANCEL:
case SMART_EVENT_SCENE_COMPLETE:
+ case SMART_EVENT_SEND_EVENT_TRIGGER:
return true;
default:
return false;
@@ -804,6 +814,7 @@ bool SmartAIMgr::CheckUnusedEventParams(SmartScriptHolder const& e)
case SMART_EVENT_ON_SPELL_FAILED: return sizeof(SmartEvent::spellCast);
case SMART_EVENT_ON_SPELL_START: return sizeof(SmartEvent::spellCast);
case SMART_EVENT_ON_DESPAWN: return NO_PARAMS;
+ case SMART_EVENT_SEND_EVENT_TRIGGER: return NO_PARAMS;
default:
TC_LOG_WARN("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} is using an event with no unused params specified in SmartAIMgr::CheckUnusedEventParams(), please report this.",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
@@ -1442,6 +1453,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_EVENT_SCENE_CANCEL:
case SMART_EVENT_SCENE_COMPLETE:
case SMART_EVENT_SCENE_TRIGGER:
+ case SMART_EVENT_SEND_EVENT_TRIGGER:
break;
// Unused
case SMART_EVENT_TARGET_HEALTH_PCT:
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
index 9406fbedb00..966b5fd2b0f 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
@@ -186,8 +186,9 @@ enum SMART_EVENT
SMART_EVENT_ON_SPELL_FAILED = 84, // SpellID, CooldownMin, CooldownMax
SMART_EVENT_ON_SPELL_START = 85, // SpellID, CooldownMin, CooldownMax
SMART_EVENT_ON_DESPAWN = 86, // NONE
+ SMART_EVENT_SEND_EVENT_TRIGGER = 87, // NONE
- SMART_EVENT_END = 87
+ SMART_EVENT_END = 88
};
struct SmartEvent
@@ -1444,18 +1445,18 @@ struct SmartTarget
enum SmartScriptType
{
- SMART_SCRIPT_TYPE_CREATURE = 0, //done
- SMART_SCRIPT_TYPE_GAMEOBJECT = 1, //done
- SMART_SCRIPT_TYPE_AREATRIGGER = 2, //done
- SMART_SCRIPT_TYPE_EVENT = 3, //
- SMART_SCRIPT_TYPE_GOSSIP = 4, //
- SMART_SCRIPT_TYPE_QUEST = 5, //done
- SMART_SCRIPT_TYPE_SPELL = 6, //
- SMART_SCRIPT_TYPE_TRANSPORT = 7, //
- SMART_SCRIPT_TYPE_INSTANCE = 8, //
- SMART_SCRIPT_TYPE_TIMED_ACTIONLIST = 9, //
- SMART_SCRIPT_TYPE_SCENE = 10, //done
- SMART_SCRIPT_TYPE_AREATRIGGER_ENTITY = 11,
+ SMART_SCRIPT_TYPE_CREATURE = 0,
+ SMART_SCRIPT_TYPE_GAMEOBJECT = 1,
+ SMART_SCRIPT_TYPE_AREATRIGGER = 2,
+ SMART_SCRIPT_TYPE_EVENT = 3,
+ SMART_SCRIPT_TYPE_GOSSIP = 4, // NYI
+ SMART_SCRIPT_TYPE_QUEST = 5,
+ SMART_SCRIPT_TYPE_SPELL = 6, // NYI
+ SMART_SCRIPT_TYPE_TRANSPORT = 7, // NYI
+ SMART_SCRIPT_TYPE_INSTANCE = 8, // NYI
+ SMART_SCRIPT_TYPE_TIMED_ACTIONLIST = 9,
+ SMART_SCRIPT_TYPE_SCENE = 10,
+ SMART_SCRIPT_TYPE_AREATRIGGER_ENTITY = 11,
SMART_SCRIPT_TYPE_AREATRIGGER_ENTITY_SERVERSIDE = 12,
SMART_SCRIPT_TYPE_MAX
};
@@ -1582,6 +1583,7 @@ const uint32 SmartAIEventMask[SMART_EVENT_END][2] =
{SMART_EVENT_ON_SPELL_FAILED, SMART_SCRIPT_TYPE_MASK_CREATURE },
{SMART_EVENT_ON_SPELL_START, SMART_SCRIPT_TYPE_MASK_CREATURE },
{SMART_EVENT_ON_DESPAWN, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_SEND_EVENT_TRIGGER, SMART_SCRIPT_TYPE_MASK_EVENT }
};
enum SmartEventFlags
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 72bb7094edc..6a13177d4b5 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -270,7 +270,7 @@ public:
}
}();
if (eventId)
- GameEvents::Trigger(eventId, &_owner, nullptr);
+ GameEvents::Trigger(eventId, &_owner, &_owner);
if (_autoCycleBetweenStopFrames)
{
diff --git a/src/server/game/Events/GameEventSender.cpp b/src/server/game/Events/GameEventSender.cpp
index 038f7d50a4e..da1dd86601e 100644
--- a/src/server/game/Events/GameEventSender.cpp
+++ b/src/server/game/Events/GameEventSender.cpp
@@ -21,6 +21,7 @@
#include "Map.h"
#include "ObjectMgr.h"
#include "Player.h"
+#include "ScriptMgr.h"
#include "Util.h"
#include "ZoneScript.h"
@@ -37,7 +38,8 @@ void GameEvents::Trigger(uint32 gameEventId, WorldObject* source, WorldObject* t
if (zoneScript)
zoneScript->ProcessEvent(target, gameEventId, source);
- Map* map = refForMapAndZoneScript->GetMap();
+ sScriptMgr->OnEventTrigger(target, source, gameEventId);
+
if (GameObject* goTarget = Object::ToGameObject(target))
if (GameObjectAI* goAI = goTarget->AI())
goAI->EventInform(gameEventId);
@@ -45,6 +47,7 @@ void GameEvents::Trigger(uint32 gameEventId, WorldObject* source, WorldObject* t
if (Player* sourcePlayer = Object::ToPlayer(source))
TriggerForPlayer(gameEventId, sourcePlayer);
+ Map* map = refForMapAndZoneScript->GetMap();
TriggerForMap(gameEventId, map, source, target);
}
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 7605cb6b698..c8e548d1a39 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -5832,24 +5832,24 @@ void ObjectMgr::LoadSpellScripts()
}
}
-void ObjectMgr::LoadEventScripts()
+void ObjectMgr::LoadEventSet()
{
- LoadScripts(SCRIPTS_EVENT);
+ _eventStore.clear();
- std::set<uint32> evt_scripts;
- // Load all possible script entries from gameobjects
+ // Load all possible event ids from gameobjects
for (auto const& gameObjectTemplatePair : _gameObjectTemplateStore)
if (uint32 eventId = gameObjectTemplatePair.second.GetEventScriptId())
- evt_scripts.insert(eventId);
+ _eventStore.insert(eventId);
- // Load all possible script entries from spells
+ // Load all possible event ids from spells
for (SpellNameEntry const* spellNameEntry : sSpellNameStore)
if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(spellNameEntry->ID, DIFFICULTY_NONE))
for (SpellEffectInfo const& spellEffectInfo : spell->GetEffects())
if (spellEffectInfo.IsEffect(SPELL_EFFECT_SEND_EVENT))
if (spellEffectInfo.MiscValue)
- evt_scripts.insert(spellEffectInfo.MiscValue);
+ _eventStore.insert(spellEffectInfo.MiscValue);
+ // Load all possible event ids from taxi path nodes
for (size_t path_idx = 0; path_idx < sTaxiPathNodesByPath.size(); ++path_idx)
{
for (size_t node_idx = 0; node_idx < sTaxiPathNodesByPath[path_idx].size(); ++node_idx)
@@ -5857,21 +5857,58 @@ void ObjectMgr::LoadEventScripts()
TaxiPathNodeEntry const* node = sTaxiPathNodesByPath[path_idx][node_idx];
if (node->ArrivalEventID)
- evt_scripts.insert(node->ArrivalEventID);
+ _eventStore.insert(node->ArrivalEventID);
if (node->DepartureEventID)
- evt_scripts.insert(node->DepartureEventID);
+ _eventStore.insert(node->DepartureEventID);
}
}
+}
+
+void ObjectMgr::LoadEventScripts()
+{
+ // Set of valid events referenced in several sources
+ LoadEventSet();
+
+ // Deprecated
+ LoadScripts(SCRIPTS_EVENT);
// Then check if all scripts are in above list of possible script entries
for (ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
{
- std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first);
- if (itr2 == evt_scripts.end())
- TC_LOG_ERROR("sql.sql", "Table `event_scripts` has script (Id: {}) not referring to any gameobject_template type 10 data2 field, type 3 data6 field, type 13 data 2 field or any spell effect {}",
+ if (!IsValidEvent(itr->first))
+ TC_LOG_ERROR("sql.sql", "Table `event_scripts` has script (Id: {}) not referring to any gameobject_template (type 3 data6 field, type 7 data3 field, type 10 data2 field, type 13 data2 field, type 50 data7 field), any taxi path node or any spell effect {}",
itr->first, SPELL_EFFECT_SEND_EVENT);
}
+
+ uint32 oldMSTime = getMSTime();
+
+ _eventScriptStore.clear(); // Reload case
+
+ QueryResult result = WorldDatabase.Query("SELECT Id, ScriptName FROM event_script_names");
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 event scripts. DB table `event_script_names` is empty.");
+ return;
+ }
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 eventId = fields[0].GetUInt32();
+ std::string const scriptName = fields[1].GetString();
+
+ if (!IsValidEvent(eventId))
+ {
+ TC_LOG_ERROR("sql.sql", "Event (ID: {}) not referring to any gameobject_template (type 3 data6 field, type 7 data3 field, type 10 data2 field, type 13 data2 field, type 50 data7 field), any taxi path node or any spell effect {}",
+ eventId, SPELL_EFFECT_SEND_EVENT);
+ continue;
+ }
+ _eventScriptStore[eventId] = GetScriptId(scriptName);
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded {} event scripts in {} ms", _eventScriptStore.size(), GetMSTimeDiffToNow(oldMSTime));
}
//Load WP Scripts
@@ -8982,6 +9019,14 @@ SpellScriptsBounds ObjectMgr::GetSpellScriptsBounds(uint32 spellId)
return SpellScriptsBounds(_spellScriptsStore.equal_range(spellId));
}
+uint32 ObjectMgr::GetEventScriptId(uint32 eventId) const
+{
+ EventScriptContainer::const_iterator i = _eventScriptStore.find(eventId);
+ if (i != _eventScriptStore.end())
+ return i->second;
+ return 0;
+}
+
// this allows calculating base reputations to offline players, just by race and class
int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 race, uint8 playerClass) const
{
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index f0b35c6aafa..2cd8185dd26 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -1096,6 +1096,9 @@ class TC_GAME_API ObjectMgr
typedef std::unordered_map<uint64, AccessRequirement> AccessRequirementContainer;
+ typedef std::set<uint32> EventContainer;
+ typedef std::unordered_map<uint32, uint32> EventScriptContainer;
+
typedef std::unordered_map<uint32, RepRewardRate > RepRewardRateContainer;
typedef std::unordered_map<uint32, ReputationOnKillEntry> RepOnKillContainer;
typedef std::unordered_map<uint32, RepSpilloverTemplate> RepSpilloverTemplateContainer;
@@ -1218,6 +1221,11 @@ class TC_GAME_API ObjectMgr
return _gameObjectForQuestStore.find(entry) != _gameObjectForQuestStore.end();
}
+ bool IsValidEvent(uint32 eventId) const
+ {
+ return _eventStore.find(eventId) != _eventStore.end();
+ }
+
NpcText const* GetNpcText(uint32 textID) const;
QuestGreeting const* GetQuestGreeting(TypeID type, uint32 id) const;
QuestGreetingLocale const* GetQuestGreetingLocale(TypeID type, uint32 id) const;
@@ -1238,6 +1246,7 @@ class TC_GAME_API ObjectMgr
AreaTriggerStruct const* GetMapEntranceTrigger(uint32 Map) const;
uint32 GetAreaTriggerScriptId(uint32 trigger_id) const;
+ uint32 GetEventScriptId(uint32 eventId) const;
SpellScriptsBounds GetSpellScriptsBounds(uint32 spellId);
RepRewardRate const* GetRepRewardRate(uint32 factionId) const
@@ -1810,6 +1819,9 @@ class TC_GAME_API ObjectMgr
DungeonEncounterContainer _dungeonEncounterStore;
std::unordered_map<uint32, WorldSafeLocsEntry> _worldSafeLocs;
+ EventContainer _eventStore;
+ EventScriptContainer _eventScriptStore;
+
RepRewardRateContainer _repRewardRateStore;
RepOnKillContainer _repOnKillStore;
RepSpilloverTemplateContainer _repSpilloverTemplateStore;
@@ -1864,6 +1876,7 @@ class TC_GAME_API ObjectMgr
std::unordered_map<uint32, std::vector<TerrainSwapInfo*>> _terrainSwapInfoByMap;
private:
+ void LoadEventSet();
void LoadScripts(ScriptsType type);
void LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReverse* reverseMap, std::string const& table);
QuestRelationResult GetQuestRelationsFrom(QuestRelations const& map, uint32 key, bool onlyActive) const { return { map.equal_range(key), onlyActive }; }
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index ea370b4efb4..1dfaa9aeb67 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -87,7 +87,7 @@ struct is_script_database_bound<AreaTriggerScript>
template<>
struct is_script_database_bound<BattlefieldScript>
- : std::true_type { };
+ : std::true_type { };
template<>
struct is_script_database_bound<BattlegroundScript>
@@ -137,6 +137,10 @@ template<>
struct is_script_database_bound<WorldStateScript>
: std::true_type { };
+template<>
+struct is_script_database_bound<EventScript>
+ : std::true_type { };
+
enum Spells
{
SPELL_HOTSWAP_VISUAL_SPELL_EFFECT = 40162 // 59084
@@ -2358,6 +2362,15 @@ void ScriptMgr::OnWorldStateValueChange(WorldStateTemplate const* worldStateTemp
tmpscript->OnValueChange(worldStateTemplate->Id, oldValue, newValue, map);
}
+// Event
+void ScriptMgr::OnEventTrigger(WorldObject* object, WorldObject* invoker, uint32 eventId)
+{
+ ASSERT(invoker);
+
+ GET_SCRIPT(EventScript, sObjectMgr->GetEventScriptId(eventId), tmpscript);
+ tmpscript->OnTrigger(object, invoker, eventId);
+}
+
SpellScriptLoader::SpellScriptLoader(char const* name)
: ScriptObject(name)
{
@@ -3209,6 +3222,18 @@ void WorldStateScript::OnValueChange(int32 /*worldStateId*/, int32 /*oldValue*/,
{
}
+EventScript::EventScript(char const* name)
+ : ScriptObject(name)
+{
+ ScriptRegistry<EventScript>::Instance()->AddScript(this);
+}
+
+EventScript::~EventScript() = default;
+
+void EventScript::OnTrigger(WorldObject* /*object*/, WorldObject* /*invoker*/, uint32 /*eventId*/)
+{
+}
+
// Specialize for each script type class like so:
template class TC_GAME_API ScriptRegistry<SpellScriptLoader>;
template class TC_GAME_API ScriptRegistry<ServerScript>;
@@ -3243,3 +3268,4 @@ template class TC_GAME_API ScriptRegistry<ConversationScript>;
template class TC_GAME_API ScriptRegistry<SceneScript>;
template class TC_GAME_API ScriptRegistry<QuestScript>;
template class TC_GAME_API ScriptRegistry<WorldStateScript>;
+template class TC_GAME_API ScriptRegistry<EventScript>;
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index 631b5320cd6..f20652607b2 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -990,6 +990,20 @@ class TC_GAME_API WorldStateScript : public ScriptObject
virtual void OnValueChange(int32 worldStateId, int32 oldValue, int32 newValue, Map const* map);
};
+class TC_GAME_API EventScript : public ScriptObject
+{
+ protected:
+
+ explicit EventScript(char const* name);
+
+ public:
+
+ ~EventScript();
+
+ // Called when a game event is triggered
+ virtual void OnTrigger(WorldObject* object, WorldObject* invoker, uint32 eventId);
+};
+
// Manages registration, loading, and execution of scripts.
class TC_GAME_API ScriptMgr
{
@@ -1296,6 +1310,10 @@ class TC_GAME_API ScriptMgr
void OnWorldStateValueChange(WorldStateTemplate const* worldStateTemplate, int32 oldValue, int32 newValue, Map const* map);
+ public: /* EventScript */
+
+ void OnEventTrigger(WorldObject* object, WorldObject* invoker, uint32 eventId);
+
private:
uint32 _scriptCount;
bool _scriptIdUpdated;