aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Entities/Player/Player.cpp16
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp8
-rw-r--r--src/server/game/Handlers/QuestHandler.cpp9
-rw-r--r--src/server/game/Quests/QuestDef.cpp2
-rw-r--r--src/server/game/Quests/QuestDef.h2
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp58
-rw-r--r--src/server/game/Scripting/ScriptMgr.h20
-rw-r--r--src/server/game/Spells/SpellEffects.cpp5
-rw-r--r--src/server/scripts/Commands/cs_quest.cpp3
9 files changed, 116 insertions, 7 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 9a04fe24bfb..3ea0834997b 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -14887,6 +14887,7 @@ void Player::AddQuest(Quest const* quest, Object* questGiver)
// if not exist then created with set uState == NEW and rewarded=false
QuestStatusData& questStatusData = m_QuestStatus[quest_id];
+ QuestStatus oldStatus = questStatusData.Status;
// check for repeatable quests status reset
questStatusData.Status = QUEST_STATUS_INCOMPLETE;
@@ -14963,6 +14964,7 @@ void Player::AddQuest(Quest const* quest, Object* questGiver)
}
sScriptMgr->OnQuestStatusChange(this, quest_id);
+ sScriptMgr->OnQuestStatusChange(this, quest, oldStatus, questStatusData.Status);
}
void Player::CompleteQuest(uint32 quest_id)
@@ -15059,6 +15061,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
SetCanDelayTeleport(true);
uint32 quest_id = quest->GetQuestId();
+ QuestStatus oldStatus = GetQuestStatus(quest_id);
for (QuestObjective const& obj : quest->GetObjectives())
{
@@ -15306,6 +15309,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
SetCanDelayTeleport(false);
sScriptMgr->OnQuestStatusChange(this, quest_id);
+ sScriptMgr->OnQuestStatusChange(this, quest, oldStatus, QUEST_STATUS_REWARDED);
}
void Player::SetRewardedQuest(uint32 quest_id)
@@ -15967,16 +15971,18 @@ void Player::SetQuestStatus(uint32 questId, QuestStatus status, bool update /*=
{
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
{
+ QuestStatus oldStatus = m_QuestStatus[questId].Status;
m_QuestStatus[questId].Status = status;
if (!quest->IsAutoComplete())
m_QuestStatusSave[questId] = QUEST_DEFAULT_SAVE_TYPE;
+
+ sScriptMgr->OnQuestStatusChange(this, questId);
+ sScriptMgr->OnQuestStatusChange(this, quest, oldStatus, status);
}
if (update)
SendQuestUpdate(questId);
-
- sScriptMgr->OnQuestStatusChange(this, questId);
}
void Player::RemoveActiveQuest(uint32 questId, bool update /*= true*/)
@@ -16899,9 +16905,13 @@ void Player::SetQuestObjectiveData(QuestObjective const& objective, int32 data)
}
// No change
- if (status.ObjectiveData[objective.StorageIndex] == data)
+ int32 oldData = status.ObjectiveData[objective.StorageIndex];
+ if (oldData == data)
return;
+ if (Quest const* quest = sObjectMgr->GetQuestTemplate(objective.QuestID))
+ sScriptMgr->OnQuestObjectiveChange(this, quest, objective, oldData, data);
+
// Set data
status.ObjectiveData[objective.StorageIndex] = data;
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 8ebc5c48e15..dbdebd62054 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -4012,8 +4012,10 @@ void ObjectMgr::LoadQuests()
// Load `quest_template_addon`
// 0 1 2 3 4 5 6 7 8
result = WorldDatabase.Query("SELECT ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, RewardMailTemplateID, RewardMailDelay, "
- //9 10 11 12 13 14 15 16 17
- "RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, RewardMailSenderEntry, SpecialFlags FROM quest_template_addon LEFT JOIN quest_mail_sender ON Id=QuestId");
+ //9 10 11 12 13 14 15 16
+ "RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, RewardMailSenderEntry, "
+ //17 18
+ "SpecialFlags, ScriptName FROM quest_template_addon LEFT JOIN quest_mail_sender ON Id=QuestId");
if (!result)
{
@@ -9119,6 +9121,8 @@ void ObjectMgr::LoadScriptNames()
"UNION "
"SELECT DISTINCT(ScriptName) FROM scene_template WHERE ScriptName <> '' "
"UNION "
+ "SELECT DISTINCT(ScriptName) FROM quest_template_addon WHERE ScriptName <> '' "
+ "UNION "
"SELECT DISTINCT(script) FROM instance_template WHERE script <> ''");
if (!result)
diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp
index 36b6831ac71..fedd4b4f072 100644
--- a/src/server/game/Handlers/QuestHandler.cpp
+++ b/src/server/game/Handlers/QuestHandler.cpp
@@ -436,7 +436,10 @@ void WorldSession::HandleQuestLogRemoveQuest(WorldPackets::Quest::QuestLogRemove
if (!_player->TakeQuestSourceItem(questId, true))
return; // can't un-equip some items, reject quest cancel
- if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
+ Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
+ QuestStatus oldStatus = _player->GetQuestStatus(questId);
+
+ if (quest)
{
if (quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED))
_player->RemoveTimedQuest(questId);
@@ -446,6 +449,7 @@ void WorldSession::HandleQuestLogRemoveQuest(WorldPackets::Quest::QuestLogRemove
_player->pvpInfo.IsHostile = _player->pvpInfo.IsInHostileArea || _player->HasPvPForcingQuest();
_player->UpdatePvPState();
}
+
}
_player->TakeQuestSourceItem(questId, true); // remove quest src item from player
@@ -467,6 +471,9 @@ void WorldSession::HandleQuestLogRemoveQuest(WorldPackets::Quest::QuestLogRemove
}
sScriptMgr->OnQuestStatusChange(_player, questId);
+
+ if (quest)
+ sScriptMgr->OnQuestStatusChange(_player, quest, oldStatus, QUEST_STATUS_NONE);
}
_player->SetQuestSlot(packet.Entry, 0);
diff --git a/src/server/game/Quests/QuestDef.cpp b/src/server/game/Quests/QuestDef.cpp
index 57f16b0622e..4ebc6cc6ec4 100644
--- a/src/server/game/Quests/QuestDef.cpp
+++ b/src/server/game/Quests/QuestDef.cpp
@@ -21,6 +21,7 @@
#include "Field.h"
#include "GameTables.h"
#include "Log.h"
+#include "ObjectMgr.h"
#include "Player.h"
#include "QuestPackets.h"
#include "World.h"
@@ -212,6 +213,7 @@ void Quest::LoadQuestTemplateAddon(Field* fields)
SourceItemIdCount = fields[15].GetUInt8();
RewardMailSenderEntry = fields[16].GetUInt32();
SpecialFlags = fields[17].GetUInt8();
+ ScriptId = sObjectMgr->GetScriptId(fields[18].GetString());
if (SpecialFlags & QUEST_SPECIAL_FLAGS_AUTO_ACCEPT)
Flags |= QUEST_FLAGS_AUTO_ACCEPT;
diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h
index efacc69367a..1b404787953 100644
--- a/src/server/game/Quests/QuestDef.h
+++ b/src/server/game/Quests/QuestDef.h
@@ -411,6 +411,7 @@ class TC_GAME_API Quest
uint32 GetFlags() const { return Flags; }
uint32 GetFlagsEx() const { return FlagsEx; }
uint32 GetSpecialFlags() const { return SpecialFlags; }
+ uint32 GetScriptId() const { return ScriptId; }
uint32 GetAreaGroupID() const { return AreaGroupID; }
uint32 GetRewardSkillId() const { return RewardSkillId; }
uint32 GetRewardSkillPoints() const { return RewardSkillPoints; }
@@ -551,6 +552,7 @@ class TC_GAME_API Quest
uint32 SourceItemIdCount = 0;
uint32 RewardMailSenderEntry = 0;
uint32 SpecialFlags = 0; // custom flags, not sniffed/WDB
+ uint32 ScriptId = 0;
};
struct QuestStatusData
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index 2e7bd6b52da..883c4c2c68d 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -118,6 +118,10 @@ template<>
struct is_script_database_bound<SceneScript>
: std::true_type { };
+template<>
+struct is_script_database_bound<QuestScript>
+ : std::true_type { };
+
enum Spells
{
SPELL_HOTSWAP_VISUAL_SPELL_EFFECT = 40162 // 59084
@@ -748,6 +752,35 @@ private:
bool swapped;
};
+/// This hook is responsible for swapping QuestScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<QuestScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
+
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+ if (bounds.first != bounds.second)
+ swapped = true;
+ }
+
+ void BeforeSwapContext(bool /*initialize*/) override
+ {
+ swapped = false;
+ }
+
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
+
/// This hook is responsible for swapping SpellScriptLoader's
template<typename Base>
class ScriptRegistrySwapHooks<SpellScriptLoader, Base>
@@ -2460,6 +2493,24 @@ void ScriptMgr::OnSceneComplete(Player* player, uint32 sceneInstanceID, SceneTem
tmpscript->OnSceneComplete(player, sceneInstanceID, sceneTemplate);
}
+void ScriptMgr::OnQuestStatusChange(Player* player, Quest const* quest, QuestStatus oldStatus, QuestStatus newStatus)
+{
+ ASSERT(player);
+ ASSERT(quest);
+
+ GET_SCRIPT(QuestScript, quest->GetScriptId(), tmpscript);
+ tmpscript->OnQuestStatusChange(player, quest, oldStatus, newStatus);
+}
+
+void ScriptMgr::OnQuestObjectiveChange(Player* player, Quest const* quest, QuestObjective const& objective, int32 oldAmount, int32 newAmount)
+{
+ ASSERT(player);
+ ASSERT(quest);
+
+ GET_SCRIPT(QuestScript, quest->GetScriptId(), tmpscript);
+ tmpscript->OnQuestObjectiveChange(player, quest, objective, oldAmount, newAmount);
+}
+
SpellScriptLoader::SpellScriptLoader(const char* name)
: ScriptObject(name)
{
@@ -2639,6 +2690,12 @@ SceneScript::SceneScript(const char* name)
ScriptRegistry<SceneScript>::Instance()->AddScript(this);
}
+QuestScript::QuestScript(const char* name)
+ : ScriptObject(name)
+{
+ ScriptRegistry<QuestScript>::Instance()->AddScript(this);
+}
+
GuildScript::GuildScript(const char* name)
: ScriptObject(name)
{
@@ -2693,3 +2750,4 @@ template class TC_GAME_API ScriptRegistry<AccountScript>;
template class TC_GAME_API ScriptRegistry<AreaTriggerEntityScript>;
template class TC_GAME_API ScriptRegistry<ConversationScript>;
template class TC_GAME_API ScriptRegistry<SceneScript>;
+template class TC_GAME_API ScriptRegistry<QuestScript>;
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index fb1e8668558..7d3b30baddb 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -72,6 +72,7 @@ struct CreatureData;
struct ItemTemplate;
struct MapEntry;
struct OutdoorPvPData;
+struct QuestObjective;
struct SceneTemplate;
enum BattlegroundTypeId : uint32;
@@ -891,6 +892,20 @@ class TC_GAME_API SceneScript : public ScriptObject
virtual void OnSceneComplete(Player* /*player*/, uint32 /*sceneInstanceID*/, SceneTemplate const* /*sceneTemplate*/) { }
};
+class TC_GAME_API QuestScript : public ScriptObject
+{
+ protected:
+
+ QuestScript(const char* name);
+
+ public:
+ // Called when a quest status change
+ virtual void OnQuestStatusChange(Player* /*player*/, Quest const* /*quest*/, QuestStatus /*oldStatus*/, QuestStatus /*newStatus*/) { }
+
+ // Called when a quest objective data change
+ virtual void OnQuestObjectiveChange(Player* /*player*/, Quest const* /*quest*/, QuestObjective const& /*objective*/, int32 /*oldAmount*/, int32 /*newAmount*/) { }
+};
+
// Manages registration, loading, and execution of scripts.
class TC_GAME_API ScriptMgr
{
@@ -1184,6 +1199,11 @@ class TC_GAME_API ScriptMgr
void OnSceneCancel(Player* player, uint32 sceneInstanceID, SceneTemplate const* sceneTemplate);
void OnSceneComplete(Player* player, uint32 sceneInstanceID, SceneTemplate const* sceneTemplate);
+ public: /* QuestScript */
+
+ void OnQuestStatusChange(Player* player, Quest const* quest, QuestStatus oldStatus, QuestStatus newStatus);
+ void OnQuestObjectiveChange(Player* player, Quest const* quest, QuestObjective const& objective, int32 oldAmount, int32 newAmount);
+
private:
uint32 _scriptCount;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 6c1e2b8cc32..b37e632f452 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -4367,8 +4367,10 @@ void Spell::EffectQuestClear(SpellEffIndex /*effIndex*/)
if (!quest)
return;
+ QuestStatus oldStatus = player->GetQuestStatus(quest_id);
+
// Player has never done this quest
- if (player->GetQuestStatus(quest_id) == QUEST_STATUS_NONE)
+ if (oldStatus == QUEST_STATUS_NONE)
return;
// remove all quest entries for 'entry' from quest log
@@ -4394,6 +4396,7 @@ void Spell::EffectQuestClear(SpellEffIndex /*effIndex*/)
player->RemoveRewardedQuest(quest_id);
sScriptMgr->OnQuestStatusChange(player, quest_id);
+ sScriptMgr->OnQuestStatusChange(player, quest, oldStatus, QUEST_STATUS_NONE);
}
void Spell::EffectSendTaxi(SpellEffIndex /*effIndex*/)
diff --git a/src/server/scripts/Commands/cs_quest.cpp b/src/server/scripts/Commands/cs_quest.cpp
index 8b7e5e1bf93..ebab565f6df 100644
--- a/src/server/scripts/Commands/cs_quest.cpp
+++ b/src/server/scripts/Commands/cs_quest.cpp
@@ -128,6 +128,8 @@ public:
return false;
}
+ QuestStatus oldStatus = player->GetQuestStatus(entry);
+
// remove all quest entries for 'entry' from quest log
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
@@ -151,6 +153,7 @@ public:
player->RemoveRewardedQuest(entry);
sScriptMgr->OnQuestStatusChange(player, entry);
+ sScriptMgr->OnQuestStatusChange(player, quest, oldStatus, QUEST_STATUS_NONE);
handler->SendSysMessage(LANG_COMMAND_QUEST_REMOVED);
return true;