diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 16 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 8 | ||||
-rw-r--r-- | src/server/game/Handlers/QuestHandler.cpp | 9 | ||||
-rw-r--r-- | src/server/game/Quests/QuestDef.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Quests/QuestDef.h | 2 | ||||
-rw-r--r-- | src/server/game/Scripting/ScriptMgr.cpp | 58 | ||||
-rw-r--r-- | src/server/game/Scripting/ScriptMgr.h | 20 | ||||
-rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 5 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_quest.cpp | 3 |
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; |