diff options
Diffstat (limited to 'src')
109 files changed, 5078 insertions, 3973 deletions
diff --git a/src/common/Metric/Metric.cpp b/src/common/Metric/Metric.cpp index 9484cebcc72..cb6b3b1217b 100644 --- a/src/common/Metric/Metric.cpp +++ b/src/common/Metric/Metric.cpp @@ -22,7 +22,7 @@ void Metric::Initialize(std::string const& realmName, boost::asio::io_service& ioService, std::function<void()> overallStatusLogger) { - _realmName = realmName; + _realmName = FormatInfluxDBTagValue(realmName); _batchTimer = Trinity::make_unique<boost::asio::deadline_timer>(ioService); _overallStatusTimer = Trinity::make_unique<boost::asio::deadline_timer>(ioService); _overallStatusLogger = overallStatusLogger; diff --git a/src/common/Metric/Metric.h b/src/common/Metric/Metric.h index 1855e1d0098..9230983da4d 100644 --- a/src/common/Metric/Metric.h +++ b/src/common/Metric/Metric.h @@ -79,6 +79,14 @@ private: static std::string FormatInfluxDBValue(double value) { return std::to_string(value); } static std::string FormatInfluxDBValue(float value) { return FormatInfluxDBValue(double(value)); } + static std::string FormatInfluxDBTagValue(std::string const& value) + { + // ToDo: should handle '=' and ',' characters too + return boost::replace_all_copy(value, " ", "\\ "); + } + + // ToDo: should format TagKey and FieldKey too in the same way as TagValue + public: static Metric* instance(); diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h index fc322a89583..88708a79cd6 100644 --- a/src/common/Utilities/Util.h +++ b/src/common/Utilities/Util.h @@ -327,33 +327,35 @@ TC_COMMON_API bool StringToBool(std::string const& str); // simple class for not-modifyable list template <typename T> -class HookList +class HookList final { - typedef typename std::list<T>::iterator ListIterator; private: - typename std::list<T> m_list; + typedef std::vector<T> ContainerType; + + ContainerType _container; + public: - HookList<T> & operator+=(T t) - { - m_list.push_back(t); - return *this; - } - HookList<T> & operator-=(T t) + typedef typename ContainerType::iterator iterator; + + HookList<T>& operator+=(T t) { - m_list.remove(t); + _container.push_back(t); return *this; } + size_t size() { - return m_list.size(); + return _container.size(); } - ListIterator begin() + + iterator begin() { - return m_list.begin(); + return _container.begin(); } - ListIterator end() + + iterator end() { - return m_list.end(); + return _container.end(); } }; diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp index 45c2b61436f..b139d50ef8a 100644 --- a/src/server/authserver/Server/AuthSession.cpp +++ b/src/server/authserver/Server/AuthSession.cpp @@ -118,10 +118,10 @@ std::unordered_map<uint8, AuthHandler> AuthSession::InitHandlers() { std::unordered_map<uint8, AuthHandler> handlers; - handlers[AUTH_LOGON_CHALLENGE] = { STATUS_CONNECTED, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleLogonChallenge }; - handlers[AUTH_LOGON_PROOF] = { STATUS_CONNECTED, sizeof(AUTH_LOGON_PROOF_C), &AuthSession::HandleLogonProof }; - handlers[AUTH_RECONNECT_CHALLENGE] = { STATUS_CONNECTED, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleReconnectChallenge }; - handlers[AUTH_RECONNECT_PROOF] = { STATUS_CONNECTED, sizeof(AUTH_RECONNECT_PROOF_C), &AuthSession::HandleReconnectProof }; + handlers[AUTH_LOGON_CHALLENGE] = { STATUS_CHALLENGE, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleLogonChallenge }; + handlers[AUTH_LOGON_PROOF] = { STATUS_LOGON_PROOF, sizeof(AUTH_LOGON_PROOF_C), &AuthSession::HandleLogonProof }; + handlers[AUTH_RECONNECT_CHALLENGE] = { STATUS_CHALLENGE, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleReconnectChallenge }; + handlers[AUTH_RECONNECT_PROOF] = { STATUS_RECONNECT_PROOF, sizeof(AUTH_RECONNECT_PROOF_C), &AuthSession::HandleReconnectProof }; handlers[REALM_LIST] = { STATUS_AUTHED, REALM_LIST_PACKET_SIZE, &AuthSession::HandleRealmList }; return handlers; @@ -154,8 +154,7 @@ void AccountInfo::LoadResult(Field* fields) } AuthSession::AuthSession(tcp::socket&& socket) : Socket(std::move(socket)), -_sentChallenge(false), _sentProof(false), -_status(STATUS_CONNECTED), _build(0), _expversion(0) +_status(STATUS_CHALLENGE), _build(0), _expversion(0) { N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); g.SetDword(7); @@ -285,10 +284,7 @@ void AuthSession::SendPacket(ByteBuffer& packet) bool AuthSession::HandleLogonChallenge() { - if (_sentChallenge) - return false; - - _sentChallenge = true; + _status = STATUS_CLOSED; sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer()); if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len) @@ -428,7 +424,10 @@ void AuthSession::LogonChallengeCallback(PreparedQueryResult result) // Fill the response packet with the result if (AuthHelper::IsAcceptedClientBuild(_build)) + { pkt << uint8(WOW_SUCCESS); + _status = STATUS_LOGON_PROOF; + } else pkt << uint8(WOW_FAIL_VERSION_INVALID); @@ -477,10 +476,7 @@ void AuthSession::LogonChallengeCallback(PreparedQueryResult result) bool AuthSession::HandleLogonProof() { TC_LOG_DEBUG("server.authserver", "Entering _HandleLogonProof"); - if (_sentProof) - return false; - - _sentProof = true; + _status = STATUS_CLOSED; // Read the packet sAuthLogonProof_C *logonProof = reinterpret_cast<sAuthLogonProof_C*>(GetReadBuffer().GetReadPointer()); @@ -500,9 +496,7 @@ bool AuthSession::HandleLogonProof() // SRP safeguard: abort if A == 0 if (A.IsZero()) - { return false; - } SHA1Hash sha; sha.UpdateBigNumbers(&A, &B, NULL); @@ -571,24 +565,6 @@ bool AuthSession::HandleLogonProof() // Check if SRP6 results match (password is correct), else send an error if (!memcmp(M.AsByteArray(sha.GetLength()).get(), logonProof->M1, 20)) { - TC_LOG_DEBUG("server.authserver", "'%s:%d' User '%s' successfully authenticated", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str()); - - // Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account - // No SQL injection (escaped user name) and IP address as received by socket - - PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF); - stmt->setString(0, K.AsHexStr()); - stmt->setString(1, GetRemoteIpAddress().to_string().c_str()); - stmt->setUInt32(2, GetLocaleByName(_localizationName)); - stmt->setString(3, _os); - stmt->setString(4, _accountInfo.Login); - LoginDatabase.DirectExecute(stmt); - - // Finish SRP6 and send the final result to the client - sha.Initialize(); - sha.UpdateBigNumbers(&A, &M, &K, NULL); - sha.Finalize(); - // Check auth token if ((logonProof->securityFlags & 0x04) || !_tokenKey.empty()) { @@ -610,6 +586,24 @@ bool AuthSession::HandleLogonProof() } } + TC_LOG_DEBUG("server.authserver", "'%s:%d' User '%s' successfully authenticated", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str()); + + // Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account + // No SQL injection (escaped user name) and IP address as received by socket + + PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF); + stmt->setString(0, K.AsHexStr()); + stmt->setString(1, GetRemoteIpAddress().to_string().c_str()); + stmt->setUInt32(2, GetLocaleByName(_localizationName)); + stmt->setString(3, _os); + stmt->setString(4, _accountInfo.Login); + LoginDatabase.DirectExecute(stmt); + + // Finish SRP6 and send the final result to the client + sha.Initialize(); + sha.UpdateBigNumbers(&A, &M, &K, NULL); + sha.Finalize(); + ByteBuffer packet; if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients { @@ -705,10 +699,7 @@ bool AuthSession::HandleLogonProof() bool AuthSession::HandleReconnectChallenge() { - if (_sentChallenge) - return false; - - _sentChallenge = true; + _status = STATUS_CLOSED; sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer()); if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len) @@ -768,6 +759,7 @@ void AuthSession::ReconnectChallengeCallback(PreparedQueryResult result) _accountInfo.LoadResult(fields); K.SetHexStr(fields[9].GetCString()); _reconnectProof.SetRand(16 * 8); + _status = STATUS_RECONNECT_PROOF; pkt << uint8(WOW_SUCCESS); pkt.append(_reconnectProof.AsByteArray(16).get(), 16); // 16 bytes random @@ -779,10 +771,7 @@ void AuthSession::ReconnectChallengeCallback(PreparedQueryResult result) bool AuthSession::HandleReconnectProof() { TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectProof"); - if (_sentProof) - return false; - - _sentProof = true; + _status = STATUS_CLOSED; sAuthReconnectProof_C *reconnectProof = reinterpret_cast<sAuthReconnectProof_C*>(GetReadBuffer().GetReadPointer()); diff --git a/src/server/authserver/Server/AuthSession.h b/src/server/authserver/Server/AuthSession.h index 027011629eb..98d2cb9fcdb 100644 --- a/src/server/authserver/Server/AuthSession.h +++ b/src/server/authserver/Server/AuthSession.h @@ -34,8 +34,11 @@ struct AuthHandler; enum AuthStatus { - STATUS_CONNECTED = 0, - STATUS_AUTHED + STATUS_CHALLENGE = 0, + STATUS_LOGON_PROOF, + STATUS_RECONNECT_PROOF, + STATUS_AUTHED, + STATUS_CLOSED }; struct AccountInfo @@ -90,9 +93,6 @@ private: BigNumber K; BigNumber _reconnectProof; - bool _sentChallenge; - bool _sentProof; - AuthStatus _status; AccountInfo _accountInfo; std::string _tokenKey; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index bbf03248ec2..6122e5aca7b 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -243,8 +243,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_EQUIP_SET, "DELETE FROM character_equipmentsets WHERE setguid=?", CONNECTION_ASYNC); // Auras - PrepareStatement(CHAR_INS_AURA, "INSERT INTO character_aura (guid, casterGuid, itemGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_AURA, "INSERT INTO character_aura (guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); // Account data PrepareStatement(CHAR_SEL_ACCOUNT_DATA, "SELECT type, time, data FROM account_data WHERE accountId = ?", CONNECTION_ASYNC); diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp index fbb79426bb8..a2967dc7391 100644 --- a/src/server/game/AI/CoreAI/UnitAI.cpp +++ b/src/server/game/AI/CoreAI/UnitAI.cpp @@ -56,16 +56,25 @@ void UnitAI::DoMeleeAttackIfReady() if (!me->IsWithinMeleeRange(victim)) return; + bool sparAttack = me->GetFactionTemplateEntry()->ShouldSparAttack() && victim->GetFactionTemplateEntry()->ShouldSparAttack(); //Make sure our attack is ready and we aren't currently casting before checking distance if (me->isAttackReady()) { - me->AttackerStateUpdate(victim); + if (sparAttack) + me->FakeAttackerStateUpdate(victim); + else + me->AttackerStateUpdate(victim); + me->resetAttackTimer(); } if (me->haveOffhandWeapon() && me->isAttackReady(OFF_ATTACK)) { - me->AttackerStateUpdate(victim, OFF_ATTACK); + if (sparAttack) + me->FakeAttackerStateUpdate(victim, OFF_ATTACK); + else + me->AttackerStateUpdate(victim, OFF_ATTACK); + me->resetAttackTimer(OFF_ATTACK); } } diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index 241441db958..b6635db5daf 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -569,7 +569,7 @@ void BossAI::_DespawnAtEvade(uint32 delayToRespawn, Creature* who) return; } - me->DespawnOrUnsummon(0, Seconds(delayToRespawn)); + who->DespawnOrUnsummon(0, Seconds(delayToRespawn)); if (instance && who == me) instance->SetBossState(_bossId, FAIL); @@ -635,29 +635,3 @@ void WorldBossAI::UpdateAI(uint32 diff) DoMeleeAttackIfReady(); } - -// SD2 grid searchers. -Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive /*= true*/) -{ - return source->FindNearestCreature(entry, maxSearchRange, alive); -} - -GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange) -{ - return source->FindNearestGameObject(entry, maxSearchRange); -} - -void GetCreatureListWithEntryInGrid(std::list<Creature*>& list, WorldObject* source, uint32 entry, float maxSearchRange) -{ - source->GetCreatureListWithEntryInGrid(list, entry, maxSearchRange); -} - -void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* source, uint32 entry, float maxSearchRange) -{ - source->GetGameObjectListWithEntryInGrid(list, entry, maxSearchRange); -} - -void GetPlayerListInGrid(std::list<Player*>& list, WorldObject* source, float maxSearchRange) -{ - source->GetPlayerListInGrid(list, maxSearchRange); -} diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index 08cc4fa3960..37a7020752b 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -417,10 +417,32 @@ class TC_GAME_API WorldBossAI : public ScriptedAI }; // SD2 grid searchers. -TC_GAME_API Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive = true); -TC_GAME_API GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange); -TC_GAME_API void GetCreatureListWithEntryInGrid(std::list<Creature*>& list, WorldObject* source, uint32 entry, float maxSearchRange); -TC_GAME_API void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* source, uint32 entry, float maxSearchRange); -TC_GAME_API void GetPlayerListInGrid(std::list<Player*>& list, WorldObject* source, float maxSearchRange); +inline Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive = true) +{ + return source->FindNearestCreature(entry, maxSearchRange, alive); +} + +inline GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange) +{ + return source->FindNearestGameObject(entry, maxSearchRange); +} + +template <typename Container> +inline void GetCreatureListWithEntryInGrid(Container& container, WorldObject* source, uint32 entry, float maxSearchRange) +{ + source->GetCreatureListWithEntryInGrid(container, entry, maxSearchRange); +} + +template <typename Container> +inline void GetGameObjectListWithEntryInGrid(Container& container, WorldObject* source, uint32 entry, float maxSearchRange) +{ + source->GetGameObjectListWithEntryInGrid(container, entry, maxSearchRange); +} + +template <typename Container> +inline void GetPlayerListInGrid(Container& container, WorldObject* source, float maxSearchRange) +{ + source->GetPlayerListInGrid(container, maxSearchRange); +} #endif // SCRIPTEDCREATURE_H_ diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index 89ad726b253..9e55b9d8187 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -150,14 +150,10 @@ void npc_escortAI::JustDied(Unit* /*killer*/) { for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next()) if (Player* member = groupRef->GetSource()) - if (member->GetQuestStatus(m_pQuestForEscort->GetQuestId()) == QUEST_STATUS_INCOMPLETE) - member->FailQuest(m_pQuestForEscort->GetQuestId()); + member->FailQuest(m_pQuestForEscort->GetQuestId()); } else - { - if (player->GetQuestStatus(m_pQuestForEscort->GetQuestId()) == QUEST_STATUS_INCOMPLETE) - player->FailQuest(m_pQuestForEscort->GetQuestId()); - } + player->FailQuest(m_pQuestForEscort->GetQuestId()); } } diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp index 24de7344f99..9730370a77c 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp @@ -147,19 +147,11 @@ void FollowerAI::JustDied(Unit* /*killer*/) if (Group* group = player->GetGroup()) { for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next()) - { if (Player* member = groupRef->GetSource()) - { - if (member->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE) - member->FailQuest(m_pQuestForFollow->GetQuestId()); - } - } + member->FailQuest(m_pQuestForFollow->GetQuestId()); } else - { - if (player->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE) - player->FailQuest(m_pQuestForFollow->GetQuestId()); - } + player->FailQuest(m_pQuestForFollow->GetQuestId()); } } diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index b57717fa53d..97bbbfe2b72 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -73,11 +73,7 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c) bool SmartAI::IsAIControlled() const { - if (me->IsControlledByPlayer()) - return false; - if (mIsCharmed) - return false; - return true; + return !mIsCharmed; } void SmartAI::UpdateDespawn(const uint32 diff) @@ -225,7 +221,7 @@ void SmartAI::EndPath(bool fail) if (!fail && player->IsAtGroupRewardDistance(me) && !player->HasCorpse()) player->GroupEventHappens(mEscortQuestID, me); - if (fail && player->GetQuestStatus(mEscortQuestID) == QUEST_STATUS_INCOMPLETE) + if (fail) player->FailQuest(mEscortQuestID); if (Group* group = player->GetGroup()) @@ -236,7 +232,7 @@ void SmartAI::EndPath(bool fail) if (!fail && groupGuy->IsAtGroupRewardDistance(me) && !groupGuy->HasCorpse()) groupGuy->AreaExploredOrEventHappens(mEscortQuestID); - if (fail && groupGuy->GetQuestStatus(mEscortQuestID) == QUEST_STATUS_INCOMPLETE) + else if (fail) groupGuy->FailQuest(mEscortQuestID); } } @@ -250,7 +246,7 @@ void SmartAI::EndPath(bool fail) Player* player = (*iter)->ToPlayer(); if (!fail && player->IsAtGroupRewardDistance(me) && !player->HasCorpse()) player->AreaExploredOrEventHappens(mEscortQuestID); - if (fail && player->GetQuestStatus(mEscortQuestID) == QUEST_STATUS_INCOMPLETE) + else if (fail) player->FailQuest(mEscortQuestID); } } diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 56e943a64c9..48315eb44ed 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -65,7 +65,6 @@ SmartScript::~SmartScript() void SmartScript::OnReset() { - SetPhase(0); ResetBaseObject(); for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i) { @@ -99,8 +98,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u //calc random if (e.GetEventType() != SMART_EVENT_LINK && e.event.event_chance < 100 && e.event.event_chance) { - uint32 rnd = urand(1, 100); - if (e.event.event_chance <= rnd) + if (!roll_chance_i(e.event.event_chance)) return; } e.runOnce = true;//used for repeat check @@ -1069,20 +1067,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u break; for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr) - { if (Creature* target = (*itr)->ToCreature()) - { - if (target->IsAlive() && IsSmart(target)) - { - ENSURE_AI(SmartAI, target->AI())->SetDespawnTime(e.action.forceDespawn.delay + 1); // Next tick - ENSURE_AI(SmartAI, target->AI())->StartDespawn(); - } - else - target->DespawnOrUnsummon(e.action.forceDespawn.delay); - } + target->DespawnOrUnsummon(e.action.forceDespawn.delay); else if (GameObject* goTarget = (*itr)->ToGameObject()) goTarget->SetRespawnTime(e.action.forceDespawn.delay + 1); - } delete targets; break; @@ -1176,7 +1164,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (!IsCreature(*itr)) continue; - if (!(e.event.event_flags & SMART_EVENT_FLAG_WHILE_CHARMED) && !IsCreatureInControlOfSelf(*itr)) + if (!(e.event.event_flags & SMART_EVENT_FLAG_WHILE_CHARMED) && IsCharmedCreature(*itr)) continue; Position pos = (*itr)->GetPosition(); @@ -1683,6 +1671,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u ResetBaseObject(); break; case SMART_ACTION_CALL_SCRIPT_RESET: + SetPhase(0); OnReset(); break; case SMART_ACTION_SET_RANGED_MOVEMENT: @@ -2854,7 +2843,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui if ((e.event.event_phase_mask && !IsInPhase(e.event.event_phase_mask)) || ((e.event.event_flags & SMART_EVENT_FLAG_NOT_REPEATABLE) && e.runOnce)) return; - if (!(e.event.event_flags & SMART_EVENT_FLAG_WHILE_CHARMED) && IsCreature(me) && !IsCreatureInControlOfSelf(me)) + if (!(e.event.event_flags & SMART_EVENT_FLAG_WHILE_CHARMED) && IsCharmedCreature(me)) return; switch (e.GetEventType()) @@ -3599,10 +3588,6 @@ void SmartScript::FillScript(SmartAIEventList e, WorldObject* obj, AreaTriggerEn } mEvents.push_back((*i));//NOTE: 'world(0)' events still get processed in ANY instance mode } - if (mEvents.empty() && obj) - TC_LOG_ERROR("sql.sql", "SmartScript: Entry %u has events but no events added to list because of instance flags.", obj->GetEntry()); - if (mEvents.empty() && at) - TC_LOG_ERROR("sql.sql", "SmartScript: AreaTrigger %u has events but no events added to list because of instance flags. NOTE: triggers can not handle any instance flags.", at->id); } void SmartScript::GetScript() diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h index 8ac5b494911..3cb66faa033 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.h +++ b/src/server/game/AI/SmartScripts/SmartScript.h @@ -78,12 +78,15 @@ class TC_GAME_API SmartScript return obj && obj->GetTypeId() == TYPEID_UNIT; } - static bool IsCreatureInControlOfSelf(WorldObject* obj) + static bool IsCharmedCreature(WorldObject* obj) { - if (Creature* creatureObj = obj ? obj->ToCreature() : nullptr) - return !creatureObj->IsCharmed() && !creatureObj->IsControlledByPlayer(); - else + if (!obj) return false; + + if (Creature* creatureObj = obj->ToCreature()) + return creatureObj->IsCharmed(); + + return false; } static bool IsGameObject(WorldObject* obj) diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index d9f5388e3ce..43bd28c7863 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -27,6 +27,7 @@ #include "ScriptMgr.h" #include "AccountMgr.h" #include "AuctionHouseMgr.h" +#include "AuctionHouseBot.h" #include "Item.h" #include "Language.h" #include "Log.h" @@ -156,7 +157,7 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, SQLTransaction& } // receiver exist - if (bidder || bidderAccId) + if ((bidder || bidderAccId) && !sAuctionBotConfig->IsBotChar(auction->bidder)) { // set owner to bidder (to prevent delete item with sender char deleting) // owner in `data` will set at mail receive and item extracting @@ -189,7 +190,7 @@ void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, SQLTrans Player* owner = ObjectAccessor::FindConnectedPlayer(owner_guid); uint32 owner_accId = sObjectMgr->GetPlayerAccountIdByGUID(owner_guid); // owner exist (online or offline) - if (owner || owner_accId) + if ((owner || owner_accId) && !sAuctionBotConfig->IsBotChar(auction->owner)) MailDraft(auction->BuildAuctionMailSubject(AUCTION_SALE_PENDING), AuctionEntry::BuildAuctionMailBody(auction->bidder, auction->bid, auction->buyout, auction->deposit, auction->GetAuctionCut())) .SendMailTo(trans, MailReceiver(owner, auction->owner), auction, MAIL_CHECK_MASK_COPIED); } @@ -201,7 +202,7 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, SQLTransa Player* owner = ObjectAccessor::FindConnectedPlayer(owner_guid); uint32 owner_accId = sObjectMgr->GetPlayerAccountIdByGUID(owner_guid); // owner exist - if (owner || owner_accId) + if ((owner || owner_accId) && !sAuctionBotConfig->IsBotChar(auction->owner)) { uint32 profit = auction->bid + auction->deposit - auction->GetAuctionCut(); @@ -232,7 +233,7 @@ void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, SQLTransacti Player* owner = ObjectAccessor::FindConnectedPlayer(owner_guid); uint32 owner_accId = sObjectMgr->GetPlayerAccountIdByGUID(owner_guid); // owner exist - if (owner || owner_accId) + if ((owner || owner_accId) && !sAuctionBotConfig->IsBotChar(auction->owner)) { if (owner) owner->GetSession()->SendAuctionOwnerNotification(auction); @@ -259,7 +260,7 @@ void AuctionHouseMgr::SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 new oldBidder_accId = sObjectMgr->GetPlayerAccountIdByGUID(oldBidder_guid); // old bidder exist - if (oldBidder || oldBidder_accId) + if ((oldBidder || oldBidder_accId) && !sAuctionBotConfig->IsBotChar(auction->bidder)) { if (oldBidder && newBidder) oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, newBidder->GetGUID(), newPrice, auction->GetAuctionOutBid(), auction->itemEntry); @@ -281,7 +282,7 @@ void AuctionHouseMgr::SendAuctionCancelledToBidderMail(AuctionEntry* auction, SQ bidder_accId = sObjectMgr->GetPlayerAccountIdByGUID(bidder_guid); // bidder exist - if (bidder || bidder_accId) + if ((bidder || bidder_accId) && !sAuctionBotConfig->IsBotChar(auction->bidder)) MailDraft(auction->BuildAuctionMailSubject(AUCTION_CANCELLED_TO_BIDDER), AuctionEntry::BuildAuctionMailBody(auction->owner, auction->bid, auction->buyout, auction->deposit, 0)) .AddMoney(auction->bid) .SendMailTo(trans, MailReceiver(bidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED); diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp index 74f0aaf428a..4601495a70b 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp +++ b/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp @@ -15,10 +15,12 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "Containers.h" #include "Log.h" #include "Item.h" #include "World.h" #include "Config.h" +#include "AccountMgr.h" #include "AuctionHouseMgr.h" #include "AuctionHouseBot.h" #include "AuctionHouseBotBuyer.h" @@ -56,6 +58,31 @@ bool AuctionBotConfig::Initialize() _itemsPerCycleBoost = GetConfig(CONFIG_AHBOT_ITEMS_PER_CYCLE_BOOST); _itemsPerCycleNormal = GetConfig(CONFIG_AHBOT_ITEMS_PER_CYCLE_NORMAL); + if (uint32 ahBotAccId = GetConfig(CONFIG_AHBOT_ACCOUNT_ID)) + { + // check character count + if (AccountMgr::GetCharactersCount(GetConfig(CONFIG_AHBOT_ACCOUNT_ID))) + { + // find account guids associated with ahbot account + uint32 count = 0; + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID); + stmt->setUInt32(0, ahBotAccId); + if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) + { + do + { + Field* fields = result->Fetch(); + _AHBotCharacters.push_back(fields[0].GetUInt32()); + ++count; + } while (result->NextRow()); + } + + TC_LOG_DEBUG("ahbot", "AuctionHouseBot found %u characters", count); + } + else + TC_LOG_WARN("ahbot", "AuctionHouseBot Account ID %u has no associated characters.", ahBotAccId); + } + return true; } @@ -111,6 +138,8 @@ void AuctionBotConfig::SetConfig(AuctionBotConfigFloatValues index, char const* //Get AuctionHousebot configuration file void AuctionBotConfig::GetConfigFromFile() { + SetConfig(CONFIG_AHBOT_ACCOUNT_ID, "AuctionHouseBot.Account", 0); + SetConfigMax(CONFIG_AHBOT_ALLIANCE_ITEM_AMOUNT_RATIO, "AuctionHouseBot.Alliance.Items.Amount.Ratio", 100, 10000); SetConfigMax(CONFIG_AHBOT_HORDE_ITEM_AMOUNT_RATIO, "AuctionHouseBot.Horde.Items.Amount.Ratio", 100, 10000); SetConfigMax(CONFIG_AHBOT_NEUTRAL_ITEM_AMOUNT_RATIO, "AuctionHouseBot.Neutral.Items.Amount.Ratio", 100, 10000); @@ -272,6 +301,40 @@ char const* AuctionBotConfig::GetHouseTypeName(AuctionHouseType houseType) return names[houseType]; } +// Picks a random character from the list of AHBot chars +uint32 AuctionBotConfig::GetRandChar() const +{ + if (_AHBotCharacters.empty()) + return 0; + + return Trinity::Containers::SelectRandomContainerElement(_AHBotCharacters); +} + +// Picks a random AHBot character, but excludes a specific one. This is used +// to have another character than the auction owner place bids +uint32 AuctionBotConfig::GetRandCharExclude(uint32 exclude) const +{ + if (_AHBotCharacters.empty()) + return 0; + + std::vector<uint32> filteredCharacters; + filteredCharacters.reserve(_AHBotCharacters.size() - 1); + + for (uint32 charId : _AHBotCharacters) + if (charId != exclude) + filteredCharacters.push_back(charId); + + if (filteredCharacters.empty()) + return 0; + + return Trinity::Containers::SelectRandomContainerElement(filteredCharacters); +} + +bool AuctionBotConfig::IsBotChar(uint32 characterID) const +{ + return !characterID || std::find(_AHBotCharacters.begin(), _AHBotCharacters.end(), characterID) != _AHBotCharacters.end(); +} + uint32 AuctionBotConfig::GetConfigItemAmountRatio(AuctionHouseType houseType) const { switch (houseType) @@ -408,7 +471,7 @@ void AuctionHouseBot::PrepareStatusInfos(AuctionHouseBotStatusInfo& statusInfo) if (Item* item = sAuctionMgr->GetAItem(auctionEntry->itemGUIDLow)) { ItemTemplate const* prototype = item->GetTemplate(); - if (!auctionEntry->owner) // Add only ahbot items + if (!auctionEntry->owner || sAuctionBotConfig->IsBotChar(auctionEntry->owner)) // Add only ahbot items { if (prototype->Quality < MAX_AUCTION_QUALITY) ++statusInfo[i].QualityInfo[prototype->Quality]; @@ -426,7 +489,7 @@ void AuctionHouseBot::Rebuild(bool all) { AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(AuctionHouseType(i)); for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = auctionHouse->GetAuctionsBegin(); itr != auctionHouse->GetAuctionsEnd(); ++itr) - if (!itr->second->owner) // ahbot auction + if (!itr->second->owner || sAuctionBotConfig->IsBotChar(itr->second->owner)) // ahbot auction if (all || itr->second->bid == 0) // expire now auction if no bid or forced itr->second->expire_time = sWorld->GetGameTime(); } diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBot.h b/src/server/game/AuctionHouseBot/AuctionHouseBot.h index 1a438e01cdb..4f68a172255 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBot.h +++ b/src/server/game/AuctionHouseBot/AuctionHouseBot.h @@ -152,6 +152,7 @@ enum AuctionBotConfigUInt32Values CONFIG_AHBOT_CLASS_RANDOMSTACKRATIO_KEY, CONFIG_AHBOT_CLASS_RANDOMSTACKRATIO_MISC, CONFIG_AHBOT_CLASS_RANDOMSTACKRATIO_GLYPH, + CONFIG_AHBOT_ACCOUNT_ID, CONFIG_UINT32_AHBOT_UINT32_COUNT }; @@ -224,6 +225,9 @@ public: uint32 GetItemPerCycleBoost() const { return _itemsPerCycleBoost; } uint32 GetItemPerCycleNormal() const { return _itemsPerCycleNormal; } + uint32 GetRandChar() const; + uint32 GetRandCharExclude(uint32 exclude) const; + bool IsBotChar(uint32 characterID) const; void Reload() { GetConfigFromFile(); } static char const* GetHouseTypeName(AuctionHouseType houseType); @@ -231,6 +235,7 @@ public: private: std::string _AHBotIncludes; std::string _AHBotExcludes; + std::vector<uint32> _AHBotCharacters; uint32 _itemsPerCycleBoost; uint32 _itemsPerCycleNormal; diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp index 6b1dcb85bec..0e6b3402db0 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp @@ -103,7 +103,7 @@ uint32 AuctionBotBuyer::GetItemInformation(BuyerConfiguration& config) { AuctionEntry* entry = itr->second; - if (!entry->owner) + if (!entry->owner || sAuctionBotConfig->IsBotChar(entry->owner)) continue; // Skip auctions owned by AHBot Item* item = sAuctionMgr->GetAItem(entry->itemGUIDLow); @@ -218,7 +218,7 @@ bool AuctionBotBuyer::RollBidChance(const BuyerItemInfo* ahInfo, const Item* ite } // If a player has bidded on item, have fifth of normal chance - if (auction->bidder) + if (auction->bidder && !sAuctionBotConfig->IsBotChar(auction->bidder)) chance = chance / 5.f; // Add config weigh in for quality @@ -391,11 +391,11 @@ void AuctionBotBuyer::BuyEntry(AuctionEntry* auction, AuctionHouseObject* auctio SQLTransaction trans = CharacterDatabase.BeginTransaction(); // Send mail to previous bidder if any - if (auction->bidder) + if (auction->bidder && !sAuctionBotConfig->IsBotChar(auction->bidder)) sAuctionMgr->SendAuctionOutbiddedMail(auction, auction->buyout, NULL, trans); // Set bot as bidder and set new bid amount - auction->bidder = 0; + auction->bidder = sAuctionBotConfig->GetRandCharExclude(auction->owner); auction->bid = auction->buyout; // Mails must be under transaction control too to prevent data loss @@ -422,11 +422,11 @@ void AuctionBotBuyer::PlaceBidToEntry(AuctionEntry* auction, uint32 bidPrice) SQLTransaction trans = CharacterDatabase.BeginTransaction(); // Send mail to previous bidder if any - if (auction->bidder) + if (auction->bidder && !sAuctionBotConfig->IsBotChar(auction->bidder)) sAuctionMgr->SendAuctionOutbiddedMail(auction, bidPrice, NULL, trans); // Set bot as bidder and set new bid amount - auction->bidder = 0; + auction->bidder = sAuctionBotConfig->GetRandCharExclude(auction->owner); auction->bid = bidPrice; // Update auction to DB diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp index 17b104eb388..34127f0c59f 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp @@ -320,7 +320,7 @@ bool AuctionBotSeller::Initialize() continue; } - if (prototype->Flags & ITEM_FLAG_UNLOCKED) + if (prototype->Flags & ITEM_FLAG_HAS_LOOT) { // skip any not locked lootable items (mostly quest specific or reward cases) if (!prototype->LockID) @@ -640,7 +640,7 @@ uint32 AuctionBotSeller::SetStat(SellerConfiguration& config) { ItemTemplate const* prototype = item->GetTemplate(); if (prototype) - if (!auctionEntry->owner) // Add only ahbot items + if (!auctionEntry->owner || sAuctionBotConfig->IsBotChar(auctionEntry->owner)) // Add only ahbot items ++itemsSaved[prototype->Quality][prototype->Class]; } } @@ -1019,7 +1019,7 @@ void AuctionBotSeller::AddNewAuctions(SellerConfiguration& config) AuctionEntry* auctionEntry = new AuctionEntry(); auctionEntry->Id = sObjectMgr->GenerateAuctionID(); - auctionEntry->owner = 0; + auctionEntry->owner = sAuctionBotConfig->GetRandChar(); auctionEntry->itemGUIDLow = item->GetGUID().GetCounter(); auctionEntry->itemEntry = item->GetEntry(); auctionEntry->startbid = bidPrice; diff --git a/src/server/game/Battlegrounds/ArenaTeam.cpp b/src/server/game/Battlegrounds/ArenaTeam.cpp index ec007df6b6c..f1747bc0d00 100644 --- a/src/server/game/Battlegrounds/ArenaTeam.cpp +++ b/src/server/game/Battlegrounds/ArenaTeam.cpp @@ -899,6 +899,10 @@ void ArenaTeam::SaveToDB() for (MemberList::const_iterator itr = Members.begin(); itr != Members.end(); ++itr) { + // Save the effort and go + if (itr->WeekGames == 0) + continue; + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_MEMBER); stmt->setUInt16(0, itr->PersonalRating); stmt->setUInt16(1, itr->WeekGames); @@ -919,8 +923,12 @@ void ArenaTeam::SaveToDB() CharacterDatabase.CommitTransaction(trans); } -void ArenaTeam::FinishWeek() +bool ArenaTeam::FinishWeek() { + // No need to go further than this + if (Stats.WeekGames == 0) + return false; + // Reset team stats Stats.WeekGames = 0; Stats.WeekWins = 0; @@ -931,6 +939,8 @@ void ArenaTeam::FinishWeek() itr->WeekGames = 0; itr->WeekWins = 0; } + + return true; } bool ArenaTeam::IsFighting() const diff --git a/src/server/game/Battlegrounds/ArenaTeam.h b/src/server/game/Battlegrounds/ArenaTeam.h index 7e9f490bff9..e283197346c 100644 --- a/src/server/game/Battlegrounds/ArenaTeam.h +++ b/src/server/game/Battlegrounds/ArenaTeam.h @@ -180,7 +180,7 @@ class TC_GAME_API ArenaTeam void UpdateArenaPointsHelper(std::map<uint32, uint32> & PlayerPoints); - void FinishWeek(); + bool FinishWeek(); // returns true if arena team played this week void FinishGame(int32 mod); protected: diff --git a/src/server/game/Battlegrounds/ArenaTeamMgr.cpp b/src/server/game/Battlegrounds/ArenaTeamMgr.cpp index 37e26d7e448..6fe11e3bd8c 100644 --- a/src/server/game/Battlegrounds/ArenaTeamMgr.cpp +++ b/src/server/game/Battlegrounds/ArenaTeamMgr.cpp @@ -186,8 +186,9 @@ void ArenaTeamMgr::DistributeArenaPoints() { if (ArenaTeam* at = titr->second) { - at->FinishWeek(); - at->SaveToDB(); + if (at->FinishWeek()) + at->SaveToDB(); + at->NotifyStatsChanged(); } } diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp index 753d0f92ef3..1ae610ecf68 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp +++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp @@ -843,7 +843,7 @@ void BattlegroundMgr::ScheduleQueueUpdate(uint32 arenaMatchmakerRating, uint8 ar { //This method must be atomic, @todo add mutex //we will use only 1 number created of bgTypeId and bracket_id - uint64 const scheduleId = ((uint64)arenaMatchmakerRating << 32) | (uint32(arenaType) << 24) | (bgQueueTypeId << 16) | (bgTypeId << 8) | bracket_id; + uint64 const scheduleId = ((uint64)arenaMatchmakerRating << 32) | ((uint64)arenaType << 24) | ((uint64)bgQueueTypeId << 16) | ((uint64)bgTypeId << 8) | (uint64)bracket_id; if (std::find(m_QueueUpdateScheduler.begin(), m_QueueUpdateScheduler.end(), scheduleId) == m_QueueUpdateScheduler.end()) m_QueueUpdateScheduler.push_back(scheduleId); } diff --git a/src/server/game/Chat/Channels/Channel.cpp b/src/server/game/Chat/Channels/Channel.cpp index 85d03ef6b1f..afaa0063a45 100644 --- a/src/server/game/Chat/Channels/Channel.cpp +++ b/src/server/game/Chat/Channels/Channel.cpp @@ -55,7 +55,6 @@ Channel::Channel(uint32 channelId, uint32 team /*= 0*/, AreaTableEntry const* zo _channelFlags |= CHANNEL_FLAG_NOT_LFG; } - Channel::Channel(std::string const& name, uint32 team /*= 0*/) : _announceEnabled(true), _ownershipEnabled(true), @@ -749,7 +748,6 @@ void Channel::Say(ObjectGuid guid, std::string const& what, uint32 lang) const }; SendToAll(builder, !info.IsModerator() ? guid : ObjectGuid::Empty); - } void Channel::Invite(Player const* player, std::string const& newname) diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index c859031b573..7ee775ec676 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -1671,7 +1671,6 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) const break; } case CONDITION_SOURCE_TYPE_QUEST_ACCEPT: - case CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK: if (!sObjectMgr->GetQuestTemplate(cond->SourceEntry)) { TC_LOG_ERROR("sql.sql", "%s SourceEntry specifies non-existing quest, skipped.", cond->ToString().c_str()); diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h index 7bda1376a0d..4d32c3f36dc 100644 --- a/src/server/game/Conditions/ConditionMgr.h +++ b/src/server/game/Conditions/ConditionMgr.h @@ -130,7 +130,7 @@ enum ConditionSourceType CONDITION_SOURCE_TYPE_SPELL = 17, CONDITION_SOURCE_TYPE_SPELL_CLICK_EVENT = 18, CONDITION_SOURCE_TYPE_QUEST_ACCEPT = 19, - CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK = 20, + // Condition source type 20 unused CONDITION_SOURCE_TYPE_VEHICLE_SPELL = 21, CONDITION_SOURCE_TYPE_SMART_EVENT = 22, CONDITION_SOURCE_TYPE_NPC_VENDOR = 23, diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index cb2f26e567f..99d7b49f82f 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -312,6 +312,7 @@ enum SpawnMask enum FactionTemplateFlags { + FACTION_TEMPLATE_ENEMY_SPAR = 0x00000020, // guessed, sparring with enemies? FACTION_TEMPLATE_FLAG_PVP = 0x00000800, // flagged for PvP FACTION_TEMPLATE_FLAG_CONTESTED_GUARD = 0x00001000, // faction will attack players that were involved in PvP combats FACTION_TEMPLATE_FLAG_HOSTILE_BY_DEFAULT= 0x00002000 diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index c85344b9bd1..8431f369b69 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -192,6 +192,17 @@ struct AreaTableEntry return true; return (flags & AREA_FLAG_SANCTUARY) != 0; } + + bool IsFlyable() const + { + if (flags & AREA_FLAG_OUTLAND) + { + if (!(flags & AREA_FLAG_NO_FLY_ZONE)) + return true; + } + + return false; + } }; #define MAX_GROUP_AREA_IDS 6 @@ -673,6 +684,7 @@ struct FactionTemplateEntry return hostileMask == 0 && friendlyMask == 0; } bool IsContestedGuardFaction() const { return (factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD) != 0; } + bool ShouldSparAttack() const { return (factionFlags & FACTION_TEMPLATE_ENEMY_SPAR) != 0; } }; struct GameObjectDisplayInfoEntry diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index fa603b9509e..9df9d2ad53c 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1450,23 +1450,21 @@ void Creature::LoadEquipment(int8 id, bool force /*= true*/) void Creature::SetSpawnHealth() { + if (!m_creatureData) + return; + uint32 curhealth; if (!m_regenHealth) { - if (m_creatureData) + curhealth = m_creatureData->curhealth; + if (curhealth) { - curhealth = m_creatureData->curhealth; - if (curhealth) - { - curhealth = uint32(curhealth*_GetHealthMod(GetCreatureTemplate()->rank)); - if (curhealth < 1) - curhealth = 1; - } - SetPower(POWER_MANA, m_creatureData->curmana); + curhealth = uint32(curhealth*_GetHealthMod(GetCreatureTemplate()->rank)); + if (curhealth < 1) + curhealth = 1; } - else - curhealth = GetHealth(); + SetPower(POWER_MANA, m_creatureData->curmana); } else { @@ -1663,6 +1661,7 @@ void Creature::setDeathState(DeathState s) SaveRespawnTime(); ReleaseFocus(nullptr, false); // remove spellcast focus + DoNotReacquireTarget(); // cancel delayed re-target SetTarget(ObjectGuid::Empty); // drop target - dead mobs shouldn't ever target things SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); @@ -1696,6 +1695,7 @@ void Creature::setDeathState(DeathState s) SetLootRecipient(nullptr); ResetPlayerDamageReq(); + SetCannotReachTarget(false); UpdateMovementFlags(); ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~UNIT_STATE_IGNORE_PATHFINDING)); @@ -1767,6 +1767,8 @@ void Creature::Respawn(bool force) } GetMotionMaster()->InitDefault(); + //Re-initialize reactstate that could be altered by movementgenerators + InitializeReactState(); //Call AI respawn virtual function if (IsAIEnabled) @@ -1779,9 +1781,6 @@ void Creature::Respawn(bool force) uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<Creature>(GetSpawnId()) : 0; if (poolid) sPoolMgr->UpdatePool<Creature>(poolid, GetSpawnId()); - - //Re-initialize reactstate that could be altered by movementgenerators - InitializeReactState(); } UpdateObjectVisibility(); diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index bb85fa9fc5b..d79e55b623e 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -339,7 +339,7 @@ struct VendorItem uint32 ExtendedCost; //helpers - bool IsGoldRequired(ItemTemplate const* pProto) const { return pProto->Flags2 & ITEM_FLAGS_EXTRA_EXT_COST_REQUIRES_GOLD || !ExtendedCost; } + bool IsGoldRequired(ItemTemplate const* pProto) const { return (pProto->Flags2 & ITEM_FLAG2_DONT_IGNORE_BUY_PRICE) || !ExtendedCost; } }; typedef std::vector<VendorItem*> VendorItemList; @@ -691,6 +691,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma // Handling caster facing during spellcast void SetTarget(ObjectGuid guid) override; void MustReacquireTarget() { m_shouldReacquireTarget = true; } // flags the Creature for forced (client displayed) target reacquisition in the next ::Update call + void DoNotReacquireTarget() { m_shouldReacquireTarget = false; m_suppressedTarget = ObjectGuid::Empty; m_suppressedOrientation = 0.0f; } void FocusTarget(Spell const* focusSpell, WorldObject const* target); bool IsFocusing(Spell const* focusSpell = nullptr, bool withDelay = false); void ReleaseFocus(Spell const* focusSpell = nullptr, bool withDelay = true); diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 8c43d287c91..3869c8be56d 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -310,6 +310,18 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u if (map->Is25ManRaid()) loot.maxDuplicates = 3; + if (uint32 linkedEntry = GetGOInfo()->GetLinkedGameObjectEntry()) + { + GameObject* linkedGO = new GameObject(); + if (linkedGO->Create(map->GenerateLowGuid<HighGuid::GameObject>(), linkedEntry, map, phaseMask, pos, rotation, 255, GO_STATE_READY)) + { + SetLinkedTrap(linkedGO); + map->AddToMap(linkedGO); + } + else + delete linkedGO; + } + return true; } @@ -428,6 +440,10 @@ void GameObject::Update(uint32 diff) m_SkillupList.clear(); m_usetimes = 0; + // If nearby linked trap exists, respawn it + if (GameObject* linkedTrap = GetLinkedTrap()) + linkedTrap->SetLootState(GO_READY); + switch (GetGoType()) { case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now @@ -511,11 +527,9 @@ void GameObject::Update(uint32 diff) if (Unit* owner = GetOwner()) { // Hunter trap: Search units which are unfriendly to the trap's owner - Trinity::AnyUnfriendlyNoTotemUnitInObjectRangeCheck checker(this, owner, radius); - Trinity::UnitSearcher<Trinity::AnyUnfriendlyNoTotemUnitInObjectRangeCheck> searcher(this, target, checker); - VisitNearbyGridObject(radius, searcher); - if (!target) - VisitNearbyWorldObject(radius, searcher); + Trinity::NearestUnfriendlyNoTotemUnitInObjectRangeCheck checker(this, owner, radius); + Trinity::UnitLastSearcher<Trinity::NearestUnfriendlyNoTotemUnitInObjectRangeCheck> searcher(this, target, checker); + VisitNearbyObject(radius, searcher); } else { @@ -613,6 +627,10 @@ void GameObject::Update(uint32 diff) } case GO_JUST_DEACTIVATED: { + // If nearby linked trap exists, despawn it + if (GameObject* linkedTrap = GetLinkedTrap()) + linkedTrap->SetLootState(GO_JUST_DEACTIVATED); + //if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed if (GetGoType() == GAMEOBJECT_TYPE_GOOBER) { @@ -1828,6 +1846,9 @@ void GameObject::CastSpell(Unit* target, uint32 spellId, TriggerCastFlags trigge if (!trigger) return; + // remove immunity flags, to allow spell to target anything + trigger->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC); + if (Unit* owner = GetOwner()) { trigger->setFaction(owner->getFaction()); @@ -1839,7 +1860,7 @@ void GameObject::CastSpell(Unit* target, uint32 spellId, TriggerCastFlags trigge } else { - trigger->setFaction(14); + trigger->setFaction(spellInfo->IsPositive() ? 35 : 14); // Set owner guid for target if no owner available - needed by trigger auras // - trigger gets despawned and there's no caster avalible (see AuraEffect::TriggerSpell()) trigger->CastSpell(target ? target : trigger, spellInfo, triggered, nullptr, nullptr, target ? target->GetGUID() : ObjectGuid::Empty); @@ -2231,6 +2252,11 @@ bool GameObject::IsLootAllowedFor(Player const* player) const return true; } +GameObject* GameObject::GetLinkedTrap() +{ + return ObjectAccessor::GetGameObject(*this, m_linkedTrap); +} + void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) const { if (!target) diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 85b37abaf3c..dc7d99bb622 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -495,6 +495,7 @@ struct GameObjectTemplate { switch (type) { + case GAMEOBJECT_TYPE_BUTTON: return button.linkedTrap; case GAMEOBJECT_TYPE_CHEST: return chest.linkedTrapId; case GAMEOBJECT_TYPE_SPELL_FOCUS: return spellFocus.linkedTrapId; case GAMEOBJECT_TYPE_GOOBER: return goober.linkedTrapId; @@ -818,6 +819,9 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> uint32 m_groupLootTimer; // (msecs)timer used for group loot ObjectGuid::LowType lootingGroupLowGUID; // used to find group which is looting + GameObject* GetLinkedTrap(); + void SetLinkedTrap(GameObject* linkedTrap) { m_linkedTrap = linkedTrap->GetGUID(); } + bool hasQuest(uint32 quest_id) const override; bool hasInvolvedQuest(uint32 quest_id) const override; bool ActivateToQuest(Player* target) const; @@ -925,6 +929,9 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> ObjectGuid m_lootRecipient; uint32 m_lootRecipientGroup; uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable + + ObjectGuid m_linkedTrap; + private: void RemoveFromOwner(); void SwitchDoorOrButton(bool activate, bool alternative = false); diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index a8dad0b2fbc..9d60eb62be6 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -256,11 +256,11 @@ Item::Item() m_paidExtendedCost = 0; } -bool Item::Create(ObjectGuid::LowType guidlow, uint32 itemid, Player const* owner) +bool Item::Create(ObjectGuid::LowType guidlow, uint32 itemId, Player const* owner) { Object::_Create(guidlow, 0, HighGuid::Item); - SetEntry(itemid); + SetEntry(itemId); SetObjectScale(1.0f); if (owner) @@ -269,7 +269,7 @@ bool Item::Create(ObjectGuid::LowType guidlow, uint32 itemid, Player const* owne SetGuidValue(ITEM_FIELD_CONTAINED, owner->GetGUID()); } - ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(itemid); + ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(itemId); if (!itemProto) return false; @@ -357,7 +357,7 @@ void Item::SaveToDB(SQLTransaction& trans) trans->Append(stmt); - if ((uState == ITEM_CHANGED) && HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) + if ((uState == ITEM_CHANGED) && HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED)) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GIFT_OWNER); stmt->setUInt32(0, GetOwnerGUID().GetCounter()); @@ -372,7 +372,7 @@ void Item::SaveToDB(SQLTransaction& trans) stmt->setUInt32(0, guid); trans->Append(stmt); - if (HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) + if (HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED)) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT); stmt->setUInt32(0, guid); @@ -443,7 +443,7 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi // Remove bind flag for items vs NO_BIND set if (IsSoulBound() && proto->Bonding == NO_BIND) { - ApplyModFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_SOULBOUND, false); + ApplyModFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_SOULBOUND, false); need_save = true; } @@ -746,7 +746,7 @@ bool Item::CanBeTraded(bool mail, bool trade) const if (m_lootGenerated) return false; - if ((!mail || !IsBoundAccountWide()) && (IsSoulBound() && (!HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE) || !trade))) + if ((!mail || !IsBoundAccountWide()) && (IsSoulBound() && (!HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE) || !trade))) return false; if (IsBag() && (Player::IsBagPos(GetPos()) || !((Bag const*)this)->IsEmpty())) @@ -1017,10 +1017,10 @@ void Item::SendTimeUpdate(Player* owner) owner->GetSession()->SendPacket(&data); } -Item* Item::CreateItem(uint32 itemEntry, uint32 count, Player const* player) +Item* Item::CreateItem(uint32 itemEntry, uint32 count, Player const* player /*= nullptr*/) { if (count < 1) - return NULL; //don't create item at zero count + return nullptr; //don't create item at zero count ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry); if (proto) @@ -1041,18 +1041,18 @@ Item* Item::CreateItem(uint32 itemEntry, uint32 count, Player const* player) } else ABORT(); - return NULL; + return nullptr; } -Item* Item::CloneItem(uint32 count, Player const* player) const +Item* Item::CloneItem(uint32 count, Player const* player /*= nullptr*/) const { Item* newItem = CreateItem(GetEntry(), count, player); if (!newItem) - return NULL; + return nullptr; newItem->SetGuidValue(ITEM_FIELD_CREATOR, GetGuidValue(ITEM_FIELD_CREATOR)); newItem->SetGuidValue(ITEM_FIELD_GIFTCREATOR, GetGuidValue(ITEM_FIELD_GIFTCREATOR)); - newItem->SetUInt32Value(ITEM_FIELD_FLAGS, GetUInt32Value(ITEM_FIELD_FLAGS) & ~(ITEM_FLAG_REFUNDABLE | ITEM_FLAG_BOP_TRADEABLE)); + newItem->SetUInt32Value(ITEM_FIELD_FLAGS, GetUInt32Value(ITEM_FIELD_FLAGS) & ~(ITEM_FIELD_FLAG_REFUNDABLE | ITEM_FIELD_FLAG_BOP_TRADEABLE)); newItem->SetUInt32Value(ITEM_FIELD_DURATION, GetUInt32Value(ITEM_FIELD_DURATION)); // player CAN be NULL in which case we must not update random properties because that accesses player's item update queue if (player) @@ -1070,7 +1070,7 @@ bool Item::IsBindedNotWith(Player const* player) const if (GetOwnerGUID() == player->GetGUID()) return false; - if (HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE)) + if (HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE)) if (allowedGUIDs.find(player->GetGUID().GetCounter()) != allowedGUIDs.end()) return false; @@ -1131,10 +1131,10 @@ void Item::DeleteRefundDataFromDB(SQLTransaction* trans) void Item::SetNotRefundable(Player* owner, bool changestate /*=true*/, SQLTransaction* trans /*=NULL*/) { - if (!HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE)) + if (!HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE)) return; - RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE); + RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE); // Following is not applicable in the trading procedure if (changestate) SetState(ITEM_CHANGED, owner); @@ -1189,13 +1189,13 @@ bool Item::IsRefundExpired() void Item::SetSoulboundTradeable(AllowedLooterSet const& allowedLooters) { - SetFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE); + SetFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE); allowedGUIDs = allowedLooters; } void Item::ClearSoulboundTradeable(Player* currentOwner) { - RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE); + RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE); if (allowedGUIDs.empty()) return; @@ -1346,7 +1346,7 @@ bool Item::ItemContainerLoadLootFromDB() // If container item is in a bag, add that player as an allowed looter if (GetBagSlot()) - loot_item.allowedGUIDs.insert(GetOwner()->GetGUID().GetCounter()); + loot_item.AddAllowedLooter(GetOwner()); // Finally add the LootItem to the container loot.items.push_back(loot_item); diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 5e00a816cab..edc7001e1c4 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -206,12 +206,12 @@ bool ItemCanGoIntoBag(ItemTemplate const* proto, ItemTemplate const* pBagProto); class TC_GAME_API Item : public Object { public: - static Item* CreateItem(uint32 itemEntry, uint32 count, Player const* player = NULL); - Item* CloneItem(uint32 count, Player const* player = NULL) const; + static Item* CreateItem(uint32 itemEntry, uint32 count, Player const* player = nullptr); + Item* CloneItem(uint32 count, Player const* player = nullptr) const; Item(); - virtual bool Create(ObjectGuid::LowType guidlow, ObjectGuid::LowType itemid, Player const* owner); + virtual bool Create(ObjectGuid::LowType guidlow, uint32 itemId, Player const* owner); ItemTemplate const* GetTemplate() const; @@ -219,9 +219,9 @@ class TC_GAME_API Item : public Object void SetOwnerGUID(ObjectGuid guid) { SetGuidValue(ITEM_FIELD_OWNER, guid); } Player* GetOwner()const; - void SetBinding(bool val) { ApplyModFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_SOULBOUND, val); } - bool IsSoulBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_SOULBOUND); } - bool IsBoundAccountWide() const { return (GetTemplate()->Flags & ITEM_PROTO_FLAG_BIND_TO_ACCOUNT) != 0; } + void SetBinding(bool val) { ApplyModFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_SOULBOUND, val); } + bool IsSoulBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_SOULBOUND); } + bool IsBoundAccountWide() const { return (GetTemplate()->Flags & ITEM_FLAG_IS_BOUND_TO_ACCOUNT) != 0; } bool IsBindedNotWith(Player const* player) const; bool IsBoundByEnchant() const; virtual void SaveToDB(SQLTransaction& trans); @@ -245,7 +245,7 @@ class TC_GAME_API Item : public Object Bag* ToBag() { if (IsBag()) return reinterpret_cast<Bag*>(this); else return NULL; } const Bag* ToBag() const { if (IsBag()) return reinterpret_cast<const Bag*>(this); else return NULL; } - bool IsLocked() const { return !HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_UNLOCKED); } + bool IsLocked() const { return !HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_UNLOCKED); } bool IsBag() const { return GetTemplate()->InventoryType == INVTYPE_BAG; } bool IsCurrencyToken() const { return GetTemplate()->IsCurrencyToken(); } bool IsNotEmptyBag() const; diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index 0ff3f00a08b..8cc408b47fb 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -103,88 +103,117 @@ enum ItemBondingType #define MAX_BIND_TYPE 6 /* /// @todo: Requiring actual cases in which using (an) item isn't allowed while shapeshifted. Else, this flag would need an implementation. - ITEM_PROTO_FLAG_USABLE_WHEN_SHAPESHIFTED = 0x00800000, // Item can be used in shapeshift forms */ - -enum ItemProtoFlags -{ - ITEM_PROTO_FLAG_NO_PICKUP = 0x00000001, // ? - ITEM_PROTO_FLAG_CONJURED = 0x00000002, // Conjured item - ITEM_PROTO_FLAG_HAS_LOOT = 0x00000004, // Item can be right clicked to open for loot - ITEM_PROTO_FLAG_HEROIC = 0x00000008, // Makes green "Heroic" text appear on item - ITEM_PROTO_FLAG_DEPRECATED = 0x00000010, // Cannot equip or use - ITEM_PROTO_FLAG_INDESTRUCTIBLE = 0x00000020, // Item can not be destroyed, except by using spell (item can be reagent for spell) - ITEM_PROTO_FLAG_PLAYER_CAST = 0x00000040, // Item's spells are castable by players - ITEM_PROTO_FLAG_NO_EQUIP_COOLDOWN = 0x00000080, // No default 30 seconds cooldown when equipped - ITEM_PROTO_FLAG_INT_BONUS_INSTEAD = 0x00000100, // ? - ITEM_PROTO_FLAG_IS_WRAPPER = 0x00000200, // Item can wrap other items - ITEM_PROTO_FLAG_USES_RESOURCES = 0x00000400, // ? - ITEM_PROTO_FLAG_MULTI_DROP = 0x00000800, // Looting this item does not remove it from available loot - ITEM_PROTO_FLAG_REFUNDABLE = 0x00001000, // Item can be returned to vendor for its original cost (extended cost) - ITEM_PROTO_FLAG_PETITION = 0x00002000, // Item is guild or arena charter - ITEM_PROTO_FLAG_UNK5 = 0x00004000, // Only readable items have this (but not all) - ITEM_PROTO_FLAG_UNK6 = 0x00008000, // ? - ITEM_PROTO_FLAG_UNK7 = 0x00010000, // ? - ITEM_PROTO_FLAG_UNK8 = 0x00020000, // ? - ITEM_PROTO_FLAG_PROSPECTABLE = 0x00040000, // Item can be prospected - ITEM_PROTO_FLAG_UNIQUE_EQUIPPED = 0x00080000, // You can only equip one of these - ITEM_PROTO_FLAG_UNK9 = 0x00100000, // ? - ITEM_PROTO_FLAG_USEABLE_IN_ARENA = 0x00200000, // Item can be used during arena match - ITEM_PROTO_FLAG_THROWABLE = 0x00400000, // Some Thrown weapons have it (and only Thrown) but not all - ITEM_PROTO_FLAG_USABLE_WHEN_SHAPESHIFTED = 0x00800000, // Item can be used in shapeshift forms - ITEM_PROTO_FLAG_UNK10 = 0x01000000, // ? - ITEM_PROTO_FLAG_SMART_LOOT = 0x02000000, // Profession recipes: can only be looted if you meet requirements and don't already know it - ITEM_PROTO_FLAG_NOT_USEABLE_IN_ARENA = 0x04000000, // Item cannot be used in arena - ITEM_PROTO_FLAG_BIND_TO_ACCOUNT = 0x08000000, // Item binds to account and can be sent only to your own characters - ITEM_PROTO_FLAG_TRIGGERED_CAST = 0x10000000, // Spell is cast with triggered flag - ITEM_PROTO_FLAG_MILLABLE = 0x20000000, // Item can be milled - ITEM_PROTO_FLAG_UNK11 = 0x40000000, // ? - ITEM_PROTO_FLAG_UNK12 = 0x80000000 // ? -}; - -enum ItemFieldFlags -{ - ITEM_FLAG_SOULBOUND = 0x00000001, // Item is soulbound and cannot be traded <<-- - ITEM_FLAG_UNK1 = 0x00000002, // ? - ITEM_FLAG_UNLOCKED = 0x00000004, // Item had lock but can be opened now - ITEM_FLAG_WRAPPED = 0x00000008, // Item is wrapped and contains another item - ITEM_FLAG_UNK2 = 0x00000010, // ? - ITEM_FLAG_UNK3 = 0x00000020, // ? - ITEM_FLAG_UNK4 = 0x00000040, // ? - ITEM_FLAG_UNK5 = 0x00000080, // ? - ITEM_FLAG_BOP_TRADEABLE = 0x00000100, // Allows trading soulbound items - ITEM_FLAG_READABLE = 0x00000200, // Opens text page when right clicked - ITEM_FLAG_UNK6 = 0x00000400, // ? - ITEM_FLAG_UNK7 = 0x00000800, // ? - ITEM_FLAG_REFUNDABLE = 0x00001000, // Item can be returned to vendor for its original cost (extended cost) - ITEM_FLAG_UNK8 = 0x00002000, // ? - ITEM_FLAG_UNK9 = 0x00004000, // ? - ITEM_FLAG_UNK10 = 0x00008000, // ? - ITEM_FLAG_UNK11 = 0x00010000, // ? - ITEM_FLAG_UNK12 = 0x00020000, // ? - ITEM_FLAG_UNK13 = 0x00040000, // ? - ITEM_FLAG_UNK14 = 0x00080000, // ? - ITEM_FLAG_UNK15 = 0x00100000, // ? - ITEM_FLAG_UNK16 = 0x00200000, // ? - ITEM_FLAG_UNK17 = 0x00400000, // ? - ITEM_FLAG_UNK18 = 0x00800000, // ? - ITEM_FLAG_UNK19 = 0x01000000, // ? - ITEM_FLAG_UNK20 = 0x02000000, // ? - ITEM_FLAG_UNK21 = 0x04000000, // ? - ITEM_FLAG_UNK22 = 0x08000000, // ? - ITEM_FLAG_UNK23 = 0x10000000, // ? - ITEM_FLAG_UNK24 = 0x20000000, // ? - ITEM_FLAG_UNK25 = 0x40000000, // ? - ITEM_FLAG_UNK26 = 0x80000000, // ? - - ITEM_FLAG_MAIL_TEXT_MASK = ITEM_FLAG_READABLE | ITEM_FLAG_UNK13 | ITEM_FLAG_UNK14 -}; - -enum ItemFlagsExtra -{ - ITEM_FLAGS_EXTRA_HORDE_ONLY = 0x00000001, - ITEM_FLAGS_EXTRA_ALLIANCE_ONLY = 0x00000002, - ITEM_FLAGS_EXTRA_EXT_COST_REQUIRES_GOLD = 0x00000004, // when item uses extended cost, gold is also required - ITEM_FLAGS_EXTRA_NEED_ROLL_DISABLED = 0x00000100 + ITEM_FLAG_USE_WHEN_SHAPESHIFTED = 0x00800000, // Item can be used in shapeshift forms */ + +// ITEM_FIELD_FLAGS +enum ItemFieldFlags : uint32 +{ + ITEM_FIELD_FLAG_SOULBOUND = 0x00000001, // Item is soulbound and cannot be traded <<-- + ITEM_FIELD_FLAG_UNK1 = 0x00000002, // ? + ITEM_FIELD_FLAG_UNLOCKED = 0x00000004, // Item had lock but can be opened now + ITEM_FIELD_FLAG_WRAPPED = 0x00000008, // Item is wrapped and contains another item + ITEM_FIELD_FLAG_UNK2 = 0x00000010, // ? + ITEM_FIELD_FLAG_UNK3 = 0x00000020, // ? + ITEM_FIELD_FLAG_UNK4 = 0x00000040, // ? + ITEM_FIELD_FLAG_UNK5 = 0x00000080, // ? + ITEM_FIELD_FLAG_BOP_TRADEABLE = 0x00000100, // Allows trading soulbound items + ITEM_FIELD_FLAG_READABLE = 0x00000200, // Opens text page when right clicked + ITEM_FIELD_FLAG_UNK6 = 0x00000400, // ? + ITEM_FIELD_FLAG_UNK7 = 0x00000800, // ? + ITEM_FIELD_FLAG_REFUNDABLE = 0x00001000, // Item can be returned to vendor for its original cost (extended cost) + ITEM_FIELD_FLAG_UNK8 = 0x00002000, // ? + ITEM_FIELD_FLAG_UNK9 = 0x00004000, // ? + ITEM_FIELD_FLAG_UNK10 = 0x00008000, // ? + ITEM_FIELD_FLAG_UNK11 = 0x00010000, // ? + ITEM_FIELD_FLAG_UNK12 = 0x00020000, // ? + ITEM_FIELD_FLAG_UNK13 = 0x00040000, // ? + ITEM_FIELD_FLAG_UNK14 = 0x00080000, // ? + ITEM_FIELD_FLAG_UNK15 = 0x00100000, // ? + ITEM_FIELD_FLAG_UNK16 = 0x00200000, // ? + ITEM_FIELD_FLAG_UNK17 = 0x00400000, // ? + ITEM_FIELD_FLAG_UNK18 = 0x00800000, // ? + ITEM_FIELD_FLAG_UNK19 = 0x01000000, // ? + ITEM_FIELD_FLAG_UNK20 = 0x02000000, // ? + ITEM_FIELD_FLAG_UNK21 = 0x04000000, // ? + ITEM_FIELD_FLAG_UNK22 = 0x08000000, // ? + ITEM_FIELD_FLAG_UNK23 = 0x10000000, // ? + ITEM_FIELD_FLAG_UNK24 = 0x20000000, // ? + ITEM_FIELD_FLAG_UNK25 = 0x40000000, // ? + ITEM_FIELD_FLAG_UNK26 = 0x80000000, // ? + + ITEM_FLAG_MAIL_TEXT_MASK = ITEM_FIELD_FLAG_READABLE | ITEM_FIELD_FLAG_UNK13 | ITEM_FIELD_FLAG_UNK14 +}; + +enum ItemFlags : uint32 +{ + ITEM_FLAG_NO_PICKUP = 0x00000001, + ITEM_FLAG_CONJURED = 0x00000002, // Conjured item + ITEM_FLAG_HAS_LOOT = 0x00000004, // Item can be right clicked to open for loot + ITEM_FLAG_HEROIC_TOOLTIP = 0x00000008, // Makes green "Heroic" text appear on item + ITEM_FLAG_DEPRECATED = 0x00000010, // Cannot equip or use + ITEM_FLAG_NO_USER_DESTROY = 0x00000020, // Item can not be destroyed, except by using spell (item can be reagent for spell) + ITEM_FLAG_PLAYERCAST = 0x00000040, // Item's spells are castable by players + ITEM_FLAG_NO_EQUIP_COOLDOWN = 0x00000080, // No default 30 seconds cooldown when equipped + ITEM_FLAG_MULTI_LOOT_QUEST = 0x00000100, + ITEM_FLAG_IS_WRAPPER = 0x00000200, // Item can wrap other items + ITEM_FLAG_USES_RESOURCES = 0x00000400, + ITEM_FLAG_MULTI_DROP = 0x00000800, // Looting this item does not remove it from available loot + ITEM_FLAG_ITEM_PURCHASE_RECORD = 0x00001000, // Item can be returned to vendor for its original cost (extended cost) + ITEM_FLAG_PETITION = 0x00002000, // Item is guild or arena charter + ITEM_FLAG_HAS_TEXT = 0x00004000, // Only readable items have this (but not all) + ITEM_FLAG_NO_DISENCHANT = 0x00008000, + ITEM_FLAG_REAL_DURATION = 0x00010000, + ITEM_FLAG_NO_CREATOR = 0x00020000, + ITEM_FLAG_IS_PROSPECTABLE = 0x00040000, // Item can be prospected + ITEM_FLAG_UNIQUE_EQUIPPABLE = 0x00080000, // You can only equip one of these + ITEM_FLAG_IGNORE_FOR_AURAS = 0x00100000, + ITEM_FLAG_IGNORE_DEFAULT_ARENA_RESTRICTIONS = 0x00200000, // Item can be used during arena match + ITEM_FLAG_NO_DURABILITY_LOSS = 0x00400000, // Some Thrown weapons have it (and only Thrown) but not all + ITEM_FLAG_USE_WHEN_SHAPESHIFTED = 0x00800000, // Item can be used in shapeshift forms + ITEM_FLAG_HAS_QUEST_GLOW = 0x01000000, + ITEM_FLAG_HIDE_UNUSABLE_RECIPE = 0x02000000, // Profession recipes: can only be looted if you meet requirements and don't already know it + ITEM_FLAG_NOT_USEABLE_IN_ARENA = 0x04000000, // Item cannot be used in arena + ITEM_FLAG_IS_BOUND_TO_ACCOUNT = 0x08000000, // Item binds to account and can be sent only to your own characters + ITEM_FLAG_NO_REAGENT_COST = 0x10000000, // Spell is cast ignoring reagents + ITEM_FLAG_IS_MILLABLE = 0x20000000, // Item can be milled + ITEM_FLAG_REPORT_TO_GUILD_CHAT = 0x40000000, + ITEM_FLAG_NO_PROGRESSIVE_LOOT = 0x80000000 +}; + +enum ItemFlags2 : uint32 +{ + ITEM_FLAG2_FACTION_HORDE = 0x00000001, + ITEM_FLAG2_FACTION_ALLIANCE = 0x00000002, + ITEM_FLAG2_DONT_IGNORE_BUY_PRICE = 0x00000004, // when item uses extended cost, gold is also required + ITEM_FLAG2_CLASSIFY_AS_CASTER = 0x00000008, + ITEM_FLAG2_CLASSIFY_AS_PHYSICAL = 0x00000010, + ITEM_FLAG2_EVERYONE_CAN_ROLL_NEED = 0x00000020, + ITEM_FLAG2_NO_TRADE_BIND_ON_ACQUIRE = 0x00000040, + ITEM_FLAG2_CAN_TRADE_BIND_ON_ACQUIRE = 0x00000080, + ITEM_FLAG2_CAN_ONLY_ROLL_GREED = 0x00000100, + ITEM_FLAG2_CASTER_WEAPON = 0x00000200, + ITEM_FLAG2_DELETE_ON_LOGIN = 0x00000400, + ITEM_FLAG2_INTERNAL_ITEM = 0x00000800, + ITEM_FLAG2_NO_VENDOR_VALUE = 0x00001000, + ITEM_FLAG2_SHOW_BEFORE_DISCOVERED = 0x00002000, + ITEM_FLAG2_OVERRIDE_GOLD_COST = 0x00004000, + ITEM_FLAG2_IGNORE_DEFAULT_RATED_BG_RESTRICTIONS = 0x00008000, + ITEM_FLAG2_NOT_USABLE_IN_RATED_BG = 0x00010000, + ITEM_FLAG2_BNET_ACCOUNT_TRADE_OK = 0x00020000, + ITEM_FLAG2_CONFIRM_BEFORE_USE = 0x00040000, + ITEM_FLAG2_REEVALUATE_BONDING_ON_TRANSFORM = 0x00080000, + ITEM_FLAG2_NO_TRANSFORM_ON_CHARGE_DEPLETION = 0x00100000, + ITEM_FLAG2_NO_ALTER_ITEM_VISUAL = 0x00200000, + ITEM_FLAG2_NO_SOURCE_FOR_ITEM_VISUAL = 0x00400000, + ITEM_FLAG2_IGNORE_QUALITY_FOR_ITEM_VISUAL_SOURCE = 0x00800000, + ITEM_FLAG2_NO_DURABILITY = 0x01000000, + ITEM_FLAG2_ROLE_TANK = 0x02000000, + ITEM_FLAG2_ROLE_HEALER = 0x04000000, + ITEM_FLAG2_ROLE_DAMAGE = 0x08000000, + ITEM_FLAG2_CAN_DROP_IN_CHALLENGE_MODE = 0x10000000, + ITEM_FLAG2_NEVER_STACK_IN_LOOT_UI = 0x20000000, + ITEM_FLAG2_DISENCHANT_TO_LOOT_TABLE = 0x40000000, + ITEM_FLAG2_USED_IN_A_TRADESKILL = 0x80000000 }; enum ItemFlagsCustom @@ -674,7 +703,7 @@ struct ItemTemplate bool IsPotion() const { return Class == ITEM_CLASS_CONSUMABLE && SubClass == ITEM_SUBCLASS_POTION; } bool IsWeaponVellum() const { return Class == ITEM_CLASS_TRADE_GOODS && SubClass == ITEM_SUBCLASS_WEAPON_ENCHANTMENT; } bool IsArmorVellum() const { return Class == ITEM_CLASS_TRADE_GOODS && SubClass == ITEM_SUBCLASS_ARMOR_ENCHANTMENT; } - bool IsConjuredConsumable() const { return Class == ITEM_CLASS_CONSUMABLE && (Flags & ITEM_PROTO_FLAG_CONJURED); } + bool IsConjuredConsumable() const { return Class == ITEM_CLASS_CONSUMABLE && (Flags & ITEM_FLAG_CONJURED); } }; // Benchmarked: Faster than std::map (insert/find) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index aea923f6eac..f7d4fb38cee 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -2116,114 +2116,42 @@ GameObject* WorldObject::FindNearestGameObjectOfType(GameobjectTypes type, float return go; } -void WorldObject::GetGameObjectListWithEntryInGrid(std::list<GameObject*>& gameobjectList, uint32 entry, float maxSearchRange) const +template <typename Container> +void WorldObject::GetGameObjectListWithEntryInGrid(Container& gameObjectContainer, uint32 entry, float maxSearchRange /*= 250.0f*/) const { - CellCoord pair(Trinity::ComputeCellCoord(this->GetPositionX(), this->GetPositionY())); + CellCoord pair(Trinity::ComputeCellCoord(GetPositionX(), GetPositionY())); Cell cell(pair); cell.SetNoCreate(); Trinity::AllGameObjectsWithEntryInRange check(this, entry, maxSearchRange); - Trinity::GameObjectListSearcher<Trinity::AllGameObjectsWithEntryInRange> searcher(this, gameobjectList, check); + Trinity::GameObjectListSearcher<Trinity::AllGameObjectsWithEntryInRange> searcher(this, gameObjectContainer, check); TypeContainerVisitor<Trinity::GameObjectListSearcher<Trinity::AllGameObjectsWithEntryInRange>, GridTypeMapContainer> visitor(searcher); - cell.Visit(pair, visitor, *(this->GetMap()), *this, maxSearchRange); + cell.Visit(pair, visitor, *GetMap(), *this, maxSearchRange); } -void WorldObject::GetCreatureListWithEntryInGrid(std::list<Creature*>& creatureList, uint32 entry, float maxSearchRange) const +template <typename Container> +void WorldObject::GetCreatureListWithEntryInGrid(Container& creatureContainer, uint32 entry, float maxSearchRange /*= 250.0f*/) const { - CellCoord pair(Trinity::ComputeCellCoord(this->GetPositionX(), this->GetPositionY())); + CellCoord pair(Trinity::ComputeCellCoord(GetPositionX(), GetPositionY())); Cell cell(pair); cell.SetNoCreate(); Trinity::AllCreaturesOfEntryInRange check(this, entry, maxSearchRange); - Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange> searcher(this, creatureList, check); + Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange> searcher(this, creatureContainer, check); TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange>, GridTypeMapContainer> visitor(searcher); - cell.Visit(pair, visitor, *(this->GetMap()), *this, maxSearchRange); + cell.Visit(pair, visitor, *GetMap(), *this, maxSearchRange); } -void WorldObject::GetPlayerListInGrid(std::list<Player*>& playerList, float maxSearchRange) const +template <typename Container> +void WorldObject::GetPlayerListInGrid(Container& playerContainer, float maxSearchRange) const { Trinity::AnyPlayerInObjectRangeCheck checker(this, maxSearchRange); - Trinity::PlayerListSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(this, playerList, checker); - this->VisitNearbyWorldObject(maxSearchRange, searcher); + Trinity::PlayerListSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(this, playerContainer, checker); + VisitNearbyWorldObject(maxSearchRange, searcher); } -/* -namespace Trinity -{ - class NearUsedPosDo - { - public: - NearUsedPosDo(WorldObject const& obj, WorldObject const* searcher, float angle, ObjectPosSelector& selector) - : i_object(obj), i_searcher(searcher), i_angle(angle), i_selector(selector) { } - - void operator()(Corpse*) const { } - void operator()(DynamicObject*) const { } - - void operator()(Creature* c) const - { - // skip self or target - if (c == i_searcher || c == &i_object) - return; - - float x, y, z; - - if (!c->IsAlive() || c->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED) || - !c->GetMotionMaster()->GetDestination(x, y, z)) - { - x = c->GetPositionX(); - y = c->GetPositionY(); - } - - add(c, x, y); - } - - template<class T> - void operator()(T* u) const - { - // skip self or target - if (u == i_searcher || u == &i_object) - return; - - float x, y; - - x = u->GetPositionX(); - y = u->GetPositionY(); - - add(u, x, y); - } - - // we must add used pos that can fill places around center - void add(WorldObject* u, float x, float y) const - { - // u is too nearest/far away to i_object - if (!i_object.IsInRange2d(x, y, i_selector.m_dist - i_selector.m_size, i_selector.m_dist + i_selector.m_size)) - return; - - float angle = i_object.GetAngle(u)-i_angle; - - // move angle to range -pi ... +pi - while (angle > M_PI) - angle -= 2.0f * M_PI; - while (angle < -M_PI) - angle += 2.0f * M_PI; - - // dist include size of u - float dist2d = i_object.GetDistance2d(x, y); - i_selector.AddUsedPos(u->GetObjectSize(), angle, dist2d + i_object.GetObjectSize()); - } - private: - WorldObject const& i_object; - WorldObject const* i_searcher; - float i_angle; - ObjectPosSelector& i_selector; - }; -} // namespace Trinity -*/ - -//=================================================================================================== - void WorldObject::GetNearPoint2D(float &x, float &y, float distance2d, float absAngle) const { x = GetPositionX() + (GetObjectSize() + distance2d) * std::cos(absAngle); @@ -2623,3 +2551,15 @@ ObjectGuid WorldObject::GetTransGUID() const return GetTransport()->GetGUID(); return ObjectGuid::Empty; } + +template TC_GAME_API void WorldObject::GetGameObjectListWithEntryInGrid(std::list<GameObject*>&, uint32, float) const; +template TC_GAME_API void WorldObject::GetGameObjectListWithEntryInGrid(std::deque<GameObject*>&, uint32, float) const; +template TC_GAME_API void WorldObject::GetGameObjectListWithEntryInGrid(std::vector<GameObject*>&, uint32, float) const; + +template TC_GAME_API void WorldObject::GetCreatureListWithEntryInGrid(std::list<Creature*>&, uint32, float) const; +template TC_GAME_API void WorldObject::GetCreatureListWithEntryInGrid(std::deque<Creature*>&, uint32, float) const; +template TC_GAME_API void WorldObject::GetCreatureListWithEntryInGrid(std::vector<Creature*>&, uint32, float) const; + +template TC_GAME_API void WorldObject::GetPlayerListInGrid(std::list<Player*>&, float) const; +template TC_GAME_API void WorldObject::GetPlayerListInGrid(std::deque<Player*>&, float) const; +template TC_GAME_API void WorldObject::GetPlayerListInGrid(std::vector<Player*>&, float) const; diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 2fdb9433356..e107a5f6bfd 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -558,9 +558,14 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation GameObject* FindNearestGameObject(uint32 entry, float range) const; GameObject* FindNearestGameObjectOfType(GameobjectTypes type, float range) const; - void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry = 0, float fMaxSearchRange = 250.0f) const; - void GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry = 0, float fMaxSearchRange = 250.0f) const; - void GetPlayerListInGrid(std::list<Player*>& lList, float fMaxSearchRange) const; + template <typename Container> + void GetGameObjectListWithEntryInGrid(Container& gameObjectContainer, uint32 entry, float maxSearchRange = 250.0f) const; + + template <typename Container> + void GetCreatureListWithEntryInGrid(Container& creatureContainer, uint32 entry, float maxSearchRange = 250.0f) const; + + template <typename Container> + void GetPlayerListInGrid(Container& playerContainer, float maxSearchRange) const; void DestroyForNearbyPlayers(); virtual void UpdateObjectVisibility(bool forced = true); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index ac8c3696530..41d9bf7b2b2 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -939,12 +939,21 @@ uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage) // Absorb, resist some environmental damage type uint32 absorb = 0; uint32 resist = 0; - if (type == DAMAGE_LAVA) - CalcAbsorbResist(this, SPELL_SCHOOL_MASK_FIRE, DIRECT_DAMAGE, damage, &absorb, &resist); - else if (type == DAMAGE_SLIME) - CalcAbsorbResist(this, SPELL_SCHOOL_MASK_NATURE, DIRECT_DAMAGE, damage, &absorb, &resist); - - damage -= absorb + resist; + switch (type) + { + case DAMAGE_LAVA: + case DAMAGE_SLIME: + { + DamageInfo dmgInfo(this, this, damage, nullptr, type == DAMAGE_LAVA ? SPELL_SCHOOL_MASK_FIRE : SPELL_SCHOOL_MASK_NATURE, DIRECT_DAMAGE, BASE_ATTACK); + CalcAbsorbResist(dmgInfo); + absorb = dmgInfo.GetAbsorb(); + resist = dmgInfo.GetResist(); + damage = dmgInfo.GetDamage(); + break; + } + default: + break; + } DealDamageMods(this, damage, &absorb); @@ -10140,7 +10149,7 @@ bool Player::HasItemOrGemWithIdEquipped(uint32 item, uint32 count, uint8 except_ return false; } -bool Player::HasItemOrGemWithLimitCategoryEquipped(uint32 limitCategory, uint32 count, uint8 except_slot) const +bool Player::HasItemWithLimitCategoryEquipped(uint32 limitCategory, uint32 count, uint8 except_slot) const { uint32 tempcount = 0; for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) @@ -10162,6 +10171,26 @@ bool Player::HasItemOrGemWithLimitCategoryEquipped(uint32 limitCategory, uint32 if (tempcount >= count) return true; } + } + + return false; +} + +bool Player::HasGemWithLimitCategoryEquipped(uint32 limitCategory, uint32 count, uint8 except_slot) const +{ + uint32 tempcount = 0; + for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) + { + if (i == except_slot) + continue; + + Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if (!pItem) + continue; + + ItemTemplate const* pProto = pItem->GetTemplate(); + if (!pProto) + continue; if (pProto->Socket[0].Color || pItem->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT)) { @@ -11627,10 +11656,8 @@ InventoryResult Player::CanUseItem(ItemTemplate const* proto) const if (!proto) return EQUIP_ERR_ITEM_NOT_FOUND; - if ((proto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) && GetTeam() != HORDE) - return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; - - if ((proto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) && GetTeam() != ALLIANCE) + if (((proto->Flags2 & ITEM_FLAG2_FACTION_HORDE) && GetTeam() != HORDE) || + (((proto->Flags2 & ITEM_FLAG2_FACTION_ALLIANCE) && GetTeam() != ALLIANCE))) return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; if ((proto->AllowableClass & getClassMask()) == 0 || (proto->AllowableRace & getRaceMask()) == 0) @@ -12296,7 +12323,7 @@ void Player::MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool // in case trade we already have item in other player inventory pLastItem->SetState(in_characterInventoryDB ? ITEM_CHANGED : ITEM_NEW, this); - if (pLastItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE)) + if (pLastItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE)) AddTradeableItem(pLastItem); } } @@ -12314,7 +12341,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) for (uint8 i = 0; i < MAX_BAG_SIZE; ++i) DestroyItem(slot, i, update); - if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) + if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED)) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT); @@ -12387,7 +12414,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) // Delete rolled money / loot from db. // MUST be done before RemoveFromWorld() or GetTemplate() fails if (ItemTemplate const* pTmp = pItem->GetTemplate()) - if (pTmp->Flags & ITEM_PROTO_FLAG_HAS_LOOT) + if (pTmp->Flags & ITEM_FLAG_HAS_LOOT) pItem->ItemContainerDeleteLootMoneyAndLootItemsFromDB(); if (IsInWorld() && update) @@ -14825,7 +14852,9 @@ bool Player::CanRewardQuest(Quest const* quest, uint32 reward, bool msg) InventoryResult res = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, quest->RewardChoiceItemId[reward], quest->RewardChoiceItemCount[reward]); if (res != EQUIP_ERR_OK) { - SendEquipError(res, nullptr, nullptr, quest->RewardChoiceItemId[reward]); + if (msg) + SendQuestFailed(quest->GetQuestId(), res); + return false; } } @@ -14840,7 +14869,9 @@ bool Player::CanRewardQuest(Quest const* quest, uint32 reward, bool msg) InventoryResult res = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, quest->RewardItemId[i], quest->RewardItemIdCount[i]); if (res != EQUIP_ERR_OK) { - SendEquipError(res, nullptr, nullptr, quest->RewardItemId[i]); + if (msg) + SendQuestFailed(quest->GetQuestId(), res); + return false; } } @@ -14989,20 +15020,20 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, { if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->RequiredItemId[i])) { - if (quest->RequiredItemCount[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1)) - DestroyItemCount(quest->RequiredItemId[i], 9999, true, true); + if (quest->RequiredItemCount[i] > 0 && itemTemplate->Bonding == BIND_QUEST_ITEM && !quest->IsRepeatable() && !HasQuestForItem(quest->RequiredItemId[i], quest_id, true)) + DestroyItemCount(quest->RequiredItemId[i], 9999, true); else - DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true, true); + DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true); } } for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) { if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->ItemDrop[i])) { - if (quest->ItemDropQuantity[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1)) - DestroyItemCount(quest->ItemDrop[i], 9999, true, true); + if (quest->ItemDropQuantity[i] > 0 && itemTemplate->Bonding == BIND_QUEST_ITEM && !quest->IsRepeatable() && !HasQuestForItem(quest->ItemDrop[i], quest_id)) + DestroyItemCount(quest->ItemDrop[i], 9999, true); else - DestroyItemCount(quest->ItemDrop[i], quest->ItemDropQuantity[i], true, true); + DestroyItemCount(quest->ItemDrop[i], quest->ItemDropQuantity[i], true); } } @@ -15187,13 +15218,16 @@ void Player::SetRewardedQuest(uint32 quest_id) void Player::FailQuest(uint32 questId) { - if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId)) { // Already complete quests shouldn't turn failed. if (GetQuestStatus(questId) == QUEST_STATUS_COMPLETE && !quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED)) return; + // You can't fail a quest if you don't have it, or if it's already rewarded. + if (GetQuestStatus(questId) == QUEST_STATUS_NONE || GetQuestStatus(questId) == QUEST_STATUS_REWARDED) + return; + SetQuestStatus(questId, QUEST_STATUS_FAILED); uint16 log_slot = FindQuestSlot(questId); @@ -15219,13 +15253,13 @@ void Player::FailQuest(uint32 questId) // Destroy quest items on quest failure. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->RequiredItemId[i])) - if (quest->RequiredItemCount[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1)) - DestroyItemCount(quest->RequiredItemId[i], 9999, true, true); + if (quest->RequiredItemCount[i] > 0 && itemTemplate->Bonding == BIND_QUEST_ITEM) + DestroyItemCount(quest->RequiredItemId[i], 9999, true); for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->ItemDrop[i])) - if (quest->ItemDropQuantity[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1)) - DestroyItemCount(quest->ItemDrop[i], 9999, true, true); + if (quest->ItemDropQuantity[i] > 0 && itemTemplate->Bonding == BIND_QUEST_ITEM) + DestroyItemCount(quest->ItemDrop[i], 9999, true); } } @@ -15236,13 +15270,13 @@ void Player::AbandonQuest(uint32 questId) // Destroy quest items on quest abandon. for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->RequiredItemId[i])) - if (quest->RequiredItemCount[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1)) - DestroyItemCount(quest->RequiredItemId[i], 9999, true, true); + if (quest->RequiredItemCount[i] > 0 && itemTemplate->Bonding == BIND_QUEST_ITEM) + DestroyItemCount(quest->RequiredItemId[i], 9999, true); for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->ItemDrop[i])) - if (quest->ItemDropQuantity[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1)) - DestroyItemCount(quest->ItemDrop[i], 9999, true, true); + if (quest->ItemDropQuantity[i] > 0 && itemTemplate->Bonding == BIND_QUEST_ITEM) + DestroyItemCount(quest->ItemDrop[i], 9999, true); } } @@ -15946,21 +15980,18 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver) if (!quest) continue; - if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK, quest->GetQuestId(), this)) + if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_ACCEPT, quest->GetQuestId(), this)) continue; QuestStatus status = GetQuestStatus(questId); - if ((status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(questId)) || - (quest->IsAutoComplete() && CanTakeQuest(quest, false))) - { - if (quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly()) - result2 = DIALOG_STATUS_REWARD_REP; - else - result2 = DIALOG_STATUS_REWARD; - } + if (status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(questId)) + result2 = DIALOG_STATUS_REWARD; else if (status == QUEST_STATUS_INCOMPLETE) result2 = DIALOG_STATUS_INCOMPLETE; + if (quest->IsAutoComplete() && CanTakeQuest(quest, false) && quest->IsRepeatable() && !quest->IsDailyOrWeekly()) + result2 = DIALOG_STATUS_REWARD_REP; + if (result2 > result) result = result2; } @@ -15973,7 +16004,7 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver) if (!quest) continue; - if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK, quest->GetQuestId(), this)) + if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_ACCEPT, quest->GetQuestId(), this)) continue; QuestStatus status = GetQuestStatus(questId); @@ -15983,9 +16014,7 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver) { if (SatisfyQuestLevel(quest, false)) { - if (quest->IsAutoComplete()) - result2 = DIALOG_STATUS_REWARD_REP; - else if (getLevel() <= (GetQuestLevel(quest) + sWorld->getIntConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF))) + if (getLevel() <= (GetQuestLevel(quest) + sWorld->getIntConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF))) { if (quest->IsDaily()) result2 = DIALOG_STATUS_AVAILABLE_REP; @@ -16546,12 +16575,12 @@ void Player::ReputationChanged2(FactionEntry const* factionEntry) } } -bool Player::HasQuestForItem(uint32 itemid) const +bool Player::HasQuestForItem(uint32 itemid, uint32 excludeQuestId /* 0 */, bool turnIn /* false */) const { for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i) { uint32 questid = GetQuestSlotQuestId(i); - if (questid == 0) + if (questid == 0 || questid == excludeQuestId) continue; QuestStatusMap::const_iterator qs_itr = m_QuestStatus.find(questid); @@ -16560,7 +16589,7 @@ bool Player::HasQuestForItem(uint32 itemid) const QuestStatusData const& q_status = qs_itr->second; - if (q_status.Status == QUEST_STATUS_INCOMPLETE) + if ((q_status.Status == QUEST_STATUS_INCOMPLETE) || (turnIn && q_status.Status == QUEST_STATUS_COMPLETE)) { Quest const* qinfo = sObjectMgr->GetQuestTemplate(questid); if (!qinfo) @@ -16575,7 +16604,7 @@ bool Player::HasQuestForItem(uint32 itemid) const // This part for ReqItem drop for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j) { - if (itemid == qinfo->RequiredItemId[j] && q_status.ItemCount[j] < qinfo->RequiredItemCount[j]) + if ((itemid == qinfo->RequiredItemId[j] && q_status.ItemCount[j] < qinfo->RequiredItemCount[j]) || (turnIn && q_status.ItemCount[j] >= qinfo->RequiredItemCount[j])) return true; } // This part - for ReqSource @@ -16585,17 +16614,17 @@ bool Player::HasQuestForItem(uint32 itemid) const if (qinfo->ItemDrop[j] == itemid) { ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid); - + uint32 ownedCount = GetItemCount(itemid, true); // 'unique' item - if (pProto->MaxCount && int32(GetItemCount(itemid, true)) < pProto->MaxCount) + if ((pProto->MaxCount && int32(ownedCount) < pProto->MaxCount) || (turnIn && int32(ownedCount) >= pProto->MaxCount)) return true; // allows custom amount drop when not 0 if (qinfo->ItemDropQuantity[j]) { - if (GetItemCount(itemid, true) < qinfo->ItemDropQuantity[j]) + if ((ownedCount < qinfo->ItemDropQuantity[j]) || (turnIn && ownedCount >= qinfo->ItemDropQuantity[j])) return true; - } else if (GetItemCount(itemid, true) < pProto->GetMaxStackSize()) + } else if (ownedCount < pProto->GetMaxStackSize()) return true; } } @@ -18079,13 +18108,13 @@ Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, F remove = true; } // "Conjured items disappear if you are logged out for more than 15 minutes" - else if (timeDiff > 15 * MINUTE && proto->Flags & ITEM_PROTO_FLAG_CONJURED) + else if (timeDiff > 15 * MINUTE && proto->Flags & ITEM_FLAG_CONJURED) { TC_LOG_DEBUG("entities.player.loading", "Player::_LoadInventory: player (GUID: %u, name: '%s', diff: %u) has conjured item (GUID: %u, entry: %u) with expired lifetime (15 minutes). Deleting item.", GetGUID().GetCounter(), GetName().c_str(), timeDiff, item->GetGUID().GetCounter(), item->GetEntry()); remove = true; } - else if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE)) + else if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE)) { if (item->GetPlayedTime() > (2 * HOUR)) { @@ -18096,7 +18125,7 @@ Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, F stmt->setUInt32(0, item->GetGUID().GetCounter()); trans->Append(stmt); - item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE); + item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE); } else { @@ -18114,11 +18143,11 @@ Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, F { TC_LOG_DEBUG("entities.player.loading", "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) with refundable flags, but without data in item_refund_instance. Removing flag.", GetGUID().GetCounter(), GetName().c_str(), item->GetGUID().GetCounter(), item->GetEntry()); - item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE); + item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE); } } } - else if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE)) + else if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE)) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_BOP_TRADE); stmt->setUInt32(0, item->GetGUID().GetCounter()); @@ -18142,7 +18171,7 @@ Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, F { TC_LOG_DEBUG("entities.player.loading", "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) with ITEM_FLAG_BOP_TRADEABLE flag, but without data in item_soulbound_trade_data. Removing flag.", GetGUID().GetCounter(), GetName().c_str(), item->GetGUID().GetCounter(), item->GetEntry()); - item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE); + item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE); } } else if (proto->HolidayId) @@ -19538,7 +19567,6 @@ void Player::_SaveAuras(SQLTransaction& trans) stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AURA); stmt->setUInt32(index++, GetGUID().GetCounter()); stmt->setUInt64(index++, itr->second->GetCasterGUID().GetRawValue()); - stmt->setUInt64(index++, itr->second->GetCastItemGUID().GetRawValue()); stmt->setUInt32(index++, itr->second->GetId()); stmt->setUInt8(index++, effMask); stmt->setUInt8(index++, recalculateMask); @@ -20853,29 +20881,30 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply) TC_LOG_DEBUG("spells", "Player::AddSpellMod: Player '%s' (%s), SpellID: %d", GetName().c_str(), GetGUID().ToString().c_str(), mod->spellId); uint16 Opcode = (mod->type == SPELLMOD_FLAT) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER; - int i = 0; - flag96 _mask = 0; - for (int32 eff = 0; eff < 96; ++eff) + flag96 modMask; + for (uint8 i = 0; i < 3; ++i) { - if (eff != 0 && eff % 32 == 0) - _mask[i++] = 0; - - _mask[i] = uint32(1) << (eff - (32 * i)); - if ((mod->mask & _mask)) + for (uint32 eff = 0; eff < 32; ++eff) { - int32 val = 0; - for (SpellModifier* spellMod : m_spellMods[mod->op]) + modMask[i] = uint32(1) << eff; + if ((mod->mask & modMask)) { - if (spellMod->type == mod->type && (spellMod->mask & _mask)) - val += spellMod->value; + int32 val = 0; + for (SpellModifier* spellMod : m_spellMods[mod->op]) + { + if (spellMod->type == mod->type && (spellMod->mask & modMask)) + val += spellMod->value; + } + val += apply ? mod->value : -(mod->value); + WorldPacket data(Opcode, (1 + 1 + 4)); + data << uint8(eff + 32 * i); + data << uint8(mod->op); + data << int32(val); + SendDirectMessage(&data); } - val += apply ? mod->value : -(mod->value); - WorldPacket data(Opcode, (1 + 1 + 4)); - data << uint8(eff); - data << uint8(mod->op); - data << int32(val); - SendDirectMessage(&data); } + + modMask[i] = 0; } if (apply) @@ -21482,9 +21511,9 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c if (!bStore) AutoUnequipOffhandIfNeed(); - if (pProto->Flags & ITEM_PROTO_FLAG_REFUNDABLE && crItem->ExtendedCost && pProto->GetMaxStackSize() == 1) + if (pProto->Flags & ITEM_FLAG_ITEM_PURCHASE_RECORD && crItem->ExtendedCost && pProto->GetMaxStackSize() == 1) { - it->SetFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE); + it->SetFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE); it->SetRefundRecipient(GetGUID().GetCounter()); it->SetPaidMoney(price); it->SetPaidExtendedCost(crItem->ExtendedCost); @@ -21521,7 +21550,7 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin return false; } - if (!IsGameMaster() && ((pProto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY && GetTeam() == ALLIANCE) || (pProto->Flags2 == ITEM_FLAGS_EXTRA_ALLIANCE_ONLY && GetTeam() == HORDE))) + if (!IsGameMaster() && ((pProto->Flags2 & ITEM_FLAG2_FACTION_HORDE && GetTeam() == ALLIANCE) || (pProto->Flags2 == ITEM_FLAG2_FACTION_ALLIANCE && GetTeam() == HORDE))) return false; Creature* creature = GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR); @@ -22709,7 +22738,7 @@ void Player::SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint3 void Player::ApplyEquipCooldown(Item* pItem) { - if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_PROTO_FLAG_NO_EQUIP_COOLDOWN)) + if (pItem->GetTemplate()->Flags & ITEM_FLAG_NO_EQUIP_COOLDOWN) return; std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); @@ -24609,9 +24638,9 @@ void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore cons void Player::StoreLootItem(uint8 lootSlot, Loot* loot) { - QuestItem* qitem = nullptr; - QuestItem* ffaitem = nullptr; - QuestItem* conditem = nullptr; + NotNormalLootItem* qitem = nullptr; + NotNormalLootItem* ffaitem = nullptr; + NotNormalLootItem* conditem = nullptr; LootItem* item = loot->LootItemInSlot(lootSlot, this, &qitem, &ffaitem, &conditem); @@ -24877,7 +24906,7 @@ InventoryResult Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limi InventoryResult Player::CanEquipUniqueItem(ItemTemplate const* itemProto, uint8 except_slot, uint32 limit_count) const { // check unique-equipped on item - if (itemProto->Flags & ITEM_PROTO_FLAG_UNIQUE_EQUIPPED) + if (itemProto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE) { // there is an equip limit on this item if (HasItemOrGemWithIdEquipped(itemProto->ItemId, 1, except_slot)) @@ -24897,7 +24926,9 @@ InventoryResult Player::CanEquipUniqueItem(ItemTemplate const* itemProto, uint8 return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED; // there is an equip limit on this item - if (HasItemOrGemWithLimitCategoryEquipped(itemProto->ItemLimitCategory, limitEntry->maxCount - limit_count + 1, except_slot)) + if (HasItemWithLimitCategoryEquipped(itemProto->ItemLimitCategory, limitEntry->maxCount - limit_count + 1, except_slot)) + return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED; + else if (HasGemWithLimitCategoryEquipped(itemProto->ItemLimitCategory, limitEntry->maxCount - limit_count + 1, except_slot)) return EQUIP_ERR_ITEM_MAX_COUNT_EQUIPPED_SOCKETED; } @@ -25861,6 +25892,20 @@ void Player::ActivateSpec(uint8 spec) UnsummonAllTotems(); ExitVehicle(); RemoveAllControlled(); + + // remove single target auras at other targets + AuraList& scAuras = GetSingleCastAuras(); + for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end();) + { + Aura* aura = *iter; + if (aura->GetUnitOwner() != this) + { + aura->Remove(); + iter = scAuras.begin(); + } + else + ++iter; + } /*RemoveAllAurasOnDeath(); if (GetPet()) GetPet()->RemoveAllAurasOnDeath();*/ @@ -26037,7 +26082,7 @@ void Player::SendRefundInfo(Item* item) // This function call unsets ITEM_FLAGS_REFUNDABLE if played time is over 2 hours. item->UpdatePlayedTime(this); - if (!item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE)) + if (!item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE)) { TC_LOG_DEBUG("entities.player.items", "Item refund: item not refundable!"); return; @@ -26097,7 +26142,7 @@ bool Player::AddItem(uint32 itemId, uint32 count) void Player::RefundItem(Item* item) { - if (!item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE)) + if (!item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE)) { TC_LOG_DEBUG("entities.player.items", "Item refund: item not refundable!"); return; diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 7c4bf2f9471..565d4f4ac2f 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1199,7 +1199,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool HasItemFitToSpellRequirements(SpellInfo const* spellInfo, Item const* ignoreItem = nullptr) const; bool CanNoReagentCast(SpellInfo const* spellInfo) const; bool HasItemOrGemWithIdEquipped(uint32 item, uint32 count, uint8 except_slot = NULL_SLOT) const; - bool HasItemOrGemWithLimitCategoryEquipped(uint32 limitCategory, uint32 count, uint8 except_slot = NULL_SLOT) const; + bool HasItemWithLimitCategoryEquipped(uint32 limitCategory, uint32 count, uint8 except_slot = NULL_SLOT) const; + bool HasGemWithLimitCategoryEquipped(uint32 limitCategory, uint32 count, uint8 except_slot = NULL_SLOT) const; InventoryResult CanTakeMoreSimilarItems(Item* pItem, uint32* itemLimitCategory = NULL) const { return CanTakeMoreSimilarItems(pItem->GetEntry(), pItem->GetCount(), pItem, NULL, itemLimitCategory); } InventoryResult CanTakeMoreSimilarItems(uint32 entry, uint32 count, uint32* itemLimitCategory = NULL) const { return CanTakeMoreSimilarItems(entry, count, NULL, NULL, itemLimitCategory); } InventoryResult CanStoreNewItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 item, uint32 count, uint32* no_space_count = nullptr) const; @@ -1405,7 +1406,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void MoneyChanged(uint32 value); void ReputationChanged(FactionEntry const* factionEntry); void ReputationChanged2(FactionEntry const* factionEntry); - bool HasQuestForItem(uint32 itemId) const; + bool HasQuestForItem(uint32 itemId, uint32 excludeQuestId = 0, bool turnIn = false) const; bool HasQuestForGO(int32 goId) const; void UpdateForQuestWorldObjects(); bool CanShareQuest(uint32 questId) const; diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index fad197949a8..cdb4b931a07 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -131,6 +131,7 @@ void Transport::Update(uint32 diff) m_goValue.Transport.PathProgress += diff; uint32 timer = m_goValue.Transport.PathProgress % GetTransportPeriod(); + bool justStopped = false; // Set current waypoint // Desired outcome: _currentFrame->DepartureTime < timer < _nextFrame->ArriveTime @@ -149,6 +150,7 @@ void Transport::Update(uint32 diff) if (timer < _currentFrame->DepartureTime) { SetMoving(false); + justStopped = true; if (_pendingStop && GetGoState() != GO_STATE_READY) { SetGoState(GO_STATE_READY); @@ -203,12 +205,14 @@ void Transport::Update(uint32 diff) _positionChangeTimer.Reset(positionUpdateDelay); if (IsMoving()) { - float t = CalculateSegmentPos(float(timer) * 0.001f); + float t = !justStopped ? CalculateSegmentPos(float(timer) * 0.001f) : 1.0f; G3D::Vector3 pos, dir; _currentFrame->Spline->evaluate_percent(_currentFrame->Index, t, pos); _currentFrame->Spline->evaluate_derivative(_currentFrame->Index, t, dir); UpdatePosition(pos.x, pos.y, pos.z, std::atan2(dir.y, dir.x) + float(M_PI)); } + else if (justStopped) + UpdatePosition(_currentFrame->Node->LocX, _currentFrame->Node->LocY, _currentFrame->Node->LocZ, _currentFrame->InitialOrientation); else { /* There are four possible scenarios that trigger loading/unloading passengers: diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 1d4886c1915..40c7ee9fc3f 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -112,7 +112,7 @@ DamageInfo::DamageInfo(CalcDamageInfo const& dmgInfo) break; } - if (m_absorb) + if (dmgInfo.HitInfo & (HITINFO_PARTIAL_ABSORB | HITINFO_FULL_ABSORB)) m_hitMask |= PROC_HIT_ABSORB; if (dmgInfo.HitInfo & HITINFO_FULL_RESIST) @@ -121,6 +121,8 @@ DamageInfo::DamageInfo(CalcDamageInfo const& dmgInfo) if (m_block) m_hitMask |= PROC_HIT_BLOCK; + bool const damageNullified = (dmgInfo.HitInfo & (HITINFO_FULL_ABSORB | HITINFO_FULL_RESIST)) != 0 || + (m_hitMask & (PROC_HIT_IMMUNE | PROC_HIT_FULL_BLOCK)) != 0; switch (dmgInfo.hitOutCome) { case MELEE_HIT_MISS: @@ -138,10 +140,12 @@ DamageInfo::DamageInfo(CalcDamageInfo const& dmgInfo) case MELEE_HIT_CRUSHING: case MELEE_HIT_GLANCING: case MELEE_HIT_NORMAL: - m_hitMask |= PROC_HIT_NORMAL; + if (!damageNullified) + m_hitMask |= PROC_HIT_NORMAL; break; case MELEE_HIT_CRIT: - m_hitMask |= PROC_HIT_CRITICAL; + if (!damageNullified) + m_hitMask |= PROC_HIT_CRITICAL; break; default: break; @@ -161,7 +165,7 @@ DamageInfo::DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEff void DamageInfo::ModifyDamage(int32 amount) { - amount = std::min(amount, int32(GetDamage())); + amount = std::max(amount, -static_cast<int32>(GetDamage())); m_damage += amount; } @@ -179,7 +183,10 @@ void DamageInfo::ResistDamage(uint32 amount) m_resist += amount; m_damage -= amount; if (!m_damage) + { m_hitMask |= PROC_HIT_FULL_RESIST; + m_hitMask &= ~(PROC_HIT_NORMAL | PROC_HIT_CRITICAL); + } } void DamageInfo::BlockDamage(uint32 amount) @@ -189,7 +196,10 @@ void DamageInfo::BlockDamage(uint32 amount) m_damage -= amount; m_hitMask |= PROC_HIT_BLOCK; if (!m_damage) + { m_hitMask |= PROC_HIT_FULL_BLOCK; + m_hitMask &= ~(PROC_HIT_NORMAL | PROC_HIT_CRITICAL); + } } uint32 DamageInfo::GetHitMask() const @@ -294,9 +304,6 @@ Unit::Unit(bool isWorldObject) : m_transform = 0; m_canModifyStats = false; - for (uint8 i = 0; i < MAX_SPELL_IMMUNITY; ++i) - m_spellImmune[i].clear(); - for (uint8 i = 0; i < UNIT_MOD_END; ++i) { m_auraModifiersGroup[i][BASE_VALUE] = 0.0f; @@ -646,7 +653,7 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons || HasBreakableByDamageAuraType(SPELL_AURA_TRANSFORM, excludeAura)); } -void Unit::DealDamageMods(Unit* victim, uint32 &damage, uint32* absorb) +void Unit::DealDamageMods(Unit const* victim, uint32 &damage, uint32* absorb) const { if (!victim || !victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks())) { @@ -967,7 +974,7 @@ void Unit::CastSpell(Unit* victim, uint32 spellId, bool triggered, Item* castIte CastSpell(victim, spellId, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster); } -void Unit::CastSpell(Unit* victim, uint32 spellId, TriggerCastFlags triggerFlags /*= TRIGGER_NONE*/, Item* castItem /*= NULL*/, AuraEffect const* triggeredByAura /*= NULL*/, ObjectGuid originalCaster /*= ObjectGuid::Empty*/) +void Unit::CastSpell(Unit* victim, uint32 spellId, TriggerCastFlags triggerFlags /*= TRIGGER_NONE*/, Item* castItem /*= nullptr*/, AuraEffect const* triggeredByAura /*= nullptr*/, ObjectGuid originalCaster /*= ObjectGuid::Empty*/) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) @@ -979,7 +986,7 @@ void Unit::CastSpell(Unit* victim, uint32 spellId, TriggerCastFlags triggerFlags CastSpell(victim, spellInfo, triggerFlags, castItem, triggeredByAura, originalCaster); } -void Unit::CastSpell(Unit* victim, SpellInfo const* spellInfo, bool triggered, Item* castItem/*= NULL*/, AuraEffect const* triggeredByAura /*= NULL*/, ObjectGuid originalCaster /*= ObjectGuid::Empty*/) +void Unit::CastSpell(Unit* victim, SpellInfo const* spellInfo, bool triggered, Item* castItem/*= nullptr*/, AuraEffect const* triggeredByAura /*= nullptr*/, ObjectGuid originalCaster /*= ObjectGuid::Empty*/) { CastSpell(victim, spellInfo, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster); } @@ -988,7 +995,7 @@ void Unit::CastSpell(Unit* victim, SpellInfo const* spellInfo, TriggerCastFlags { SpellCastTargets targets; targets.SetUnitTarget(victim); - CastSpell(targets, spellInfo, NULL, triggerFlags, castItem, triggeredByAura, originalCaster); + CastSpell(targets, spellInfo, nullptr, triggerFlags, castItem, triggeredByAura, originalCaster); } void Unit::CastCustomSpell(Unit* target, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster) @@ -1042,7 +1049,21 @@ void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, SpellCastTargets targets; targets.SetDst(x, y, z, GetOrientation()); - CastSpell(targets, spellInfo, NULL, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster); + CastSpell(targets, spellInfo, nullptr, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster); +} + +void Unit::CastSpell(float x, float y, float z, uint32 spellId, TriggerCastFlags triggerFlags, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster) +{ + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (!spellInfo) + { + TC_LOG_ERROR("entities.unit", "CastSpell: unknown spell id %u by caster: %s %u)", spellId, (GetTypeId() == TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"), (GetTypeId() == TYPEID_PLAYER ? GetGUID().GetCounter() : GetEntry())); + return; + } + SpellCastTargets targets; + targets.SetDst(x, y, z, GetOrientation()); + + CastSpell(targets, spellInfo, nullptr, triggerFlags, castItem, triggeredByAura, originalCaster); } void Unit::CastSpell(GameObject* go, uint32 spellId, bool triggered, Item* castItem, AuraEffect* triggeredByAura, ObjectGuid originalCaster) @@ -1056,7 +1077,7 @@ void Unit::CastSpell(GameObject* go, uint32 spellId, bool triggered, Item* castI SpellCastTargets targets; targets.SetGOTarget(go); - CastSpell(targets, spellInfo, NULL, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster); + CastSpell(targets, spellInfo, nullptr, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster); } // Obsolete func need remove, here only for comotability vs another patches @@ -1155,7 +1176,7 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama ApplyResilience(victim, nullptr, &damage, crit, CR_CRIT_TAKEN_RANGED); break; } - // Magical Attacks + // Magical Attacks case SPELL_DAMAGE_CLASS_NONE: case SPELL_DAMAGE_CLASS_MAGIC: { @@ -1178,15 +1199,13 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama sScriptMgr->ModifySpellDamageTaken(damageInfo->target, damageInfo->attacker, damage); // Calculate absorb resist - if (damage > 0) - { - CalcAbsorbResist(victim, damageSchoolMask, SPELL_DIRECT_DAMAGE, damage, &damageInfo->absorb, &damageInfo->resist, spellInfo); - damage -= damageInfo->absorb + damageInfo->resist; - } - else + if (damage < 0) damage = 0; damageInfo->damage = damage; + DamageInfo dmgInfo(*damageInfo, SPELL_DIRECT_DAMAGE, BASE_ATTACK, PROC_HIT_NONE); + CalcAbsorbResist(dmgInfo); + damageInfo->damage = dmgInfo.GetDamage(); } void Unit::DealSpellDamage(SpellNonMeleeDamage* damageInfo, bool durabilityLoss) @@ -1396,7 +1415,10 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam { damageInfo->procVictim |= PROC_FLAG_TAKEN_DAMAGE; // Calculate absorb & resists - CalcAbsorbResist(damageInfo->target, SpellSchoolMask(damageInfo->damageSchoolMask), DIRECT_DAMAGE, damageInfo->damage, &damageInfo->absorb, &damageInfo->resist); + DamageInfo dmgInfo(*damageInfo); + CalcAbsorbResist(dmgInfo); + damageInfo->absorb = dmgInfo.GetAbsorb(); + damageInfo->resist = dmgInfo.GetResist(); if (damageInfo->absorb) damageInfo->HitInfo |= (damageInfo->damage - damageInfo->absorb == 0 ? HITINFO_FULL_ABSORB : HITINFO_PARTIAL_ABSORB); @@ -1404,7 +1426,7 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam if (damageInfo->resist) damageInfo->HitInfo |= (damageInfo->damage - damageInfo->resist == 0 ? HITINFO_FULL_RESIST : HITINFO_PARTIAL_RESIST); - damageInfo->damage -= damageInfo->absorb + damageInfo->resist; + damageInfo->damage = dmgInfo.GetDamage(); } else // Impossible get negative result but.... damageInfo->damage = 0; @@ -1731,57 +1753,49 @@ uint32 Unit::CalcSpellResistance(Unit* victim, SpellSchoolMask schoolMask, Spell return resistance * 10; } -void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffectType damagetype, uint32 const damage, uint32* absorb, uint32* resist, SpellInfo const* spellInfo /*= NULL*/) +void Unit::CalcAbsorbResist(DamageInfo& damageInfo) { - if (!victim || !victim->IsAlive() || !damage) + if (!damageInfo.GetVictim() || !damageInfo.GetVictim()->IsAlive() || !damageInfo.GetDamage()) return; - DamageInfo dmgInfo = DamageInfo(this, victim, damage, spellInfo, schoolMask, damagetype, BASE_ATTACK); - - uint32 spellResistance = CalcSpellResistance(victim, schoolMask, spellInfo); - dmgInfo.ResistDamage(CalculatePct(damage, spellResistance)); + uint32 spellResistance = CalcSpellResistance(damageInfo.GetVictim(), damageInfo.GetSchoolMask(), damageInfo.GetSpellInfo()); + damageInfo.ResistDamage(CalculatePct(damageInfo.GetDamage(), spellResistance)); // Ignore Absorption Auras - float auraAbsorbMod = 0; - AuraEffectList const& AbsIgnoreAurasA = GetAuraEffectsByType(SPELL_AURA_MOD_TARGET_ABSORB_SCHOOL); - for (AuraEffectList::const_iterator itr = AbsIgnoreAurasA.begin(); itr != AbsIgnoreAurasA.end(); ++itr) + float auraAbsorbMod(GetMaxPositiveAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_ABSORB_SCHOOL, damageInfo.GetSchoolMask())); + + AuraEffectList const& abilityAbsorbAuras = GetAuraEffectsByType(SPELL_AURA_MOD_TARGET_ABILITY_ABSORB_SCHOOL); + for (AuraEffect const* aurEff : abilityAbsorbAuras) { - if (!((*itr)->GetMiscValue() & schoolMask)) + if (!(aurEff->GetMiscValue() & damageInfo.GetSchoolMask())) continue; - if ((*itr)->GetAmount() > auraAbsorbMod) - auraAbsorbMod = float((*itr)->GetAmount()); - } - - AuraEffectList const& AbsIgnoreAurasB = GetAuraEffectsByType(SPELL_AURA_MOD_TARGET_ABILITY_ABSORB_SCHOOL); - for (AuraEffectList::const_iterator itr = AbsIgnoreAurasB.begin(); itr != AbsIgnoreAurasB.end(); ++itr) - { - if (!((*itr)->GetMiscValue() & schoolMask)) + if (!aurEff->IsAffectedOnSpell(damageInfo.GetSpellInfo())) continue; - if (((*itr)->GetAmount() > auraAbsorbMod) && (*itr)->IsAffectedOnSpell(spellInfo)) - auraAbsorbMod = float((*itr)->GetAmount()); + if ((aurEff->GetAmount() > auraAbsorbMod)) + auraAbsorbMod = float(aurEff->GetAmount()); } RoundToInterval(auraAbsorbMod, 0.0f, 100.0f); - int32 absorbIgnoringDamage = CalculatePct(dmgInfo.GetDamage(), auraAbsorbMod); - dmgInfo.ModifyDamage(-absorbIgnoringDamage); + int32 absorbIgnoringDamage = CalculatePct(damageInfo.GetDamage(), auraAbsorbMod); + damageInfo.ModifyDamage(-absorbIgnoringDamage); // We're going to call functions which can modify content of the list during iteration over it's elements // Let's copy the list so we can prevent iterator invalidation - AuraEffectList vSchoolAbsorbCopy(victim->GetAuraEffectsByType(SPELL_AURA_SCHOOL_ABSORB)); + AuraEffectList vSchoolAbsorbCopy(damageInfo.GetVictim()->GetAuraEffectsByType(SPELL_AURA_SCHOOL_ABSORB)); vSchoolAbsorbCopy.sort(Trinity::AbsorbAuraOrderPred()); // absorb without mana cost - for (AuraEffectList::iterator itr = vSchoolAbsorbCopy.begin(); (itr != vSchoolAbsorbCopy.end()) && (dmgInfo.GetDamage() > 0); ++itr) + for (AuraEffectList::iterator itr = vSchoolAbsorbCopy.begin(); (itr != vSchoolAbsorbCopy.end()) && (damageInfo.GetDamage() > 0); ++itr) { AuraEffect* absorbAurEff = *itr; // Check if aura was removed during iteration - we don't need to work on such auras - AuraApplication const* aurApp = absorbAurEff->GetBase()->GetApplicationOfTarget(victim->GetGUID()); + AuraApplication const* aurApp = absorbAurEff->GetBase()->GetApplicationOfTarget(damageInfo.GetVictim()->GetGUID()); if (!aurApp) continue; - if (!(absorbAurEff->GetMiscValue() & schoolMask)) + if (!(absorbAurEff->GetMiscValue() & damageInfo.GetSchoolMask())) continue; // get amount which can be still absorbed by the aura @@ -1794,19 +1808,19 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe bool defaultPrevented = false; - absorbAurEff->GetBase()->CallScriptEffectAbsorbHandlers(absorbAurEff, aurApp, dmgInfo, tempAbsorb, defaultPrevented); + absorbAurEff->GetBase()->CallScriptEffectAbsorbHandlers(absorbAurEff, aurApp, damageInfo, tempAbsorb, defaultPrevented); currentAbsorb = tempAbsorb; if (defaultPrevented) continue; // absorb must be smaller than the damage itself - currentAbsorb = RoundToInterval(currentAbsorb, 0, int32(dmgInfo.GetDamage())); + currentAbsorb = RoundToInterval(currentAbsorb, 0, int32(damageInfo.GetDamage())); - dmgInfo.AbsorbDamage(currentAbsorb); + damageInfo.AbsorbDamage(currentAbsorb); tempAbsorb = currentAbsorb; - absorbAurEff->GetBase()->CallScriptEffectAfterAbsorbHandlers(absorbAurEff, aurApp, dmgInfo, tempAbsorb); + absorbAurEff->GetBase()->CallScriptEffectAfterAbsorbHandlers(absorbAurEff, aurApp, damageInfo, tempAbsorb); // Check if our aura is using amount to count damage if (absorbAurEff->GetAmount() >= 0) @@ -1820,16 +1834,16 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe } // absorb by mana cost - AuraEffectList vManaShieldCopy(victim->GetAuraEffectsByType(SPELL_AURA_MANA_SHIELD)); - for (AuraEffectList::const_iterator itr = vManaShieldCopy.begin(); (itr != vManaShieldCopy.end()) && (dmgInfo.GetDamage() > 0); ++itr) + AuraEffectList vManaShieldCopy(damageInfo.GetVictim()->GetAuraEffectsByType(SPELL_AURA_MANA_SHIELD)); + for (AuraEffectList::const_iterator itr = vManaShieldCopy.begin(); (itr != vManaShieldCopy.end()) && (damageInfo.GetDamage() > 0); ++itr) { AuraEffect* absorbAurEff = *itr; // Check if aura was removed during iteration - we don't need to work on such auras - AuraApplication const* aurApp = absorbAurEff->GetBase()->GetApplicationOfTarget(victim->GetGUID()); + AuraApplication const* aurApp = absorbAurEff->GetBase()->GetApplicationOfTarget(damageInfo.GetVictim()->GetGUID()); if (!aurApp) continue; // check damage school mask - if (!(absorbAurEff->GetMiscValue() & schoolMask)) + if (!(absorbAurEff->GetMiscValue() & damageInfo.GetSchoolMask())) continue; // get amount which can be still absorbed by the aura @@ -1842,14 +1856,14 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe bool defaultPrevented = false; - absorbAurEff->GetBase()->CallScriptEffectManaShieldHandlers(absorbAurEff, aurApp, dmgInfo, tempAbsorb, defaultPrevented); + absorbAurEff->GetBase()->CallScriptEffectManaShieldHandlers(absorbAurEff, aurApp, damageInfo, tempAbsorb, defaultPrevented); currentAbsorb = tempAbsorb; if (defaultPrevented) continue; // absorb must be smaller than the damage itself - currentAbsorb = RoundToInterval(currentAbsorb, 0, int32(dmgInfo.GetDamage())); + currentAbsorb = RoundToInterval(currentAbsorb, 0, int32(damageInfo.GetDamage())); int32 manaReduction = currentAbsorb; @@ -1857,15 +1871,15 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe if (float manaMultiplier = absorbAurEff->GetSpellInfo()->Effects[absorbAurEff->GetEffIndex()].CalcValueMultiplier(absorbAurEff->GetCaster())) manaReduction = int32(float(manaReduction) * manaMultiplier); - int32 manaTaken = -victim->ModifyPower(POWER_MANA, -manaReduction); + int32 manaTaken = -damageInfo.GetVictim()->ModifyPower(POWER_MANA, -manaReduction); // take case when mana has ended up into account currentAbsorb = currentAbsorb ? int32(float(currentAbsorb) * (float(manaTaken) / float(manaReduction))) : 0; - dmgInfo.AbsorbDamage(currentAbsorb); + damageInfo.AbsorbDamage(currentAbsorb); tempAbsorb = currentAbsorb; - absorbAurEff->GetBase()->CallScriptEffectAfterManaShieldHandlers(absorbAurEff, aurApp, dmgInfo, tempAbsorb); + absorbAurEff->GetBase()->CallScriptEffectAfterManaShieldHandlers(absorbAurEff, aurApp, damageInfo, tempAbsorb); // Check if our aura is using amount to count damage if (absorbAurEff->GetAmount() >= 0) @@ -1876,39 +1890,39 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe } } - dmgInfo.ModifyDamage(absorbIgnoringDamage); + damageInfo.ModifyDamage(absorbIgnoringDamage); // split damage auras - only when not damaging self - if (victim != this) + if (damageInfo.GetVictim() != this) { // We're going to call functions which can modify content of the list during iteration over it's elements // Let's copy the list so we can prevent iterator invalidation - AuraEffectList vSplitDamageFlatCopy(victim->GetAuraEffectsByType(SPELL_AURA_SPLIT_DAMAGE_FLAT)); - for (AuraEffectList::iterator itr = vSplitDamageFlatCopy.begin(); (itr != vSplitDamageFlatCopy.end()) && (dmgInfo.GetDamage() > 0); ++itr) + AuraEffectList vSplitDamageFlatCopy(damageInfo.GetVictim()->GetAuraEffectsByType(SPELL_AURA_SPLIT_DAMAGE_FLAT)); + for (AuraEffectList::iterator itr = vSplitDamageFlatCopy.begin(); (itr != vSplitDamageFlatCopy.end()) && (damageInfo.GetDamage() > 0); ++itr) { // Check if aura was removed during iteration - we don't need to work on such auras - if (!((*itr)->GetBase()->IsAppliedOnTarget(victim->GetGUID()))) + if (!((*itr)->GetBase()->IsAppliedOnTarget(damageInfo.GetVictim()->GetGUID()))) continue; // check damage school mask - if (!((*itr)->GetMiscValue() & schoolMask)) + if (!((*itr)->GetMiscValue() & damageInfo.GetSchoolMask())) continue; // Damage can be splitted only if aura has an alive caster Unit* caster = (*itr)->GetCaster(); - if (!caster || (caster == victim) || !caster->IsInWorld() || !caster->IsAlive()) + if (!caster || (caster == damageInfo.GetVictim()) || !caster->IsInWorld() || !caster->IsAlive()) continue; int32 splitDamage = (*itr)->GetAmount(); // absorb must be smaller than the damage itself - splitDamage = RoundToInterval(splitDamage, 0, int32(dmgInfo.GetDamage())); + splitDamage = RoundToInterval(splitDamage, 0, int32(damageInfo.GetDamage())); - dmgInfo.AbsorbDamage(splitDamage); + damageInfo.AbsorbDamage(splitDamage); // check if caster is immune to damage - if (caster->IsImmunedToDamage(schoolMask)) + if (caster->IsImmunedToDamage(damageInfo.GetSchoolMask())) { - victim->SendSpellMiss(caster, (*itr)->GetSpellInfo()->Id, SPELL_MISS_IMMUNE); + damageInfo.GetVictim()->SendSpellMiss(caster, (*itr)->GetSpellInfo()->Id, SPELL_MISS_IMMUNE); continue; } @@ -1916,66 +1930,62 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe uint32 splitted_absorb = 0; DealDamageMods(caster, splitted, &splitted_absorb); - SendSpellNonMeleeDamageLog(caster, (*itr)->GetSpellInfo()->Id, splitted, schoolMask, splitted_absorb, 0, false, 0, false); + SendSpellNonMeleeDamageLog(caster, (*itr)->GetSpellInfo()->Id, splitted, damageInfo.GetSchoolMask(), splitted_absorb, 0, false, 0, false); CleanDamage cleanDamage = CleanDamage(splitted, 0, BASE_ATTACK, MELEE_HIT_NORMAL); - DealDamage(caster, splitted, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*itr)->GetSpellInfo(), false); + DealDamage(caster, splitted, &cleanDamage, DIRECT_DAMAGE, damageInfo.GetSchoolMask(), (*itr)->GetSpellInfo(), false); } // We're going to call functions which can modify content of the list during iteration over it's elements // Let's copy the list so we can prevent iterator invalidation - AuraEffectList vSplitDamagePctCopy(victim->GetAuraEffectsByType(SPELL_AURA_SPLIT_DAMAGE_PCT)); - for (AuraEffectList::iterator itr = vSplitDamagePctCopy.begin(); itr != vSplitDamagePctCopy.end() && dmgInfo.GetDamage() > 0; ++itr) + AuraEffectList vSplitDamagePctCopy(damageInfo.GetVictim()->GetAuraEffectsByType(SPELL_AURA_SPLIT_DAMAGE_PCT)); + for (AuraEffectList::iterator itr = vSplitDamagePctCopy.begin(); itr != vSplitDamagePctCopy.end() && damageInfo.GetDamage() > 0; ++itr) { // Check if aura was removed during iteration - we don't need to work on such auras - AuraApplication const* aurApp = (*itr)->GetBase()->GetApplicationOfTarget(victim->GetGUID()); + AuraApplication const* aurApp = (*itr)->GetBase()->GetApplicationOfTarget(damageInfo.GetVictim()->GetGUID()); if (!aurApp) continue; // check damage school mask - if (!((*itr)->GetMiscValue() & schoolMask)) + if (!((*itr)->GetMiscValue() & damageInfo.GetSchoolMask())) continue; // Damage can be splitted only if aura has an alive caster Unit* caster = (*itr)->GetCaster(); - if (!caster || (caster == victim) || !caster->IsInWorld() || !caster->IsAlive()) + if (!caster || (caster == damageInfo.GetVictim()) || !caster->IsInWorld() || !caster->IsAlive()) continue; - uint32 splitDamage = CalculatePct(dmgInfo.GetDamage(), (*itr)->GetAmount()); + uint32 splitDamage = CalculatePct(damageInfo.GetDamage(), (*itr)->GetAmount()); - (*itr)->GetBase()->CallScriptEffectSplitHandlers((*itr), aurApp, dmgInfo, splitDamage); + (*itr)->GetBase()->CallScriptEffectSplitHandlers((*itr), aurApp, damageInfo, splitDamage); // absorb must be smaller than the damage itself - splitDamage = RoundToInterval(splitDamage, uint32(0), uint32(dmgInfo.GetDamage())); + splitDamage = RoundToInterval(splitDamage, uint32(0), uint32(damageInfo.GetDamage())); - dmgInfo.AbsorbDamage(splitDamage); + damageInfo.AbsorbDamage(splitDamage); // check if caster is immune to damage - if (caster->IsImmunedToDamage(schoolMask)) + if (caster->IsImmunedToDamage(damageInfo.GetSchoolMask())) { - victim->SendSpellMiss(caster, (*itr)->GetSpellInfo()->Id, SPELL_MISS_IMMUNE); + damageInfo.GetVictim()->SendSpellMiss(caster, (*itr)->GetSpellInfo()->Id, SPELL_MISS_IMMUNE); continue; } uint32 split_absorb = 0; DealDamageMods(caster, splitDamage, &split_absorb); - SendSpellNonMeleeDamageLog(caster, (*itr)->GetSpellInfo()->Id, splitDamage, schoolMask, split_absorb, 0, false, 0, false); + SendSpellNonMeleeDamageLog(caster, (*itr)->GetSpellInfo()->Id, splitDamage, damageInfo.GetSchoolMask(), split_absorb, 0, false, 0, false); CleanDamage cleanDamage = CleanDamage(splitDamage, 0, BASE_ATTACK, MELEE_HIT_NORMAL); - DealDamage(caster, splitDamage, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*itr)->GetSpellInfo(), false); + DealDamage(caster, splitDamage, &cleanDamage, DIRECT_DAMAGE, damageInfo.GetSchoolMask(), (*itr)->GetSpellInfo(), false); // break 'Fear' and similar auras - DamageInfo damageInfo(caster, this, splitDamage, (*itr)->GetSpellInfo(), schoolMask, DIRECT_DAMAGE, BASE_ATTACK); ProcSkillsAndAuras(caster, PROC_FLAG_NONE, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_HIT, PROC_HIT_NONE, nullptr, &damageInfo, nullptr); } } - - *resist = dmgInfo.GetResist(); - *absorb = dmgInfo.GetAbsorb(); } -void Unit::CalcHealAbsorb(HealInfo& healInfo) +void Unit::CalcHealAbsorb(HealInfo& healInfo) const { if (!healInfo.GetHeal()) return; @@ -2086,6 +2096,49 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extr } } +void Unit::FakeAttackerStateUpdate(Unit* victim, WeaponAttackType attType /*= BASE_ATTACK*/) +{ + if (HasUnitState(UNIT_STATE_CANNOT_AUTOATTACK) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED)) + return; + + if (!victim->IsAlive()) + return; + + if ((attType == BASE_ATTACK || attType == OFF_ATTACK) && !IsWithinLOSInMap(victim)) + return; + + CombatStart(victim); + RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MELEE_ATTACK); + + if (attType != BASE_ATTACK && attType != OFF_ATTACK) + return; // ignore ranged case + + if (GetTypeId() == TYPEID_UNIT && !HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) + SetFacingToObject(victim); // update client side facing to face the target (prevents visual glitches when casting untargeted spells) + + CalcDamageInfo damageInfo; + damageInfo.attacker = this; + damageInfo.target = victim; + damageInfo.damageSchoolMask = GetMeleeDamageSchoolMask(); + damageInfo.attackType = attType; + damageInfo.damage = 0; + damageInfo.cleanDamage = 0; + damageInfo.absorb = 0; + damageInfo.resist = 0; + damageInfo.blocked_amount = 0; + + damageInfo.TargetState = VICTIMSTATE_HIT; + damageInfo.HitInfo = HITINFO_AFFECTS_VICTIM | HITINFO_NORMALSWING | HITINFO_FAKE_DAMAGE; + if (attType == OFF_ATTACK) + damageInfo.HitInfo |= HITINFO_OFFHAND; + + damageInfo.procAttacker = PROC_FLAG_NONE; + damageInfo.procVictim = PROC_FLAG_NONE; + damageInfo.hitOutCome = MELEE_HIT_NORMAL; + + SendAttackStateUpdate(&damageInfo); +} + void Unit::HandleProcExtraAttackFor(Unit* victim) { while (m_extraAttacks) @@ -2321,7 +2374,7 @@ void Unit::SendMeleeAttackStop(Unit* victim) bool Unit::isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType attackType) { // These spells can't be blocked - if (spellProto && spellProto->HasAttribute(SPELL_ATTR0_IMPOSSIBLE_DODGE_PARRY_BLOCK)) + if (spellProto && (spellProto->HasAttribute(SPELL_ATTR0_IMPOSSIBLE_DODGE_PARRY_BLOCK) || spellProto->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT))) return false; // Can't block when casting/controlled @@ -2330,13 +2383,8 @@ bool Unit::isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttac if (victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION) || victim->HasInArc(float(M_PI), this)) { - // Check creatures flags_extra for disable block - if (victim->GetTypeId() == TYPEID_UNIT && - victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK) - return false; - float blockChance = GetUnitBlockChance(attackType, victim); - if (roll_chance_f(blockChance)) + if (blockChance && roll_chance_f(blockChance)) return true; } @@ -2391,11 +2439,6 @@ bool Unit::CanUseAttackType(uint8 attacktype) const // Melee based spells hit result calculations SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const { - // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will additionally fully ignore - // resist and deflect chances - if (spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) - return SPELL_MISS_NONE; - WeaponAttackType attType = BASE_ATTACK; // Check damage class instead of attack type to correctly handle judgements @@ -2449,7 +2492,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo canDodge = false; // only if in front - if (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)) + if (!victim->HasUnitState(UNIT_STATE_CONTROLLED) && (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION))) { int32 deflect_chance = victim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS) * 100; tmp += deflect_chance; @@ -2543,7 +2586,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const { // Can`t miss on dead target (on skinning for example) - if ((!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER) || spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) + if ((!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER)) return SPELL_MISS_NONE; SpellSchoolMask schoolMask = spellInfo->GetSchoolMask(); @@ -2586,8 +2629,7 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo int32 tmp = 10000 - HitChance; - int32 rand = irand(0, 10000); - + int32 rand = irand(0, 9999); if (rand < tmp) return SPELL_MISS_MISS; @@ -2609,10 +2651,7 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo } if (hasAura) - { - tmp += victim->GetMaxPositiveAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(spellInfo->Dispel)) * 100; - tmp += victim->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(spellInfo->Dispel)) * 100; - } + tmp += victim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, static_cast<int32>(spellInfo->Dispel)) * 100; } // Roll chance @@ -2641,6 +2680,9 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo // Resist SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool CanReflect) { + if (spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) + return SPELL_MISS_NONE; + // Check for immune if (victim->IsImmunedToSpell(spellInfo)) return SPELL_MISS_IMMUNE; @@ -2787,15 +2829,18 @@ float Unit::GetUnitParryChance(WeaponAttackType attType, Unit const* victim) con skillBonus = 0.04f * skillDiff; } } - else if (victim->GetTypeId() == TYPEID_UNIT && !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY)) + else { - chance = 5.0f; - chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT); + if (!victim->IsTotem() && !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY)) + { + chance = 5.0f; + chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT); - if (skillDiff <= 10) - skillBonus = skillDiff * 0.1f; - else - skillBonus = 1.0f + (skillDiff - 10) * 1.6f; + if (skillDiff <= 10) + skillBonus = skillDiff * 0.1f; + else + skillBonus = 1.0f + (skillDiff - 10) * 1.6f; + } } chance += skillBonus; @@ -2864,8 +2909,8 @@ float Unit::GetUnitBlockChance(WeaponAttackType attType, Unit const* victim) con float Unit::GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victim) const { int32 const attackerWeaponSkill = GetWeaponSkillValue(attackType, victim); - int32 const victimMaxSkillValueForLevel = victim->GetMaxSkillValueForLevel(this); - int32 const skillDiff = victimMaxSkillValueForLevel - attackerWeaponSkill; + int32 const victimDefenseSkill = victim->GetDefenseSkillValue(this); + int32 const skillDiff = victimDefenseSkill - attackerWeaponSkill; float chance = 0.0f; float skillBonus = 0.0f; @@ -3259,10 +3304,13 @@ int32 Unit::GetCurrentSpellCastTime(uint32 spell_id) const bool Unit::CanMoveDuringChannel() const { if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL]) - if (spell->getState() != SPELL_STATE_FINISHED) - return spell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING) && spell->IsChannelActive(); + { + if (spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive()) + if (!spell->GetSpellInfo()->IsMoveAllowedChannel()) + return false; + } - return false; + return true; } bool Unit::isInFrontInMap(Unit const* target, float distance, float arc) const @@ -5298,8 +5346,8 @@ void Unit::SendAttackStateUpdate(CalcDamageInfo* damageInfo) { TC_LOG_DEBUG("entities.unit", "WORLD: Sending SMSG_ATTACKERSTATEUPDATE"); - uint32 count = 1; - size_t maxsize = 4+5+5+4+4+1+4+4+4+4+4+1+4+4+4+4+4*12; + uint32 const count = 1; + size_t const maxsize = 4+5+5+4+4+1+4+4+4+4+4+1+4+4+4+4+4*12; WorldPacket data(SMSG_ATTACKERSTATEUPDATE, maxsize); // we guess size data << uint32(damageInfo->HitInfo); data << damageInfo->attacker->GetPackGUID(); @@ -5434,6 +5482,8 @@ FactionTemplateEntry const* Unit::GetFactionTemplateEntry() const TC_LOG_ERROR("entities.unit", "Creature (template id: %u) has invalid faction (faction template id) #%u", creature->GetCreatureTemplate()->Entry, getFaction()); else TC_LOG_ERROR("entities.unit", "Unit (name=%s, type=%u) has invalid faction (faction template id) #%u", GetName().c_str(), uint32(GetTypeId()), getFaction()); + + ABORT(); } return entry; } @@ -5868,17 +5918,14 @@ void Unit::ModifyAuraState(AuraStateType flag, bool apply) { RemoveFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)); - if (flag != AURA_STATE_ENRAGE) // enrage aura state triggering continues auras + Unit::AuraApplicationMap& tAuras = GetAppliedAuras(); + for (Unit::AuraApplicationMap::iterator itr = tAuras.begin(); itr != tAuras.end();) { - Unit::AuraApplicationMap& tAuras = GetAppliedAuras(); - for (Unit::AuraApplicationMap::iterator itr = tAuras.begin(); itr != tAuras.end();) - { - SpellInfo const* spellProto = (*itr).second->GetBase()->GetSpellInfo(); - if (spellProto->CasterAuraState == uint32(flag)) - RemoveAura(itr); - else - ++itr; - } + SpellInfo const* spellProto = itr->second->GetBase()->GetSpellInfo(); + if (itr->second->GetBase()->GetCasterGUID() == GetGUID() && spellProto->CasterAuraState == uint32(flag) && (spellProto->IsPassive() || flag != AURA_STATE_ENRAGE)) + RemoveAura(itr); + else + ++itr; } } } @@ -6553,7 +6600,7 @@ void Unit::SendHealSpellLog(HealInfo& healInfo, bool critical /*= false*/) // we guess size WorldPacket data(SMSG_SPELLHEALLOG, 8 + 8 + 4 + 4 + 4 + 4 + 1 + 1); data << healInfo.GetTarget()->GetPackGUID(); - data << GetPackGUID(); + data << healInfo.GetHealer()->GetPackGUID(); data << uint32(healInfo.GetSpellInfo()->Id); data << uint32(healInfo.GetHeal()); data << uint32(healInfo.GetHeal() - healInfo.GetEffectiveHeal()); @@ -7820,18 +7867,18 @@ int32 Unit::SpellBaseHealingBonusTaken(SpellSchoolMask schoolMask) const return advertisedBenefit; } -bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask) const +bool Unit::IsImmunedToDamage(SpellSchoolMask schoolMask) const { // If m_immuneToSchool type contain this school type, IMMUNE damage. - SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; - for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr) - if (itr->type & shoolMask) + SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; + for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr) + if ((itr->first & schoolMask) != 0) return true; // If m_immuneToDamage type contain magic, IMMUNE damage. - SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE]; - for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr) - if (itr->type & shoolMask) + SpellImmuneContainer const& damageList = m_spellImmune[IMMUNITY_DAMAGE]; + for (auto itr = damageList.begin(); itr != damageList.end(); ++itr) + if ((itr->first & schoolMask) != 0) return true; return false; @@ -7839,23 +7886,20 @@ bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask) const bool Unit::IsImmunedToDamage(SpellInfo const* spellInfo) const { - if (spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) + if (spellInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) || spellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) return false; - uint32 shoolMask = spellInfo->GetSchoolMask(); - if (spellInfo->Id != 42292 && spellInfo->Id != 59752) - { - // If m_immuneToSchool type contain this school type, IMMUNE damage. - SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; - for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr) - if (itr->type & shoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->spellId))) - return true; - } + uint32 schoolMask = spellInfo->GetSchoolMask(); + // If m_immuneToSchool type contain this school type, IMMUNE damage. + SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; + for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr) + if ((itr->first & schoolMask) && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->second))) + return true; // If m_immuneToDamage type contain magic, IMMUNE damage. - SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE]; - for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr) - if (itr->type & shoolMask) + SpellImmuneContainer const& damageList = m_spellImmune[IMMUNITY_DAMAGE]; + for (auto itr = damageList.begin(); itr != damageList.end(); ++itr) + if ((itr->first & schoolMask) != 0) return true; return false; @@ -7867,29 +7911,26 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo) const return false; // Single spell immunity. - SpellImmuneList const& idList = m_spellImmune[IMMUNITY_ID]; - for (SpellImmuneList::const_iterator itr = idList.begin(); itr != idList.end(); ++itr) - if (itr->type == spellInfo->Id) - return true; + SpellImmuneContainer const& idList = m_spellImmune[IMMUNITY_ID]; + if (idList.count(spellInfo->Id) > 0) + return true; if (spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return false; - if (spellInfo->Dispel) + if (uint32 dispel = spellInfo->Dispel) { - SpellImmuneList const& dispelList = m_spellImmune[IMMUNITY_DISPEL]; - for (SpellImmuneList::const_iterator itr = dispelList.begin(); itr != dispelList.end(); ++itr) - if (itr->type == spellInfo->Dispel) - return true; + SpellImmuneContainer const& dispelList = m_spellImmune[IMMUNITY_DISPEL]; + if (dispelList.count(dispel) > 0) + return true; } // Spells that don't have effectMechanics. - if (spellInfo->Mechanic) + if (uint32 mechanic = spellInfo->Mechanic) { - SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; - for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) - if (itr->type == spellInfo->Mechanic) - return true; + SpellImmuneContainer const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; + if (mechanicList.count(mechanic) > 0) + return true; } bool immuneToAllEffects = true; @@ -7897,8 +7938,6 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo) const { // State/effect immunities applied by aura expect full spell immunity // Ignore effects with mechanic, they are supposed to be checked separately - if (!spellInfo->Effects[i].IsEffect()) - continue; if (!IsImmunedToSpellEffect(spellInfo, i)) { immuneToAllEffects = false; @@ -7909,13 +7948,13 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo) const if (immuneToAllEffects) //Return immune only if the target is immune to all spell effects. return true; - if (spellInfo->Id != 42292 && spellInfo->Id != 59752) + if (!spellInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) && !spellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) { - SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; - for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr) + SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; + for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr) { - SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->spellId); - if ((itr->type & spellInfo->GetSchoolMask()) + SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->second); + if ((itr->first & spellInfo->GetSchoolMask()) && !(immuneSpellInfo && immuneSpellInfo->IsPositive() && spellInfo->IsPositive()) && !spellInfo->CanPierceImmuneAura(immuneSpellInfo)) return true; @@ -7928,9 +7967,9 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo) const uint32 Unit::GetSchoolImmunityMask() const { uint32 mask = 0; - SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_SCHOOL]; - for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) - mask |= itr->type; + SpellImmuneContainer const& mechanicList = m_spellImmune[IMMUNITY_SCHOOL]; + for (auto itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) + mask |= itr->first; return mask; } @@ -7938,9 +7977,9 @@ uint32 Unit::GetSchoolImmunityMask() const uint32 Unit::GetMechanicImmunityMask() const { uint32 mask = 0; - SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; - for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) - mask |= (1 << itr->type); + SpellImmuneContainer const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; + for (auto itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) + mask |= (1 << itr->first); return mask; } @@ -7955,33 +7994,35 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) cons // If m_immuneToEffect type contain this effect type, IMMUNE effect. uint32 effect = spellInfo->Effects[index].Effect; - SpellImmuneList const& effectList = m_spellImmune[IMMUNITY_EFFECT]; - for (SpellImmuneList::const_iterator itr = effectList.begin(); itr != effectList.end(); ++itr) - if (itr->type == effect) - return true; + auto const& effectList = m_spellImmune[IMMUNITY_EFFECT]; + if (effectList.count(effect) > 0) + return true; if (uint32 mechanic = spellInfo->Effects[index].Mechanic) { - SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; - for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) - if (itr->type == mechanic) - return true; + auto const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; + if (mechanicList.count(mechanic) > 0) + return true; } - if (uint32 aura = spellInfo->Effects[index].ApplyAuraName) + if (!spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) { - SpellImmuneList const& list = m_spellImmune[IMMUNITY_STATE]; - for (SpellImmuneList::const_iterator itr = list.begin(); itr != list.end(); ++itr) - if (itr->type == aura) - if (!spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) - return true; - - // Check for immune to application of harmful magical effects - AuraEffectList const& immuneAuraApply = GetAuraEffectsByType(SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL); - for (AuraEffectList::const_iterator iter = immuneAuraApply.begin(); iter != immuneAuraApply.end(); ++iter) - if (((*iter)->GetMiscValue() & spellInfo->GetSchoolMask()) && // Check school - !spellInfo->IsPositiveEffect(index)) // Harmful + if (uint32 aura = spellInfo->Effects[index].ApplyAuraName) + { + SpellImmuneContainer const& list = m_spellImmune[IMMUNITY_STATE]; + if (list.count(aura) > 0) return true; + + if (!spellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) + { + // Check for immune to application of harmful magical effects + AuraEffectList const& immuneAuraApply = GetAuraEffectsByType(SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL); + for (AuraEffectList::const_iterator iter = immuneAuraApply.begin(); iter != immuneAuraApply.end(); ++iter) + if (((*iter)->GetMiscValue() & spellInfo->GetSchoolMask()) && // Check school + !spellInfo->IsPositiveEffect(index)) // Harmful + return true; + } + } } return false; @@ -8287,52 +8328,14 @@ uint32 Unit::MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackT void Unit::ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply) { if (apply) - { - for (SpellImmuneList::iterator itr = m_spellImmune[op].begin(), next; itr != m_spellImmune[op].end(); itr = next) - { - next = itr; ++next; - if (itr->type == type) - { - m_spellImmune[op].erase(itr); - next = m_spellImmune[op].begin(); - } - } - SpellImmune Immune; - Immune.spellId = spellId; - Immune.type = type; - m_spellImmune[op].push_back(Immune); - } + m_spellImmune[op].emplace(type, spellId); else { - for (SpellImmuneList::iterator itr = m_spellImmune[op].begin(); itr != m_spellImmune[op].end(); ++itr) + auto bounds = m_spellImmune[op].equal_range(type); + for (auto itr = bounds.first; itr != bounds.second;) { - if (itr->spellId == spellId && itr->type == type) - { - m_spellImmune[op].erase(itr); - break; - } - } - } -} - -void Unit::ApplySpellDispelImmunity(const SpellInfo* spellProto, DispelType type, bool apply) -{ - ApplySpellImmune(spellProto->Id, IMMUNITY_DISPEL, type, apply); - - if (apply && spellProto->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) - { - // Create dispel mask by dispel type - uint32 dispelMask = SpellInfo::GetDispelMask(type); - // Dispel all existing auras vs current dispel type - AuraApplicationMap& auras = GetAppliedAuras(); - for (AuraApplicationMap::iterator itr = auras.begin(); itr != auras.end();) - { - SpellInfo const* spell = itr->second->GetBase()->GetSpellInfo(); - if (spell->GetDispelMask() & dispelMask) - { - // Dispel aura - RemoveAura(itr); - } + if (itr->second == spellId) + itr = m_spellImmune[op].erase(itr); else ++itr; } @@ -8650,13 +8653,9 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo || (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster())) return false; - // can't attack own vehicle or passenger - if (m_vehicle) - if (IsOnVehicle(target) || m_vehicle->GetBase()->IsOnVehicle(target)) - return false; - // can't attack invisible (ignore stealth for aoe spells) also if the area being looked at is from a spell use the dynamic object created instead of the casting unit. Ignore stealth if target is player and unit in combat with same player - if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) && (obj ? !obj->CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()) : !CanSeeOrDetect(target, (bySpell && bySpell->IsAffectingArea()) || (target->GetTypeId() == TYPEID_PLAYER && target->HasStealthAura() && target->IsInCombat() && IsInCombatWith(target))))) + // skip visibility check for GO casts, needs removal when go cast is implemented + if (GetEntry() != WORLD_TRIGGER && (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) && (obj ? !obj->CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()) : !CanSeeOrDetect(target, (bySpell && bySpell->IsAffectingArea()) || (target->GetTypeId() == TYPEID_PLAYER && target->HasStealthAura() && target->IsInCombat() && IsInCombatWith(target))))) return false; // can't attack dead @@ -9724,63 +9723,53 @@ void Unit::ModSpellDurationTime(SpellInfo const* spellInfo, int32 & duration, Sp DiminishingLevels Unit::GetDiminishing(DiminishingGroup group) { - for (Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i) - { - if (i->DRGroup != group) - continue; - - if (!i->hitCount) - return DIMINISHING_LEVEL_1; + DiminishingReturn& diminish = m_Diminishing[group]; + if (!diminish.hitCount) + return DIMINISHING_LEVEL_1; - if (!i->hitTime) - return DIMINISHING_LEVEL_1; - - // If last spell was cast more than 15 seconds ago - reset the count. - if (i->stack == 0 && getMSTimeDiff(i->hitTime, getMSTime()) > 15000) - { - i->hitCount = DIMINISHING_LEVEL_1; - return DIMINISHING_LEVEL_1; - } - // or else increase the count. - else - return DiminishingLevels(i->hitCount); + // If last spell was cast more than 15 seconds ago - reset the count. + if (!diminish.stack && GetMSTimeDiffToNow(diminish.hitTime) > 15000) + { + diminish.hitCount = DIMINISHING_LEVEL_1; + return DIMINISHING_LEVEL_1; } - return DIMINISHING_LEVEL_1; + + return DiminishingLevels(diminish.hitCount); } -void Unit::IncrDiminishing(DiminishingGroup group) +void Unit::IncrDiminishing(SpellInfo const* auraSpellInfo, bool triggered) { + DiminishingGroup const group = auraSpellInfo->GetDiminishingReturnsGroupForSpell(triggered); + DiminishingLevels const maxLevel = auraSpellInfo->GetDiminishingReturnsMaxLevel(triggered); + // Checking for existing in the table - for (Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i) - { - if (i->DRGroup != group) - continue; - if (int32(i->hitCount) < GetDiminishingReturnsMaxLevel(group)) - i->hitCount += 1; - return; - } - m_Diminishing.push_back(DiminishingReturn(group, getMSTime(), DIMINISHING_LEVEL_2)); + DiminishingReturn& diminish = m_Diminishing[group]; + if (static_cast<int32>(diminish.hitCount) < maxLevel) + ++diminish.hitCount; } -float Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration, Unit* caster, DiminishingLevels Level, int32 limitduration) +float Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, bool triggered, int32& duration, Unit* caster, DiminishingLevels previousLevel) { + DiminishingGroup const group = auraSpellInfo->GetDiminishingReturnsGroupForSpell(triggered); if (duration == -1 || group == DIMINISHING_NONE) return 1.0f; + int32 const limitDuration = auraSpellInfo->GetDiminishingReturnsLimitDuration(triggered); + // test pet/charm masters instead pets/charmeds - Unit const* tarGetOwner = GetCharmerOrOwner(); + Unit const* targetOwner = GetCharmerOrOwner(); Unit const* casterOwner = caster->GetCharmerOrOwner(); // Duration of crowd control abilities on pvp target is limited by 10 sec. (2.2.0) - if (limitduration > 0 && duration > limitduration) + if (limitDuration > 0 && duration > limitDuration) { - Unit const* target = tarGetOwner ? tarGetOwner : this; + Unit const* target = targetOwner ? targetOwner : this; Unit const* source = casterOwner ? casterOwner : caster; if ((target->GetTypeId() == TYPEID_PLAYER || target->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_ALL_DIMINISH) && source->GetTypeId() == TYPEID_PLAYER) - duration = limitduration; + duration = limitDuration; } float mod = 1.0f; @@ -9789,7 +9778,7 @@ float Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration, { if (GetTypeId() == TYPEID_UNIT && (ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_TAUNT_DIMINISH)) { - DiminishingLevels diminish = Level; + DiminishingLevels diminish = previousLevel; switch (diminish) { case DIMINISHING_LEVEL_1: break; @@ -9802,12 +9791,12 @@ float Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration, } } // Some diminishings applies to mobs too (for example, Stun) - else if ((GetDiminishingReturnsGroupType(group) == DRTYPE_PLAYER - && ((tarGetOwner ? (tarGetOwner->GetTypeId() == TYPEID_PLAYER) : (GetTypeId() == TYPEID_PLAYER)) + else if ((auraSpellInfo->GetDiminishingReturnsGroupType(triggered) == DRTYPE_PLAYER + && ((targetOwner ? (targetOwner->GetTypeId() == TYPEID_PLAYER) : (GetTypeId() == TYPEID_PLAYER)) || (GetTypeId() == TYPEID_UNIT && ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_ALL_DIMINISH))) - || GetDiminishingReturnsGroupType(group) == DRTYPE_ALL) + || auraSpellInfo->GetDiminishingReturnsGroupType(triggered) == DRTYPE_ALL) { - DiminishingLevels diminish = Level; + DiminishingLevels diminish = previousLevel; switch (diminish) { case DIMINISHING_LEVEL_1: break; @@ -9825,24 +9814,26 @@ float Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration, void Unit::ApplyDiminishingAura(DiminishingGroup group, bool apply) { // Checking for existing in the table - for (Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i) + DiminishingReturn& diminish = m_Diminishing[group]; + + if (apply) + ++diminish.stack; + else if (diminish.stack) { - if (i->DRGroup != group) - continue; + --diminish.stack; - if (apply) - i->stack += 1; - else if (i->stack) - { - i->stack -= 1; - // Remember time after last aura from group removed - if (i->stack == 0) - i->hitTime = getMSTime(); - } - break; + // Remember time after last aura from group removed + if (!diminish.stack) + diminish.hitTime = getMSTime(); } } +void Unit::ClearDiminishings() +{ + for (uint32 i = 0; i < DIMINISHING_MAX; ++i) + m_Diminishing[i].Clear(); +} + float Unit::GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const { if (!spellInfo->RangeEntry) @@ -10900,7 +10891,7 @@ void Unit::GetProcAurasTriggeredOnEvent(AuraApplicationProcContainer& aurasTrigg if (uint8 procEffectMask = aurApp->GetBase()->IsProcTriggeredOnEvent(aurApp, eventInfo, now)) { aurApp->GetBase()->PrepareProcToTrigger(aurApp, eventInfo, now); - aurasTriggeringProc.emplace_back(std::make_pair(procEffectMask, aurApp)); + aurasTriggeringProc.emplace_back(procEffectMask, aurApp); } } } @@ -10912,7 +10903,7 @@ void Unit::GetProcAurasTriggeredOnEvent(AuraApplicationProcContainer& aurasTrigg if (uint8 procEffectMask = itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo, now)) { itr->second->GetBase()->PrepareProcToTrigger(itr->second, eventInfo, now); - aurasTriggeringProc.emplace_back(std::make_pair(procEffectMask, itr->second)); + aurasTriggeringProc.emplace_back(procEffectMask, itr->second); } } } @@ -10932,6 +10923,21 @@ void Unit::TriggerAurasProcOnEvent(Unit* actionTarget, uint32 typeMaskActor, uin { AuraApplicationProcContainer myAurasTriggeringProc; GetProcAurasTriggeredOnEvent(myAurasTriggeringProc, nullptr, myProcEventInfo); + + // needed for example for Cobra Strikes, pet does the attack, but aura is on owner + if (Player* modOwner = GetSpellModOwner()) + { + if (modOwner != this && spell) + { + AuraApplicationList modAuras; + for (auto itr = modOwner->GetAppliedAuras().begin(); itr != modOwner->GetAppliedAuras().end(); ++itr) + { + if (spell->m_appliedMods.count(itr->second->GetBase()) != 0) + modAuras.push_back(itr->second); + } + modOwner->GetProcAurasTriggeredOnEvent(myAurasTriggeringProc, &modAuras, myProcEventInfo); + } + } TriggerAurasProcOnEvent(myProcEventInfo, myAurasTriggeringProc); } @@ -10947,6 +10953,11 @@ void Unit::TriggerAurasProcOnEvent(Unit* actionTarget, uint32 typeMaskActor, uin void Unit::TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationProcContainer& aurasTriggeringProc) { + Spell const* triggeringSpell = eventInfo.GetProcSpell(); + bool const disableProcs = triggeringSpell && triggeringSpell->IsProcDisabled(); + if (disableProcs) + SetCantProc(true); + for (auto const& aurAppProc : aurasTriggeringProc) { AuraApplication* aurApp; @@ -10965,6 +10976,9 @@ void Unit::TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationProc if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) SetCantProc(false); } + + if (disableProcs) + SetCantProc(false); } SpellSchoolMask Unit::GetMeleeDamageSchoolMask() const @@ -12461,8 +12475,9 @@ bool Unit::IsInPartyWith(Unit const* unit) const else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) return true; - else - return false; + + // else u1->GetTypeId() == u2->GetTypeId() == TYPEID_UNIT + return u1->getFaction() == u2->getFaction(); } bool Unit::IsInRaidWith(Unit const* unit) const @@ -12480,8 +12495,9 @@ bool Unit::IsInRaidWith(Unit const* unit) const else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) return true; - else - return false; + + // else u1->GetTypeId() == u2->GetTypeId() == TYPEID_UNIT + return u1->getFaction() == u2->getFaction(); } void Unit::GetPartyMembers(std::list<Unit*> &TagUnitMap) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 525d0d54ecb..f2135583c78 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -324,20 +324,21 @@ enum HitInfo HITINFO_FULL_RESIST = 0x00000080, HITINFO_PARTIAL_RESIST = 0x00000100, HITINFO_CRITICALHIT = 0x00000200, // critical hit - // 0x00000400 - // 0x00000800 - // 0x00001000 + HITINFO_UNK10 = 0x00000400, + HITINFO_UNK11 = 0x00000800, + HITINFO_UNK12 = 0x00001000, HITINFO_BLOCK = 0x00002000, // blocked damage - // 0x00004000 // Hides worldtext for 0 damage - // 0x00008000 // Related to blood visual + HITINFO_UNK14 = 0x00004000, // set only if meleespellid is present// no world text when victim is hit for 0 dmg(HideWorldTextForNoDamage?) + HITINFO_UNK15 = 0x00008000, // player victim?// something related to blod sprut visual (BloodSpurtInBack?) HITINFO_GLANCING = 0x00010000, HITINFO_CRUSHING = 0x00020000, HITINFO_NO_ANIMATION = 0x00040000, - // 0x00080000 - // 0x00100000 + HITINFO_UNK19 = 0x00080000, + HITINFO_UNK20 = 0x00100000, HITINFO_SWINGNOHITSOUND = 0x00200000, // unused? - // 0x00400000 - HITINFO_RAGE_GAIN = 0x00800000 + HITINFO_UNK22 = 0x00400000, + HITINFO_RAGE_GAIN = 0x00800000, + HITINFO_FAKE_DAMAGE = 0x01000000 // enables damage animation even if no damage done, set only if no damage }; //i would like to remove this: (it is defined in item.h @@ -376,13 +377,7 @@ class SpellCastTargets; typedef std::list<Unit*> UnitList; typedef std::list<std::pair<Aura*, uint8>> DispelChargesList; -struct SpellImmune -{ - uint32 type; - uint32 spellId; -}; - -typedef std::list<SpellImmune> SpellImmuneList; +typedef std::unordered_multimap<uint32 /*type*/, uint32 /*spellId*/> SpellImmuneContainer; enum UnitModifierType { @@ -424,12 +419,18 @@ enum TriggerCastFlags TRIGGERED_IGNORE_SET_FACING = 0x00000200, //! Will not adjust facing to target (if any) TRIGGERED_IGNORE_SHAPESHIFT = 0x00000400, //! Will ignore shapeshift checks TRIGGERED_IGNORE_CASTER_AURASTATE = 0x00000800, //! Will ignore caster aura states including combat requirements and death state + TRIGGERED_DISALLOW_PROC_EVENTS = 0x00001000, //! Disallows proc events from triggered spell (default) TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE = 0x00002000, //! Will ignore mounted/on vehicle restrictions + // reuse = 0x00004000, + // reuse = 0x00008000, TRIGGERED_IGNORE_CASTER_AURAS = 0x00010000, //! Will ignore caster aura restrictions or requirements TRIGGERED_DONT_RESET_PERIODIC_TIMER = 0x00020000, //! Will allow periodic aura timers to keep ticking (instead of resetting) TRIGGERED_DONT_REPORT_CAST_ERROR = 0x00040000, //! Will return SPELL_FAILED_DONT_REPORT in CheckCast functions + TRIGGERED_FULL_MASK = 0x0007FFFF, //! Used when doing CastSpell with triggered == true + + // debug flags (used with .cast triggered commands) TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT = 0x00080000, //! Will ignore equipped item requirements - TRIGGERED_FULL_MASK = 0xFFFFFFFF + TRIGGERED_FULL_DEBUG_MASK = 0xFFFFFFFF }; enum UnitMods @@ -801,11 +802,15 @@ namespace Movement{ struct DiminishingReturn { - DiminishingReturn(DiminishingGroup group, uint32 t, uint32 count) - : DRGroup(group), stack(0), hitTime(t), hitCount(count) - { } + DiminishingReturn() : stack(0), hitTime(0), hitCount(DIMINISHING_LEVEL_1) { } + + void Clear() + { + stack = 0; + hitTime = 0; + hitCount = DIMINISHING_LEVEL_1; + } - DiminishingGroup DRGroup; uint16 stack; uint32 hitTime; uint32 hitCount; @@ -1255,9 +1260,9 @@ class TC_GAME_API Unit : public WorldObject typedef std::list<AuraEffect*> AuraEffectList; typedef std::list<Aura*> AuraList; typedef std::list<AuraApplication *> AuraApplicationList; - typedef std::list<DiminishingReturn> Diminishing; + typedef std::array<DiminishingReturn, DIMINISHING_MAX> Diminishing; - typedef std::deque<std::pair<uint8 /*procEffectMask*/, AuraApplication*>> AuraApplicationProcContainer; + typedef std::vector<std::pair<uint8 /*procEffectMask*/, AuraApplication*>> AuraApplicationProcContainer; typedef std::map<uint8, AuraApplication*> VisibleAuraMap; @@ -1272,11 +1277,11 @@ class TC_GAME_API Unit : public WorldObject void CleanupBeforeRemoveFromMap(bool finalCleanup); void CleanupsBeforeDelete(bool finalCleanup = true) override; // used in ~Creature/~Player (or before mass creature delete to remove cross-references to already deleted units) - DiminishingLevels GetDiminishing(DiminishingGroup group); - void IncrDiminishing(DiminishingGroup group); - float ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration, Unit* caster, DiminishingLevels Level, int32 limitduration); - void ApplyDiminishingAura(DiminishingGroup group, bool apply); - void ClearDiminishings() { m_Diminishing.clear(); } + DiminishingLevels GetDiminishing(DiminishingGroup group); + void IncrDiminishing(SpellInfo const* auraSpellInfo, bool triggered); + float ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, bool triggered, int32& duration, Unit* caster, DiminishingLevels previousLevel); + void ApplyDiminishingAura(DiminishingGroup group, bool apply); + void ClearDiminishings(); // target dependent range checks float GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const; @@ -1428,7 +1433,7 @@ class TC_GAME_API Unit : public WorldObject void Dismount(); uint16 GetMaxSkillValueForLevel(Unit const* target = NULL) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; } - void DealDamageMods(Unit* victim, uint32 &damage, uint32* absorb); + void DealDamageMods(Unit const* victim, uint32 &damage, uint32* absorb) const; uint32 DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDamage = NULL, DamageEffectType damagetype = DIRECT_DAMAGE, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* spellProto = NULL, bool durabilityLoss = true); void Kill(Unit* victim, bool durabilityLoss = true); void KillSelf(bool durabilityLoss = true) { Kill(this, durabilityLoss); } @@ -1447,6 +1452,7 @@ class TC_GAME_API Unit : public WorldObject void HandleEmoteCommand(uint32 anim_id); void AttackerStateUpdate (Unit* victim, WeaponAttackType attType = BASE_ATTACK, bool extra = false); + void FakeAttackerStateUpdate(Unit* victim, WeaponAttackType attType = BASE_ATTACK); void CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* damageInfo, WeaponAttackType attackType = BASE_ATTACK); void DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss); @@ -1556,17 +1562,18 @@ class TC_GAME_API Unit : public WorldObject void EnergizeBySpell(Unit* victim, uint32 SpellID, int32 Damage, Powers powertype); uint32 SpellNonMeleeDamageLog(Unit* victim, uint32 spellID, uint32 damage); - void CastSpell(SpellCastTargets const& targets, SpellInfo const* spellInfo, CustomSpellValues const* value, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = NULL, AuraEffect const* triggeredByAura = NULL, ObjectGuid originalCaster = ObjectGuid::Empty); - void CastSpell(Unit* victim, uint32 spellId, bool triggered, Item* castItem = NULL, AuraEffect const* triggeredByAura = NULL, ObjectGuid originalCaster = ObjectGuid::Empty); - void CastSpell(Unit* victim, uint32 spellId, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = NULL, AuraEffect const* triggeredByAura = NULL, ObjectGuid originalCaster = ObjectGuid::Empty); - void CastSpell(Unit* victim, SpellInfo const* spellInfo, bool triggered, Item* castItem = NULL, AuraEffect const* triggeredByAura = NULL, ObjectGuid originalCaster = ObjectGuid::Empty); - void CastSpell(Unit* victim, SpellInfo const* spellInfo, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = NULL, AuraEffect const* triggeredByAura = NULL, ObjectGuid originalCaster = ObjectGuid::Empty); - void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item* castItem = NULL, AuraEffect const* triggeredByAura = NULL, ObjectGuid originalCaster = ObjectGuid::Empty); - void CastSpell(GameObject* go, uint32 spellId, bool triggered, Item* castItem = NULL, AuraEffect* triggeredByAura = NULL, ObjectGuid originalCaster = ObjectGuid::Empty); - void CastCustomSpell(Unit* victim, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item* castItem = NULL, AuraEffect const* triggeredByAura = NULL, ObjectGuid originalCaster = ObjectGuid::Empty); - void CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* victim, bool triggered, Item* castItem = NULL, AuraEffect const* triggeredByAura = NULL, ObjectGuid originalCaster = ObjectGuid::Empty); - void CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* victim = NULL, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = NULL, AuraEffect const* triggeredByAura = NULL, ObjectGuid originalCaster = ObjectGuid::Empty); - void CastCustomSpell(uint32 spellId, CustomSpellValues const &value, Unit* victim = NULL, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = NULL, AuraEffect const* triggeredByAura = NULL, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastSpell(SpellCastTargets const& targets, SpellInfo const* spellInfo, CustomSpellValues const* value, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastSpell(Unit* victim, uint32 spellId, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastSpell(Unit* victim, uint32 spellId, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastSpell(Unit* victim, SpellInfo const* spellInfo, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastSpell(Unit* victim, SpellInfo const* spellInfo, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastSpell(float x, float y, float z, uint32 spellId, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastSpell(GameObject* go, uint32 spellId, bool triggered, Item* castItem = nullptr, AuraEffect* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastCustomSpell(Unit* victim, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* victim, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* victim = nullptr, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastCustomSpell(uint32 spellId, CustomSpellValues const &value, Unit* victim = nullptr, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); Aura* AddAura(uint32 spellId, Unit* target); Aura* AddAura(SpellInfo const* spellInfo, uint8 effMask, Unit* target); void SetAuraStack(uint32 spellId, Unit* target, uint32 stack); @@ -1930,7 +1937,7 @@ class TC_GAME_API Unit : public WorldObject void SetPhaseMask(uint32 newPhaseMask, bool update) override;// overwrite WorldObject::SetPhaseMask void UpdateObjectVisibility(bool forced = true) override; - SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY]; + SpellImmuneContainer m_spellImmune[MAX_SPELL_IMMUNITY]; uint32 m_lastSanctuaryTime; // Threat related methods @@ -2015,7 +2022,6 @@ class TC_GAME_API Unit : public WorldObject uint32 GetRemainingPeriodicAmount(ObjectGuid caster, uint32 spellId, AuraType auraType, uint8 effectIndex = 0) const; void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply); - void ApplySpellDispelImmunity(const SpellInfo* spellProto, DispelType type, bool apply); virtual bool IsImmunedToSpell(SpellInfo const* spellInfo) const; // redefined in Creature uint32 GetSchoolImmunityMask() const; uint32 GetMechanicImmunityMask() const; @@ -2027,8 +2033,8 @@ class TC_GAME_API Unit : public WorldObject static bool IsDamageReducedByArmor(SpellSchoolMask damageSchoolMask, SpellInfo const* spellInfo = NULL, uint8 effIndex = MAX_SPELL_EFFECTS); uint32 CalcArmorReducedDamage(Unit* victim, const uint32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType = MAX_ATTACK); uint32 CalcSpellResistance(Unit* victim, SpellSchoolMask schoolMask, SpellInfo const* spellInfo) const; - void CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffectType damagetype, uint32 const damage, uint32* absorb, uint32* resist, SpellInfo const* spellInfo = NULL); - void CalcHealAbsorb(HealInfo& healInfo); + void CalcAbsorbResist(DamageInfo& damageInfo); + void CalcHealAbsorb(HealInfo& healInfo) const; void UpdateSpeed(UnitMoveType mtype); float GetSpeed(UnitMoveType mtype) const; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 781d30f148d..e594cf7f289 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -804,7 +804,10 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction); if (!factionTemplate) - TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has non-existing faction template (%u).", cInfo->Entry, cInfo->faction); + { + TC_LOG_FATAL("sql.sql", "Creature (Entry: %u) has non-existing faction template (%u). This can lead to crashes, aborting.", cInfo->Entry, cInfo->faction); + ABORT(); + } // used later for scale CreatureDisplayInfoEntry const* displayScaleEntry = nullptr; @@ -2545,23 +2548,23 @@ void ObjectMgr::LoadItemTemplates() itemTemplate.Quality = ITEM_QUALITY_NORMAL; } - if (itemTemplate.Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) + if (itemTemplate.Flags2 & ITEM_FLAG2_FACTION_HORDE) { if (FactionEntry const* faction = sFactionStore.LookupEntry(HORDE)) if ((itemTemplate.AllowableRace & faction->BaseRepRaceMask[0]) == 0) - TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has value (%u) in `AllowableRace` races, not compatible with ITEM_FLAGS_EXTRA_HORDE_ONLY (%u) in Flags field, item cannot be equipped or used by these races.", - entry, itemTemplate.AllowableRace, ITEM_FLAGS_EXTRA_HORDE_ONLY); + TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has value (%u) in `AllowableRace` races, not compatible with ITEM_FLAG2_FACTION_HORDE (%u) in Flags field, item cannot be equipped or used by these races.", + entry, itemTemplate.AllowableRace, ITEM_FLAG2_FACTION_HORDE); - if (itemTemplate.Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) - TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has value (%u) in `Flags2` flags (ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) and ITEM_FLAGS_EXTRA_HORDE_ONLY (%u) in Flags field, this is a wrong combination.", - entry, ITEM_FLAGS_EXTRA_ALLIANCE_ONLY, ITEM_FLAGS_EXTRA_HORDE_ONLY); + if (itemTemplate.Flags2 & ITEM_FLAG2_FACTION_ALLIANCE) + TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has value (%u) in `Flags2` flags (ITEM_FLAG2_FACTION_ALLIANCE) and ITEM_FLAG2_FACTION_HORDE (%u) in Flags field, this is a wrong combination.", + entry, ITEM_FLAG2_FACTION_ALLIANCE, ITEM_FLAG2_FACTION_HORDE); } - else if (itemTemplate.Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) + else if (itemTemplate.Flags2 & ITEM_FLAG2_FACTION_ALLIANCE) { if (FactionEntry const* faction = sFactionStore.LookupEntry(ALLIANCE)) if ((itemTemplate.AllowableRace & faction->BaseRepRaceMask[0]) == 0) - TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has value (%u) in `AllowableRace` races, not compatible with ITEM_FLAGS_EXTRA_ALLIANCE_ONLY (%u) in Flags field, item cannot be equipped or used by these races.", - entry, itemTemplate.AllowableRace, ITEM_FLAGS_EXTRA_ALLIANCE_ONLY); + TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has value (%u) in `AllowableRace` races, not compatible with ITEM_FLAG2_FACTION_ALLIANCE (%u) in Flags field, item cannot be equipped or used by these races.", + entry, itemTemplate.AllowableRace, ITEM_FLAG2_FACTION_ALLIANCE); } if (itemTemplate.BuyCount <= 0) diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index ea0a4da8d62..a30d92ad68a 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -428,7 +428,7 @@ struct BroadcastText std::string const& GetText(LocaleConstant locale = DEFAULT_LOCALE, uint8 gender = GENDER_MALE, bool forceGender = false) const { - if (gender == GENDER_FEMALE && (forceGender || !FemaleText[DEFAULT_LOCALE].empty())) + if ((gender == GENDER_FEMALE || gender == GENDER_NONE) && (forceGender || !FemaleText[DEFAULT_LOCALE].empty())) { if (FemaleText.size() > size_t(locale) && !FemaleText[locale].empty()) return FemaleText[locale]; diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 317378dfa8b..263ba355865 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -642,6 +642,7 @@ namespace Trinity { public: GameObjectFocusCheck(Unit const* unit, uint32 focusId) : i_unit(unit), i_focusId(focusId) { } + bool operator()(GameObject* go) const { if (go->GetGOInfo()->type != GAMEOBJECT_TYPE_SPELL_FOCUS) @@ -657,6 +658,7 @@ namespace Trinity return go->IsWithinDistInMap(i_unit, dist); } + private: Unit const* i_unit; uint32 i_focusId; @@ -667,6 +669,7 @@ namespace Trinity { public: NearestGameObjectFishingHole(WorldObject const& obj, float range) : i_obj(obj), i_range(range) { } + bool operator()(GameObject* go) { if (go->GetGOInfo()->type == GAMEOBJECT_TYPE_FISHINGHOLE && go->isSpawned() && i_obj.IsWithinDistInMap(go, i_range) && i_obj.IsWithinDistInMap(go, (float)go->GetGOInfo()->fishinghole.radius)) @@ -676,19 +679,20 @@ namespace Trinity } return false; } - float GetLastRange() const { return i_range; } + private: WorldObject const& i_obj; - float i_range; + float i_range; // prevent clone - NearestGameObjectFishingHole(NearestGameObjectFishingHole const&); + NearestGameObjectFishingHole(NearestGameObjectFishingHole const&) = delete; }; class NearestGameObjectCheck { public: - NearestGameObjectCheck(WorldObject const& obj) : i_obj(obj), i_range(999) { } + NearestGameObjectCheck(WorldObject const& obj) : i_obj(obj), i_range(999.f) { } + bool operator()(GameObject* go) { if (i_obj.IsWithinDistInMap(go, i_range)) @@ -698,13 +702,13 @@ namespace Trinity } return false; } - float GetLastRange() const { return i_range; } + private: WorldObject const& i_obj; float i_range; // prevent clone this object - NearestGameObjectCheck(NearestGameObjectCheck const&); + NearestGameObjectCheck(NearestGameObjectCheck const&) = delete; }; // Success at unit in range, range update for next check (this can be use with GameobjectLastSearcher to find nearest GO) @@ -712,6 +716,7 @@ namespace Trinity { public: NearestGameObjectEntryInObjectRangeCheck(WorldObject const& obj, uint32 entry, float range) : i_obj(obj), i_entry(entry), i_range(range) { } + bool operator()(GameObject* go) { if (go->GetEntry() == i_entry && i_obj.IsWithinDistInMap(go, i_range)) @@ -721,38 +726,39 @@ namespace Trinity } return false; } - float GetLastRange() const { return i_range; } + private: WorldObject const& i_obj; uint32 i_entry; float i_range; // prevent clone this object - NearestGameObjectEntryInObjectRangeCheck(NearestGameObjectEntryInObjectRangeCheck const&); + NearestGameObjectEntryInObjectRangeCheck(NearestGameObjectEntryInObjectRangeCheck const&) = delete; }; // Success at unit in range, range update for next check (this can be use with GameobjectLastSearcher to find nearest GO with a certain type) class NearestGameObjectTypeInObjectRangeCheck { - public: - NearestGameObjectTypeInObjectRangeCheck(WorldObject const& obj, GameobjectTypes type, float range) : i_obj(obj), i_type(type), i_range(range) { } - bool operator()(GameObject* go) - { - if (go->GetGoType() == i_type && i_obj.IsWithinDistInMap(go, i_range)) + public: + NearestGameObjectTypeInObjectRangeCheck(WorldObject const& obj, GameobjectTypes type, float range) : i_obj(obj), i_type(type), i_range(range) { } + + bool operator()(GameObject* go) { - i_range = i_obj.GetDistance(go); // use found GO range as new range limit for next check - return true; + if (go->GetGoType() == i_type && i_obj.IsWithinDistInMap(go, i_range)) + { + i_range = i_obj.GetDistance(go); // use found GO range as new range limit for next check + return true; + } + return false; } - return false; - } - float GetLastRange() const { return i_range; } - private: - WorldObject const& i_obj; - GameobjectTypes i_type; - float i_range; - // prevent clone this object - NearestGameObjectTypeInObjectRangeCheck(NearestGameObjectTypeInObjectRangeCheck const&); + private: + WorldObject const& i_obj; + GameobjectTypes i_type; + float i_range; + + // prevent clone this object + NearestGameObjectTypeInObjectRangeCheck(NearestGameObjectTypeInObjectRangeCheck const&) = delete; }; // Unit checks @@ -761,6 +767,7 @@ namespace Trinity { public: MostHPMissingInRange(Unit const* obj, float range, uint32 hp) : i_obj(obj), i_range(range), i_hp(hp) { } + bool operator()(Unit* u) { if (u->IsAlive() && u->IsInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && u->GetMaxHealth() - u->GetHealth() > i_hp) @@ -770,6 +777,7 @@ namespace Trinity } return false; } + private: Unit const* i_obj; float i_range; @@ -780,7 +788,8 @@ namespace Trinity { public: FriendlyCCedInRange(Unit const* obj, float range) : i_obj(obj), i_range(range) { } - bool operator()(Unit* u) + + bool operator()(Unit* u) const { if (u->IsAlive() && u->IsInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && (u->isFeared() || u->IsCharmed() || u->isFrozen() || u->HasUnitState(UNIT_STATE_STUNNED) || u->HasUnitState(UNIT_STATE_CONFUSED))) @@ -789,6 +798,7 @@ namespace Trinity } return false; } + private: Unit const* i_obj; float i_range; @@ -798,15 +808,15 @@ namespace Trinity { public: FriendlyMissingBuffInRange(Unit const* obj, float range, uint32 spellid) : i_obj(obj), i_range(range), i_spell(spellid) { } - bool operator()(Unit* u) + + bool operator()(Unit* u) const { - if (u->IsAlive() && u->IsInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && - !(u->HasAura(i_spell))) - { + if (u->IsAlive() && u->IsInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && !u->HasAura(i_spell)) return true; - } + return false; } + private: Unit const* i_obj; float i_range; @@ -817,23 +827,26 @@ namespace Trinity { public: AnyUnfriendlyUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) { } - bool operator()(Unit* u) + + bool operator()(Unit* u) const { if (u->IsAlive() && i_obj->IsWithinDistInMap(u, i_range) && !i_funit->IsFriendlyTo(u)) return true; - else - return false; + + return false; } + private: WorldObject const* i_obj; Unit const* i_funit; float i_range; }; - class AnyUnfriendlyNoTotemUnitInObjectRangeCheck + class NearestUnfriendlyNoTotemUnitInObjectRangeCheck { public: - AnyUnfriendlyNoTotemUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) { } + NearestUnfriendlyNoTotemUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) { } + bool operator()(Unit* u) { if (!u->IsAlive()) @@ -848,8 +861,13 @@ namespace Trinity if (!u->isTargetableForAttack(false)) return false; - return i_obj->IsWithinDistInMap(u, i_range) && !i_funit->IsFriendlyTo(u); + if (!i_obj->IsWithinDistInMap(u, i_range) || i_funit->IsFriendlyTo(u)) + return false; + + i_range = i_obj->GetDistance(*u); + return true; } + private: WorldObject const* i_obj; Unit const* i_funit; @@ -860,13 +878,15 @@ namespace Trinity { public: AnyFriendlyUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, bool playerOnly = false) : i_obj(obj), i_funit(funit), i_range(range), i_playerOnly(playerOnly) { } - bool operator()(Unit* u) + + bool operator()(Unit* u) const { if (u->IsAlive() && i_obj->IsWithinDistInMap(u, i_range) && i_funit->IsFriendlyTo(u) && (!i_playerOnly || u->GetTypeId() == TYPEID_PLAYER)) return true; else return false; } + private: WorldObject const* i_obj; Unit const* i_funit; @@ -877,10 +897,14 @@ namespace Trinity class AnyGroupedUnitInObjectRangeCheck { public: - AnyGroupedUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, bool raid) : _source(obj), _refUnit(funit), _range(range), _raid(raid) { } - bool operator()(Unit* u) + AnyGroupedUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, bool raid, bool playerOnly = false) : _source(obj), _refUnit(funit), _range(range), _raid(raid), _playerOnly(playerOnly) { } + + bool operator()(Unit* u) const { - if (G3D::fuzzyEq(_range, 0)) + if (G3D::fuzzyEq(_range, 0.0f)) + return false; + + if (_playerOnly && u->GetTypeId() != TYPEID_PLAYER) return false; if (_raid) @@ -899,19 +923,22 @@ namespace Trinity Unit const* _refUnit; float _range; bool _raid; + bool _playerOnly; }; class AnyUnitInObjectRangeCheck { public: AnyUnitInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) { } - bool operator()(Unit* u) + + bool operator()(Unit* u) const { if (u->IsAlive() && i_obj->IsWithinDistInMap(u, i_range)) return true; return false; } + private: WorldObject const* i_obj; float i_range; @@ -922,6 +949,7 @@ namespace Trinity { public: NearestAttackableUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) { } + bool operator()(Unit* u) { if (u->isTargetableForAttack() && i_obj->IsWithinDistInMap(u, i_range) && @@ -933,42 +961,40 @@ namespace Trinity return false; } + private: WorldObject const* i_obj; Unit const* i_funit; float i_range; // prevent clone this object - NearestAttackableUnitInObjectRangeCheck(NearestAttackableUnitInObjectRangeCheck const&); + NearestAttackableUnitInObjectRangeCheck(NearestAttackableUnitInObjectRangeCheck const&) = delete; }; class AnyAoETargetUnitInObjectRangeCheck { public: - AnyAoETargetUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) - : i_obj(obj), i_funit(funit), _spellInfo(NULL), i_range(range) + AnyAoETargetUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, SpellInfo const* spellInfo = nullptr) + : i_obj(obj), i_funit(funit), _spellInfo(spellInfo), i_range(range) { - Unit const* check = i_funit; - Unit const* owner = i_funit->GetOwner(); - if (owner) - check = owner; - i_targetForPlayer = (check->GetTypeId() == TYPEID_PLAYER); - if (DynamicObject const* dynObj = i_obj->ToDynObject()) - _spellInfo = sSpellMgr->GetSpellInfo(dynObj->GetSpellId()); + if (!_spellInfo) + if (DynamicObject const* dynObj = i_obj->ToDynObject()) + _spellInfo = sSpellMgr->GetSpellInfo(dynObj->GetSpellId()); } - bool operator()(Unit* u) + + bool operator()(Unit* u) const { // Check contains checks for: live, non-selectable, non-attackable flags, flight check and GM check, ignore totems if (u->GetTypeId() == TYPEID_UNIT && u->IsTotem()) return false; - if (i_funit->_IsValidAttackTarget(u, _spellInfo, i_obj->GetTypeId() == TYPEID_DYNAMICOBJECT ? i_obj : NULL) && i_obj->IsWithinDistInMap(u, i_range)) - return true; + if (_spellInfo && _spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS) && u->GetTypeId() != TYPEID_PLAYER) + return false; - return false; + return i_funit->_IsValidAttackTarget(u, _spellInfo, i_obj->GetTypeId() == TYPEID_DYNAMICOBJECT ? i_obj : nullptr) && i_obj->IsWithinDistInMap(u, i_range); } + private: - bool i_targetForPlayer; WorldObject const* i_obj; Unit const* i_funit; SpellInfo const* _spellInfo; @@ -980,9 +1006,9 @@ namespace Trinity { public: CallOfHelpCreatureInRangeDo(Unit* funit, Unit* enemy, float range) - : i_funit(funit), i_enemy(enemy), i_range(range) - { } - void operator()(Creature* u) + : i_funit(funit), i_enemy(enemy), i_range(range) { } + + void operator()(Creature* u) const { if (u == i_funit) return; @@ -1007,27 +1033,16 @@ namespace Trinity float i_range; }; - struct AnyDeadUnitCheck - { - bool operator()(Unit* u) { return !u->IsAlive(); } - }; - - /* - struct AnyStealthedCheck - { - bool operator()(Unit* u) { return u->GetVisibility() == VISIBILITY_GROUP_STEALTH; } - }; - */ - // Creature checks class NearestHostileUnitCheck { public: - explicit NearestHostileUnitCheck(Creature const* creature, float dist = 0, bool playerOnly = false) : me(creature), i_playerOnly(playerOnly) + explicit NearestHostileUnitCheck(Creature const* creature, float dist = 0.f, bool playerOnly = false) : me(creature), i_playerOnly(playerOnly) { - m_range = (dist == 0 ? 9999 : dist); + m_range = (dist == 0.f ? 9999.f : dist); } + bool operator()(Unit* u) { if (!me->IsWithinDistInMap(u, m_range)) @@ -1043,21 +1058,22 @@ namespace Trinity return true; } - private: + private: Creature const* me; float m_range; bool i_playerOnly; - NearestHostileUnitCheck(NearestHostileUnitCheck const&); + NearestHostileUnitCheck(NearestHostileUnitCheck const&) = delete; }; class NearestHostileUnitInAttackDistanceCheck { public: - explicit NearestHostileUnitInAttackDistanceCheck(Creature const* creature, float dist = 0) : me(creature) + explicit NearestHostileUnitInAttackDistanceCheck(Creature const* creature, float dist = 0.f) : me(creature) { - m_range = (dist == 0 ? 9999 : dist); - m_force = (dist == 0 ? false : true); + m_range = (dist == 0.f ? 9999.f : dist); + m_force = (dist == 0.f ? false : true); } + bool operator()(Unit* u) { if (!me->IsWithinDistInMap(u, m_range)) @@ -1077,21 +1093,20 @@ namespace Trinity m_range = me->GetDistance(u); // use found unit range as new range limit for next check return true; } - float GetLastRange() const { return m_range; } + private: Creature const* me; float m_range; bool m_force; - NearestHostileUnitInAttackDistanceCheck(NearestHostileUnitInAttackDistanceCheck const&); + NearestHostileUnitInAttackDistanceCheck(NearestHostileUnitInAttackDistanceCheck const&) = delete; }; class NearestHostileUnitInAggroRangeCheck { public: - explicit NearestHostileUnitInAggroRangeCheck(Creature const* creature, bool useLOS = false) : _me(creature), _useLOS(useLOS) - { - } - bool operator()(Unit* u) + explicit NearestHostileUnitInAggroRangeCheck(Creature const* creature, bool useLOS = false) : _me(creature), _useLOS(useLOS) { } + + bool operator()(Unit* u) const { if (!u->IsHostileTo(_me)) return false; @@ -1108,20 +1123,19 @@ namespace Trinity return true; } - private: + private: Creature const* _me; bool _useLOS; - NearestHostileUnitInAggroRangeCheck(NearestHostileUnitInAggroRangeCheck const&); + NearestHostileUnitInAggroRangeCheck(NearestHostileUnitInAggroRangeCheck const&) = delete; }; class AnyAssistCreatureInRangeCheck { public: AnyAssistCreatureInRangeCheck(Unit* funit, Unit* enemy, float range) - : i_funit(funit), i_enemy(enemy), i_range(range) - { - } - bool operator()(Creature* u) + : i_funit(funit), i_enemy(enemy), i_range(range) { } + + bool operator()(Creature* u) const { if (u == i_funit) return false; @@ -1139,6 +1153,7 @@ namespace Trinity return true; } + private: Unit* const i_funit; Unit* const i_enemy; @@ -1167,14 +1182,14 @@ namespace Trinity i_range = i_obj->GetDistance(u); // use found unit range as new range limit for next check return true; } - float GetLastRange() const { return i_range; } + private: Creature* const i_obj; Unit* const i_enemy; float i_range; // prevent clone this object - NearestAssistCreatureInCreatureRangeCheck(NearestAssistCreatureInCreatureRangeCheck const&); + NearestAssistCreatureInCreatureRangeCheck(NearestAssistCreatureInCreatureRangeCheck const&) = delete; }; // Success at unit in range, range update for next check (this can be use with CreatureLastSearcher to find nearest creature) @@ -1193,7 +1208,7 @@ namespace Trinity } return false; } - float GetLastRange() const { return i_range; } + private: WorldObject const& i_obj; uint32 i_entry; @@ -1201,14 +1216,15 @@ namespace Trinity float i_range; // prevent clone this object - NearestCreatureEntryWithLiveStateInObjectRangeCheck(NearestCreatureEntryWithLiveStateInObjectRangeCheck const&); + NearestCreatureEntryWithLiveStateInObjectRangeCheck(NearestCreatureEntryWithLiveStateInObjectRangeCheck const&) = delete; }; class AnyPlayerInObjectRangeCheck { public: AnyPlayerInObjectRangeCheck(WorldObject const* obj, float range, bool reqAlive = true) : _obj(obj), _range(range), _reqAlive(reqAlive) { } - bool operator()(Player* u) + + bool operator()(Player* u) const { if (_reqAlive && !u->IsAlive()) return false; @@ -1228,9 +1244,7 @@ namespace Trinity class NearestPlayerInObjectRangeCheck { public: - NearestPlayerInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) - { - } + NearestPlayerInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) { } bool operator()(Player* u) { @@ -1246,46 +1260,51 @@ namespace Trinity WorldObject const* i_obj; float i_range; - NearestPlayerInObjectRangeCheck(NearestPlayerInObjectRangeCheck const&); + NearestPlayerInObjectRangeCheck(NearestPlayerInObjectRangeCheck const&) = delete; }; class AllFriendlyCreaturesInGrid { - public: - AllFriendlyCreaturesInGrid(Unit const* obj) : unit(obj) { } - bool operator() (Unit* u) - { - if (u->IsAlive() && u->IsVisible() && u->IsFriendlyTo(unit)) - return true; + public: + AllFriendlyCreaturesInGrid(Unit const* obj) : unit(obj) { } - return false; - } - private: - Unit const* unit; + bool operator()(Unit* u) const + { + if (u->IsAlive() && u->IsVisible() && u->IsFriendlyTo(unit)) + return true; + + return false; + } + + private: + Unit const* unit; }; class AllGameObjectsWithEntryInRange { - public: - AllGameObjectsWithEntryInRange(const WorldObject* object, uint32 entry, float maxRange) : m_pObject(object), m_uiEntry(entry), m_fRange(maxRange) { } - bool operator() (GameObject* go) - { - if ((!m_uiEntry || go->GetEntry() == m_uiEntry) && m_pObject->IsWithinDist(go, m_fRange, false)) - return true; + public: + AllGameObjectsWithEntryInRange(const WorldObject* object, uint32 entry, float maxRange) : m_pObject(object), m_uiEntry(entry), m_fRange(maxRange) { } - return false; - } - private: - const WorldObject* m_pObject; - uint32 m_uiEntry; - float m_fRange; + bool operator()(GameObject* go) const + { + if ((!m_uiEntry || go->GetEntry() == m_uiEntry) && m_pObject->IsWithinDist(go, m_fRange, false)) + return true; + + return false; + } + + private: + WorldObject const* m_pObject; + uint32 m_uiEntry; + float m_fRange; }; class AllCreaturesOfEntryInRange { public: AllCreaturesOfEntryInRange(const WorldObject* object, uint32 entry, float maxRange) : m_pObject(object), m_uiEntry(entry), m_fRange(maxRange) { } - bool operator() (Unit* unit) + + bool operator()(Unit* unit) const { if ((!m_uiEntry || unit->GetEntry() == m_uiEntry) && m_pObject->IsWithinDist(unit, m_fRange, false)) return true; @@ -1294,63 +1313,69 @@ namespace Trinity } private: - const WorldObject* m_pObject; + WorldObject const* m_pObject; uint32 m_uiEntry; float m_fRange; }; class PlayerAtMinimumRangeAway { - public: - PlayerAtMinimumRangeAway(Unit const* unit, float fMinRange) : unit(unit), fRange(fMinRange) { } - bool operator() (Player* player) - { - //No threat list check, must be done explicit if expected to be in combat with creature - if (!player->IsGameMaster() && player->IsAlive() && !unit->IsWithinDist(player, fRange, false)) - return true; + public: + PlayerAtMinimumRangeAway(Unit const* unit, float fMinRange) : unit(unit), fRange(fMinRange) { } - return false; - } + bool operator()(Player* player) const + { + //No threat list check, must be done explicit if expected to be in combat with creature + if (!player->IsGameMaster() && player->IsAlive() && !unit->IsWithinDist(player, fRange, false)) + return true; - private: - Unit const* unit; - float fRange; + return false; + } + + private: + Unit const* unit; + float fRange; }; class GameObjectInRangeCheck { - public: - GameObjectInRangeCheck(float _x, float _y, float _z, float _range, uint32 _entry = 0) : - x(_x), y(_y), z(_z), range(_range), entry(_entry) { } - bool operator() (GameObject* go) - { - if (!entry || (go->GetGOInfo() && go->GetGOInfo()->entry == entry)) - return go->IsInRange(x, y, z, range); - else return false; - } - private: - float x, y, z, range; - uint32 entry; + public: + GameObjectInRangeCheck(float _x, float _y, float _z, float _range, uint32 _entry = 0) : + x(_x), y(_y), z(_z), range(_range), entry(_entry) { } + + bool operator()(GameObject* go) const + { + if (!entry || (go->GetGOInfo() && go->GetGOInfo()->entry == entry)) + return go->IsInRange(x, y, z, range); + else return false; + } + + private: + float x, y, z, range; + uint32 entry; }; class AllWorldObjectsInRange { - public: - AllWorldObjectsInRange(const WorldObject* object, float maxRange) : m_pObject(object), m_fRange(maxRange) { } - bool operator() (WorldObject* go) - { - return m_pObject->IsWithinDist(go, m_fRange, false) && m_pObject->InSamePhase(go); - } - private: - const WorldObject* m_pObject; - float m_fRange; + public: + AllWorldObjectsInRange(const WorldObject* object, float maxRange) : m_pObject(object), m_fRange(maxRange) { } + + bool operator()(WorldObject* go) const + { + return m_pObject->IsWithinDist(go, m_fRange, false) && m_pObject->InSamePhase(go); + } + + private: + WorldObject const* m_pObject; + float m_fRange; }; class ObjectTypeIdCheck { public: ObjectTypeIdCheck(TypeID typeId, bool equals) : _typeId(typeId), _equals(equals) { } - bool operator()(WorldObject* object) + + bool operator()(WorldObject* object) const { return (object->GetTypeId() == _typeId) == _equals; } @@ -1364,7 +1389,8 @@ namespace Trinity { public: ObjectGUIDCheck(ObjectGuid GUID) : _GUID(GUID) { } - bool operator()(WorldObject* object) + + bool operator()(WorldObject* object) const { return object->GetGUID() == _GUID; } @@ -1377,6 +1403,7 @@ namespace Trinity { public: UnitAuraCheck(bool present, uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty) : _present(present), _spellId(spellId), _casterGUID(casterGUID) { } + bool operator()(Unit* unit) const { return unit->HasAura(_spellId, _casterGUID) == _present; @@ -1407,6 +1434,7 @@ namespace Trinity for (size_t i = 0; i < i_data_cache.size(); ++i) delete i_data_cache[i]; } + void operator()(Player* p); private: diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index ccfd4845dc3..e99ee78991d 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -1154,7 +1154,7 @@ void Group::NeedBeforeGreed(Loot* loot, WorldObject* lootedObject) if (item->DisenchantID && m_maxEnchantingLevel >= item->RequiredDisenchantSkill) r->rollVoteMask |= ROLL_FLAG_TYPE_DISENCHANT; - if (item->Flags2 & ITEM_FLAGS_EXTRA_NEED_ROLL_DISABLED) + if (item->Flags2 & ITEM_FLAG2_CAN_ONLY_ROLL_GREED) r->rollVoteMask &= ~ROLL_FLAG_TYPE_NEED; loot->items[itemSlot].is_blocked = true; diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index 71242d57f33..2c5eddcefdc 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -202,7 +202,7 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) itemEntry = item->GetTemplate()->ItemId; if (sAuctionMgr->GetAItem(item->GetGUID().GetCounter()) || !item->CanBeTraded() || item->IsNotEmptyBag() || - item->GetTemplate()->Flags & ITEM_PROTO_FLAG_CONJURED || item->GetUInt32Value(ITEM_FIELD_DURATION) || + (item->GetTemplate()->Flags & ITEM_FLAG_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION) || item->GetCount() < count[i] || itemEntry != item->GetTemplate()->ItemId) { SendAuctionCommandResult(0, AUCTION_SELL_ITEM, ERR_AUCTION_DATABASE_ERROR); diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index bb188b08fbc..8f9a3ba6ffc 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -284,7 +284,7 @@ void WorldSession::HandleDestroyItemOpcode(WorldPacket& recvData) return; } - if (pItem->GetTemplate()->Flags & ITEM_PROTO_FLAG_INDESTRUCTIBLE) + if (pItem->GetTemplate()->Flags & ITEM_FLAG_NO_USER_DESTROY) { _player->SendEquipError(EQUIP_ERR_CANT_DROP_SOULBOUND, NULL, NULL); return; @@ -542,7 +542,7 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) // prevent selling item for sellprice when the item is still refundable // this probably happens when right clicking a refundable item, the client sends both // CMSG_SELL_ITEM and CMSG_REFUND_ITEM (unverified) - if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE)) + if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE)) return; // Therefore, no feedback to client // special case at auto sell (sell all) @@ -776,7 +776,7 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid) continue; // Only display items in vendor lists for the team the // player is on. If GM on, display all items. - if (!_player->IsGameMaster() && ((itemTemplate->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY && _player->GetTeam() == ALLIANCE) || (itemTemplate->Flags2 == ITEM_FLAGS_EXTRA_ALLIANCE_ONLY && _player->GetTeam() == HORDE))) + if (!_player->IsGameMaster() && ((itemTemplate->Flags2 & ITEM_FLAG2_FACTION_HORDE && _player->GetTeam() == ALLIANCE) || (itemTemplate->Flags2 == ITEM_FLAG2_FACTION_ALLIANCE && _player->GetTeam() == HORDE))) continue; // Items sold out are not displayed in list @@ -1093,7 +1093,7 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) return; } - if (!(gift->GetTemplate()->Flags & ITEM_PROTO_FLAG_IS_WRAPPER)) // cheating: non-wrapper wrapper + if (!(gift->GetTemplate()->Flags & ITEM_FLAG_IS_WRAPPER)) // cheating: non-wrapper wrapper { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL); return; @@ -1171,7 +1171,7 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) case 21830: item->SetEntry(21831); break; } item->SetGuidValue(ITEM_FIELD_GIFTCREATOR, _player->GetGUID()); - item->SetUInt32Value(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED); + item->SetUInt32Value(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED); item->SetState(ITEM_CHANGED, _player); if (item->GetState() == ITEM_NEW) // save new item, to have alway for `character_gifts` record in `item_instance` @@ -1272,7 +1272,7 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recvData) ItemTemplate const* iGemProto = Gems[i]->GetTemplate(); // unique item (for new and already placed bit removed enchantments - if (iGemProto->Flags & ITEM_PROTO_FLAG_UNIQUE_EQUIPPED) + if (iGemProto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE) { for (int j = 0; j < MAX_GEM_SOCKETS; ++j) { diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index d91dd6f495f..94e271465cd 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -322,7 +322,7 @@ void WorldSession::DoLootRelease(ObjectGuid lguid) ItemTemplate const* proto = pItem->GetTemplate(); // destroy only 5 items from stack in case prospecting and milling - if (proto->Flags & (ITEM_PROTO_FLAG_PROSPECTABLE | ITEM_PROTO_FLAG_MILLABLE)) + if (proto->Flags & (ITEM_FLAG_IS_PROSPECTABLE | ITEM_FLAG_IS_MILLABLE)) { pItem->m_lootGenerated = false; pItem->loot.clear(); @@ -338,7 +338,7 @@ void WorldSession::DoLootRelease(ObjectGuid lguid) else { // Only delete item if no loot or money (unlooted loot is saved to db) or if it isn't an openable item - if (pItem->loot.isLooted() || !(proto->Flags & ITEM_PROTO_FLAG_HAS_LOOT)) + if (pItem->loot.isLooted() || !(proto->Flags & ITEM_FLAG_HAS_LOOT)) player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true); } return; // item can be looted only single player diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index 36df5b64f1b..a4dcd22c1f2 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -199,7 +199,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) if (Item* item = player->GetItemByGuid(itemGUIDs[i])) { ItemTemplate const* itemProto = item->GetTemplate(); - if (!itemProto || !(itemProto->Flags & ITEM_PROTO_FLAG_BIND_TO_ACCOUNT)) + if (!itemProto || !(itemProto->Flags & ITEM_FLAG_IS_BOUND_TO_ACCOUNT)) { accountBound = false; break; @@ -250,13 +250,13 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) return; } - if (item->GetTemplate()->Flags & ITEM_PROTO_FLAG_CONJURED || item->GetUInt32Value(ITEM_FIELD_DURATION)) + if ((item->GetTemplate()->Flags & ITEM_FLAG_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } - if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) + if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index cbc9be3b140..728fda8d184 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -115,14 +115,14 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) } // only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB) - if (proto->Class == ITEM_CLASS_CONSUMABLE && !(proto->Flags & ITEM_PROTO_FLAG_USEABLE_IN_ARENA) && pUser->InArena()) + if (proto->Class == ITEM_CLASS_CONSUMABLE && !(proto->Flags & ITEM_FLAG_IGNORE_DEFAULT_ARENA_RESTRICTIONS) && pUser->InArena()) { pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH, pItem, NULL); return; } // don't allow items banned in arena - if (proto->Flags & ITEM_PROTO_FLAG_NOT_USEABLE_IN_ARENA && pUser->InArena()) + if ((proto->Flags & ITEM_FLAG_NOT_USEABLE_IN_ARENA) && pUser->InArena()) { pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH, pItem, NULL); return; @@ -196,7 +196,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) } // Verify that the bag is an actual bag or wrapped item that can be used "normally" - if (!(proto->Flags & ITEM_PROTO_FLAG_HAS_LOOT) && !item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) + if (!(proto->Flags & ITEM_FLAG_HAS_LOOT) && !item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED)) { pUser->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, item, NULL); TC_LOG_ERROR("entities.player.cheat", "Possible hacking attempt: Player %s [guid: %u] tried to open item [guid: %u, entry: %u] which is not openable!", @@ -225,7 +225,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) } } - if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))// wrapped? + if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED))// wrapped? { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM); diff --git a/src/server/game/Handlers/TradeHandler.cpp b/src/server/game/Handlers/TradeHandler.cpp index e2743f8f456..6b4d16111ed 100644 --- a/src/server/game/Handlers/TradeHandler.cpp +++ b/src/server/game/Handlers/TradeHandler.cpp @@ -93,7 +93,7 @@ void WorldSession::SendUpdateTrade(bool trader_data /*= true*/) data << uint32(item->GetTemplate()->DisplayInfoID);// display id data << uint32(item->GetCount()); // stack count // wrapped: hide stats but show giftcreator name - data << uint32(item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED) ? 1 : 0); + data << uint32(item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED) ? 1 : 0); data << uint64(item->GetGuidValue(ITEM_FIELD_GIFTCREATOR)); // perm. enchantment and gems data << uint32(item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT)); @@ -152,7 +152,7 @@ void WorldSession::moveItems(Item* myItems[], Item* hisItems[]) } // adjust time (depends on /played) - if (myItems[i]->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE)) + if (myItems[i]->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE)) myItems[i]->SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, trader->GetTotalPlayedTime()-(_player->GetTotalPlayedTime()-myItems[i]->GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME))); // store trader->MoveItemToInventory(traderDst, myItems[i], true, true); @@ -170,7 +170,7 @@ void WorldSession::moveItems(Item* myItems[], Item* hisItems[]) } // adjust time (depends on /played) - if (hisItems[i]->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE)) + if (hisItems[i]->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE)) hisItems[i]->SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, _player->GetTotalPlayedTime()-(trader->GetTotalPlayedTime()-hisItems[i]->GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME))); // store _player->MoveItemToInventory(playerDst, hisItems[i], true, true); diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index da50b818341..b69bb41d3ad 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -353,7 +353,7 @@ LootItem::LootItem(LootStoreItem const& li) conditions = li.conditions; ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid); - freeforall = proto && (proto->Flags & ITEM_PROTO_FLAG_MULTI_DROP); + freeforall = proto && (proto->Flags & ITEM_FLAG_MULTI_DROP); follow_loot_rules = proto && (proto->FlagsCu & ITEM_FLAGS_CU_FOLLOW_LOOT_RULES); needs_quest = li.needs_quest; @@ -380,24 +380,20 @@ bool LootItem::AllowedForPlayer(Player const* player) const return false; // not show loot for players without profession or those who already know the recipe - if ((pProto->Flags & ITEM_PROTO_FLAG_SMART_LOOT) && (!player->HasSkill(pProto->RequiredSkill) || player->HasSpell(pProto->Spells[1].SpellId))) + if ((pProto->Flags & ITEM_FLAG_HIDE_UNUSABLE_RECIPE) && (!player->HasSkill(pProto->RequiredSkill) || player->HasSpell(pProto->Spells[1].SpellId))) return false; // not show loot for not own team - if ((pProto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) && player->GetTeam() != HORDE) + if ((pProto->Flags2 & ITEM_FLAG2_FACTION_HORDE) && player->GetTeam() != HORDE) return false; - if ((pProto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) && player->GetTeam() != ALLIANCE) + if ((pProto->Flags2 & ITEM_FLAG2_FACTION_ALLIANCE) && player->GetTeam() != ALLIANCE) return false; // check quest requirements if (!(pProto->FlagsCu & ITEM_FLAGS_CU_IGNORE_QUEST_STATUS) && ((needs_quest || (pProto->StartQuest && player->GetQuestStatus(pProto->StartQuest) != QUEST_STATUS_NONE)) && !player->HasQuestForItem(itemid))) return false; - // Don't show bind-when-picked-up unique items if player already has the maximum allowed quantity. - if (pProto->Bonding == BIND_WHEN_PICKED_UP && pProto->MaxCount && int32(player->GetItemCount(itemid, true)) >= pProto->MaxCount) - return false; - return true; } @@ -433,7 +429,7 @@ void Loot::AddItem(LootStoreItem const& item) // non-conditional one-player only items are counted here, // free for all items are counted in FillFFALoot(), // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot() - if (!item.needs_quest && item.conditions.empty() && !(proto->Flags & ITEM_PROTO_FLAG_MULTI_DROP)) + if (!item.needs_quest && item.conditions.empty() && !(proto->Flags & ITEM_FLAG_MULTI_DROP)) ++unlootedCount; } } @@ -487,7 +483,7 @@ void Loot::FillNotNormalLootFor(Player* player, bool presentAtLooting) { ObjectGuid::LowType plguid = player->GetGUID().GetCounter(); - QuestItemMap::const_iterator qmapitr = PlayerQuestItems.find(plguid); + NotNormalLootItemMap::const_iterator qmapitr = PlayerQuestItems.find(plguid); if (qmapitr == PlayerQuestItems.end()) FillQuestLoot(player); @@ -521,16 +517,16 @@ void Loot::FillNotNormalLootFor(Player* player, bool presentAtLooting) } } -QuestItemList* Loot::FillFFALoot(Player* player) +NotNormalLootItemList* Loot::FillFFALoot(Player* player) { - QuestItemList* ql = new QuestItemList(); + NotNormalLootItemList* ql = new NotNormalLootItemList(); for (uint8 i = 0; i < items.size(); ++i) { LootItem &item = items[i]; if (!item.is_looted && item.freeforall && item.AllowedForPlayer(player)) { - ql->push_back(QuestItem(i)); + ql->push_back(NotNormalLootItem(i)); ++unlootedCount; } } @@ -544,12 +540,12 @@ QuestItemList* Loot::FillFFALoot(Player* player) return ql; } -QuestItemList* Loot::FillQuestLoot(Player* player) +NotNormalLootItemList* Loot::FillQuestLoot(Player* player) { if (items.size() == MAX_NR_LOOT_ITEMS) return NULL; - QuestItemList* ql = new QuestItemList(); + NotNormalLootItemList* ql = new NotNormalLootItemList(); for (uint8 i = 0; i < quest_items.size(); ++i) { @@ -557,7 +553,7 @@ QuestItemList* Loot::FillQuestLoot(Player* player) if (!item.is_looted && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player->GetGroup() && ((player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID()) || player->GetGroup()->GetLootMethod() != MASTER_LOOT)))) { - ql->push_back(QuestItem(i)); + ql->push_back(NotNormalLootItem(i)); // quest items get blocked when they first appear in a // player's quest vector @@ -582,20 +578,20 @@ QuestItemList* Loot::FillQuestLoot(Player* player) return ql; } -QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player, bool presentAtLooting) +NotNormalLootItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player, bool presentAtLooting) { - QuestItemList* ql = new QuestItemList(); + NotNormalLootItemList* ql = new NotNormalLootItemList(); for (uint8 i = 0; i < items.size(); ++i) { LootItem &item = items[i]; - if (!item.is_looted && !item.freeforall && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player->GetGroup() && ((player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID()) || player->GetGroup()->GetLootMethod() != MASTER_LOOT)))) + if (!item.is_looted && !item.freeforall && (item.AllowedForPlayer(player))) { if (presentAtLooting) item.AddAllowedLooter(player); if (!item.conditions.empty()) { - ql->push_back(QuestItem(i)); + ql->push_back(NotNormalLootItem(i)); if (!item.is_counted) { ++unlootedCount; @@ -661,11 +657,11 @@ void Loot::NotifyQuestItemRemoved(uint8 questIndex) ++i_next; if (Player* player = ObjectAccessor::FindPlayer(*i)) { - QuestItemMap::const_iterator pq = PlayerQuestItems.find(player->GetGUID().GetCounter()); + NotNormalLootItemMap::const_iterator pq = PlayerQuestItems.find(player->GetGUID().GetCounter()); if (pq != PlayerQuestItems.end() && pq->second) { // find where/if the player has the given item in it's vector - QuestItemList& pql = *pq->second; + NotNormalLootItemList& pql = *pq->second; uint8 j; for (j = 0; j < pql.size(); ++j) @@ -721,17 +717,17 @@ void Loot::DeleteLootMoneyFromContainerItemDB() CharacterDatabase.Execute(stmt); } -LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem* *qitem, QuestItem* *ffaitem, QuestItem* *conditem) +LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, NotNormalLootItem* *qitem, NotNormalLootItem* *ffaitem, NotNormalLootItem* *conditem) { LootItem* item = NULL; bool is_looted = true; if (lootSlot >= items.size()) { uint32 questSlot = lootSlot - items.size(); - QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUID().GetCounter()); + NotNormalLootItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUID().GetCounter()); if (itr != PlayerQuestItems.end() && questSlot < itr->second->size()) { - QuestItem* qitem2 = &itr->second->at(questSlot); + NotNormalLootItem* qitem2 = &itr->second->at(questSlot); if (qitem) *qitem = qitem2; item = &quest_items[qitem2->index]; @@ -744,13 +740,13 @@ LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem* *qite is_looted = item->is_looted; if (item->freeforall) { - QuestItemMap::const_iterator itr = PlayerFFAItems.find(player->GetGUID().GetCounter()); + NotNormalLootItemMap::const_iterator itr = PlayerFFAItems.find(player->GetGUID().GetCounter()); if (itr != PlayerFFAItems.end()) { - for (QuestItemList::const_iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter) + for (NotNormalLootItemList::const_iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter) if (iter->index == lootSlot) { - QuestItem* ffaitem2 = (QuestItem*)&(*iter); + NotNormalLootItem* ffaitem2 = (NotNormalLootItem*)&(*iter); if (ffaitem) *ffaitem = ffaitem2; is_looted = ffaitem2->is_looted; @@ -760,14 +756,14 @@ LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem* *qite } else if (!item->conditions.empty()) { - QuestItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.find(player->GetGUID().GetCounter()); + NotNormalLootItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.find(player->GetGUID().GetCounter()); if (itr != PlayerNonQuestNonFFAConditionalItems.end()) { - for (QuestItemList::const_iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter) + for (NotNormalLootItemList::const_iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter) { if (iter->index == lootSlot) { - QuestItem* conditem2 = (QuestItem*)&(*iter); + NotNormalLootItem* conditem2 = (NotNormalLootItem*)&(*iter); if (conditem) *conditem = conditem2; is_looted = conditem2->is_looted; @@ -786,7 +782,7 @@ LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem* *qite uint32 Loot::GetMaxSlotInLootFor(Player* player) const { - QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUID().GetCounter()); + NotNormalLootItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUID().GetCounter()); return items.size() + (itr != PlayerQuestItems.end() ? itr->second->size() : 0); } @@ -806,12 +802,12 @@ bool Loot::hasItemForAll() const // return true if there is any FFA, quest or conditional item for the player. bool Loot::hasItemFor(Player* player) const { - QuestItemMap const& lootPlayerQuestItems = GetPlayerQuestItems(); - QuestItemMap::const_iterator q_itr = lootPlayerQuestItems.find(player->GetGUID().GetCounter()); + NotNormalLootItemMap const& lootPlayerQuestItems = GetPlayerQuestItems(); + NotNormalLootItemMap::const_iterator q_itr = lootPlayerQuestItems.find(player->GetGUID().GetCounter()); if (q_itr != lootPlayerQuestItems.end()) { - QuestItemList* q_list = q_itr->second; - for (QuestItemList::const_iterator qi = q_list->begin(); qi != q_list->end(); ++qi) + NotNormalLootItemList* q_list = q_itr->second; + for (NotNormalLootItemList::const_iterator qi = q_list->begin(); qi != q_list->end(); ++qi) { const LootItem &item = quest_items[qi->index]; if (!qi->is_looted && !item.is_looted) @@ -819,12 +815,12 @@ bool Loot::hasItemFor(Player* player) const } } - QuestItemMap const& lootPlayerFFAItems = GetPlayerFFAItems(); - QuestItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(player->GetGUID().GetCounter()); + NotNormalLootItemMap const& lootPlayerFFAItems = GetPlayerFFAItems(); + NotNormalLootItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(player->GetGUID().GetCounter()); if (ffa_itr != lootPlayerFFAItems.end()) { - QuestItemList* ffa_list = ffa_itr->second; - for (QuestItemList::const_iterator fi = ffa_list->begin(); fi != ffa_list->end(); ++fi) + NotNormalLootItemList* ffa_list = ffa_itr->second; + for (NotNormalLootItemList::const_iterator fi = ffa_list->begin(); fi != ffa_list->end(); ++fi) { const LootItem &item = items[fi->index]; if (!fi->is_looted && !item.is_looted) @@ -832,12 +828,12 @@ bool Loot::hasItemFor(Player* player) const } } - QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems(); - QuestItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(player->GetGUID().GetCounter()); + NotNormalLootItemMap const& lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems(); + NotNormalLootItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(player->GetGUID().GetCounter()); if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) { - QuestItemList* conditional_list = nn_itr->second; - for (QuestItemList::const_iterator ci = conditional_list->begin(); ci != conditional_list->end(); ++ci) + NotNormalLootItemList* conditional_list = nn_itr->second; + for (NotNormalLootItemList::const_iterator ci = conditional_list->begin(); ci != conditional_list->end(); ++ci) { const LootItem &item = items[ci->index]; if (!ci->is_looted && !item.is_looted) @@ -980,12 +976,12 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) } LootSlotType slotType = lv.permission == OWNER_PERMISSION ? LOOT_SLOT_TYPE_OWNER : LOOT_SLOT_TYPE_ALLOW_LOOT; - QuestItemMap const& lootPlayerQuestItems = l.GetPlayerQuestItems(); - QuestItemMap::const_iterator q_itr = lootPlayerQuestItems.find(lv.viewer->GetGUID().GetCounter()); + NotNormalLootItemMap const& lootPlayerQuestItems = l.GetPlayerQuestItems(); + NotNormalLootItemMap::const_iterator q_itr = lootPlayerQuestItems.find(lv.viewer->GetGUID().GetCounter()); if (q_itr != lootPlayerQuestItems.end()) { - QuestItemList* q_list = q_itr->second; - for (QuestItemList::const_iterator qi = q_list->begin(); qi != q_list->end(); ++qi) + NotNormalLootItemList* q_list = q_itr->second; + for (NotNormalLootItemList::const_iterator qi = q_list->begin(); qi != q_list->end(); ++qi) { LootItem &item = l.quest_items[qi->index]; if (!qi->is_looted && !item.is_looted) @@ -1021,12 +1017,12 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) } } - QuestItemMap const& lootPlayerFFAItems = l.GetPlayerFFAItems(); - QuestItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(lv.viewer->GetGUID().GetCounter()); + NotNormalLootItemMap const& lootPlayerFFAItems = l.GetPlayerFFAItems(); + NotNormalLootItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(lv.viewer->GetGUID().GetCounter()); if (ffa_itr != lootPlayerFFAItems.end()) { - QuestItemList* ffa_list = ffa_itr->second; - for (QuestItemList::const_iterator fi = ffa_list->begin(); fi != ffa_list->end(); ++fi) + NotNormalLootItemList* ffa_list = ffa_itr->second; + for (NotNormalLootItemList::const_iterator fi = ffa_list->begin(); fi != ffa_list->end(); ++fi) { LootItem &item = l.items[fi->index]; if (!fi->is_looted && !item.is_looted) @@ -1039,42 +1035,37 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) } } - QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = l.GetPlayerNonQuestNonFFAConditionalItems(); - QuestItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(lv.viewer->GetGUID().GetCounter()); + NotNormalLootItemMap const& lootPlayerNonQuestNonFFAConditionalItems = l.GetPlayerNonQuestNonFFAConditionalItems(); + NotNormalLootItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(lv.viewer->GetGUID().GetCounter()); if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) { - QuestItemList* conditional_list = nn_itr->second; - for (QuestItemList::const_iterator ci = conditional_list->begin(); ci != conditional_list->end(); ++ci) + NotNormalLootItemList* conditional_list = nn_itr->second; + for (NotNormalLootItemList::const_iterator ci = conditional_list->begin(); ci != conditional_list->end(); ++ci) { LootItem &item = l.items[ci->index]; if (!ci->is_looted && !item.is_looted) { b << uint8(ci->index); b << item; - if (item.follow_loot_rules) + switch (lv.permission) { - switch (lv.permission) - { - case MASTER_PERMISSION: - b << uint8(LOOT_SLOT_TYPE_MASTER); - break; - case RESTRICTED_PERMISSION: - b << (item.is_blocked ? uint8(LOOT_SLOT_TYPE_LOCKED) : uint8(slotType)); - break; - case GROUP_PERMISSION: - case ROUND_ROBIN_PERMISSION: - if (!item.is_blocked) - b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT); - else - b << uint8(LOOT_SLOT_TYPE_ROLL_ONGOING); - break; - default: - b << uint8(slotType); - break; - } - } - else + case MASTER_PERMISSION: + b << uint8(LOOT_SLOT_TYPE_MASTER); + break; + case RESTRICTED_PERMISSION: + b << (item.is_blocked ? uint8(LOOT_SLOT_TYPE_LOCKED) : uint8(slotType)); + break; + case GROUP_PERMISSION: + case ROUND_ROBIN_PERMISSION: + if (!item.is_blocked) + b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT); + else + b << uint8(LOOT_SLOT_TYPE_ROLL_ONGOING); + break; + default: b << uint8(slotType); + break; + } ++itemsShown; } } @@ -1657,7 +1648,7 @@ void LoadLootTemplates_Item() // remove real entries and check existence loot ItemTemplateContainer const* its = sObjectMgr->GetItemTemplateStore(); for (ItemTemplateContainer::const_iterator itr = its->begin(); itr != its->end(); ++itr) - if (lootIdSet.find(itr->second.ItemId) != lootIdSet.end() && itr->second.Flags & ITEM_PROTO_FLAG_HAS_LOOT) + if (lootIdSet.find(itr->second.ItemId) != lootIdSet.end() && itr->second.Flags & ITEM_FLAG_HAS_LOOT) lootIdSet.erase(itr->second.ItemId); // output error for any still listed (not referenced from appropriate table) ids @@ -1682,7 +1673,7 @@ void LoadLootTemplates_Milling() ItemTemplateContainer const* its = sObjectMgr->GetItemTemplateStore(); for (ItemTemplateContainer::const_iterator itr = its->begin(); itr != its->end(); ++itr) { - if (!(itr->second.Flags & ITEM_PROTO_FLAG_MILLABLE)) + if (!(itr->second.Flags & ITEM_FLAG_IS_MILLABLE)) continue; if (lootIdSet.find(itr->second.ItemId) != lootIdSet.end()) @@ -1745,7 +1736,7 @@ void LoadLootTemplates_Prospecting() ItemTemplateContainer const* its = sObjectMgr->GetItemTemplateStore(); for (ItemTemplateContainer::const_iterator itr = its->begin(); itr != its->end(); ++itr) { - if (!(itr->second.Flags & ITEM_PROTO_FLAG_PROSPECTABLE)) + if (!(itr->second.Flags & ITEM_FLAG_IS_PROSPECTABLE)) continue; if (lootIdSet.find(itr->second.ItemId) != lootIdSet.end()) diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index bca5cde1fa5..ecfb864823f 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -180,24 +180,24 @@ struct TC_GAME_API LootItem const AllowedLooterSet & GetAllowedLooters() const { return allowedGUIDs; } }; -struct QuestItem +struct NotNormalLootItem { - uint8 index; // position in quest_items; + uint8 index; // position in quest_items or items; bool is_looted; - QuestItem() + NotNormalLootItem() : index(0), is_looted(false) { } - QuestItem(uint8 _index, bool _islooted = false) + NotNormalLootItem(uint8 _index, bool _islooted = false) : index(_index), is_looted(_islooted) { } }; struct Loot; class LootTemplate; -typedef std::vector<QuestItem> QuestItemList; +typedef std::vector<NotNormalLootItem> NotNormalLootItemList; typedef std::vector<LootItem> LootItemList; -typedef std::map<uint32, QuestItemList*> QuestItemMap; +typedef std::map<uint32, NotNormalLootItemList*> NotNormalLootItemMap; typedef std::list<LootStoreItem*> LootStoreItemList; typedef std::unordered_map<uint32, LootTemplate*> LootTemplateMap; @@ -308,9 +308,9 @@ struct TC_GAME_API Loot { friend ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv); - QuestItemMap const& GetPlayerQuestItems() const { return PlayerQuestItems; } - QuestItemMap const& GetPlayerFFAItems() const { return PlayerFFAItems; } - QuestItemMap const& GetPlayerNonQuestNonFFAConditionalItems() const { return PlayerNonQuestNonFFAConditionalItems; } + NotNormalLootItemMap const& GetPlayerQuestItems() const { return PlayerQuestItems; } + NotNormalLootItemMap const& GetPlayerFFAItems() const { return PlayerFFAItems; } + NotNormalLootItemMap const& GetPlayerNonQuestNonFFAConditionalItems() const { return PlayerNonQuestNonFFAConditionalItems; } std::vector<LootItem> items; std::vector<LootItem> quest_items; @@ -340,15 +340,15 @@ struct TC_GAME_API Loot // void clear(); void clear() { - for (QuestItemMap::const_iterator itr = PlayerQuestItems.begin(); itr != PlayerQuestItems.end(); ++itr) + for (NotNormalLootItemMap::const_iterator itr = PlayerQuestItems.begin(); itr != PlayerQuestItems.end(); ++itr) delete itr->second; PlayerQuestItems.clear(); - for (QuestItemMap::const_iterator itr = PlayerFFAItems.begin(); itr != PlayerFFAItems.end(); ++itr) + for (NotNormalLootItemMap::const_iterator itr = PlayerFFAItems.begin(); itr != PlayerFFAItems.end(); ++itr) delete itr->second; PlayerFFAItems.clear(); - for (QuestItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.begin(); itr != PlayerNonQuestNonFFAConditionalItems.end(); ++itr) + for (NotNormalLootItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.begin(); itr != PlayerNonQuestNonFFAConditionalItems.end(); ++itr) delete itr->second; PlayerNonQuestNonFFAConditionalItems.clear(); @@ -377,7 +377,7 @@ struct TC_GAME_API Loot // Inserts the item into the loot (called by LootTemplate processors) void AddItem(LootStoreItem const & item); - LootItem* LootItemInSlot(uint32 lootslot, Player* player, QuestItem** qitem = NULL, QuestItem** ffaitem = NULL, QuestItem** conditem = NULL); + LootItem* LootItemInSlot(uint32 lootslot, Player* player, NotNormalLootItem** qitem = NULL, NotNormalLootItem** ffaitem = NULL, NotNormalLootItem** conditem = NULL); uint32 GetMaxSlotInLootFor(Player* player) const; bool hasItemForAll() const; bool hasItemFor(Player* player) const; @@ -385,14 +385,14 @@ struct TC_GAME_API Loot private: void FillNotNormalLootFor(Player* player, bool presentAtLooting); - QuestItemList* FillFFALoot(Player* player); - QuestItemList* FillQuestLoot(Player* player); - QuestItemList* FillNonQuestNonFFAConditionalLoot(Player* player, bool presentAtLooting); + NotNormalLootItemList* FillFFALoot(Player* player); + NotNormalLootItemList* FillQuestLoot(Player* player); + NotNormalLootItemList* FillNonQuestNonFFAConditionalLoot(Player* player, bool presentAtLooting); GuidSet PlayersLooting; - QuestItemMap PlayerQuestItems; - QuestItemMap PlayerFFAItems; - QuestItemMap PlayerNonQuestNonFFAConditionalItems; + NotNormalLootItemMap PlayerQuestItems; + NotNormalLootItemMap PlayerFFAItems; + NotNormalLootItemMap PlayerNonQuestNonFFAConditionalItems; // All rolls are registered here. They need to know, when the loot is not valid anymore LootValidatorRefManager i_LootValidatorRefManager; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index f7c440ce8bc..bc84e3fde29 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -374,9 +374,9 @@ enum SpellAttr2 SPELL_ATTR2_IS_ARCANE_CONCENTRATION = 0x00800000, // 23 Only mage Arcane Concentration have this flag SPELL_ATTR2_UNK24 = 0x01000000, // 24 SPELL_ATTR2_UNK25 = 0x02000000, // 25 - SPELL_ATTR2_UNK26 = 0x04000000, // 26 unaffected by school immunity + SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE = 0x04000000, // 26 unaffected by school immunity SPELL_ATTR2_UNK27 = 0x08000000, // 27 - SPELL_ATTR2_UNK28 = 0x10000000, // 28 + SPELL_ATTR2_IGNORE_ITEM_CHECK = 0x10000000, // 28 Spell is cast without checking item requirements (charges/reagents/totem) SPELL_ATTR2_CANT_CRIT = 0x20000000, // 29 Spell can't crit SPELL_ATTR2_TRIGGERED_CAN_TRIGGER_PROC = 0x40000000, // 30 spell can trigger even if triggered SPELL_ATTR2_FOOD_BUFF = 0x80000000 // 31 Food or Drink Buff (like Well Fed) @@ -548,7 +548,7 @@ enum SpellAttr7 SPELL_ATTR7_UNK17 = 0x00020000, // 17 Only 27965 (Suicide) spell. SPELL_ATTR7_HAS_CHARGE_EFFECT = 0x00040000, // 18 Only spells that have Charge among effects. SPELL_ATTR7_ZONE_TELEPORT = 0x00080000, // 19 Teleports to specific zones. - SPELL_ATTR7_UNK20 = 0x00100000, // 20 Blink, Divine Shield, Ice Block + SPELL_ATTR7_USABLE_IN_STUN_FEAR_CONFUSION = 0x00100000, // 20 Blink, Divine Shield, Ice Block SPELL_ATTR7_UNK21 = 0x00200000, // 21 Not set SPELL_ATTR7_UNK22 = 0x00400000, // 22 SPELL_ATTR7_UNK23 = 0x00800000, // 23 Motivate, Mutilate, Shattering Throw @@ -1286,10 +1286,11 @@ enum SpellImmunity IMMUNITY_DAMAGE = 3, // enum SpellSchoolMask IMMUNITY_DISPEL = 4, // enum DispelType IMMUNITY_MECHANIC = 5, // enum Mechanics - IMMUNITY_ID = 6 + IMMUNITY_ID = 6, + + MAX_SPELL_IMMUNITY }; -#define MAX_SPELL_IMMUNITY 7 // target enum name consist of: // TARGET_[OBJECT_TYPE]_[REFERENCE_TYPE(skipped for caster)]_[SELECTION_TYPE(skipped for default)]_[additional specifiers(friendly, BACK_LEFT, etc.] @@ -3136,7 +3137,9 @@ enum DiminishingGroup : uint16 DIMINISHING_SLEEP = 17, DIMINISHING_TAUNT = 18, DIMINISHING_LIMITONLY = 19, - DIMINISHING_DRAGONS_BREATH = 20 + DIMINISHING_DRAGONS_BREATH = 20, + + DIMINISHING_MAX }; enum SummonCategory diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp index bae8e541ddc..61916504758 100644 --- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp @@ -34,8 +34,8 @@ void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner) owner->ClearUnitState(UNIT_STATE_EVADE); owner->SetWalk(true); owner->LoadCreaturesAddon(); - owner->SetSpawnHealth(); owner->AI()->JustReachedHome(); + owner->SetSpawnHealth(); } } diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 2737c852d98..b9357d60967 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -1202,12 +1202,11 @@ void ScriptMgr::FillSpellSummary() } } -template<typename T, typename F> -void CreateSpellOrAuraScripts(uint32 spellId, std::list<T*>& scriptVector, F&& extractor) +template<typename T, typename F, typename O> +void CreateSpellOrAuraScripts(uint32 spellId, std::vector<T*>& scriptVector, F&& extractor, O* objectInvoker) { SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(spellId); - - for (SpellScriptsContainer::iterator itr = bounds.first; itr != bounds.second; ++itr) + for (auto itr = bounds.first; itr != bounds.second; ++itr) { // When the script is disabled continue with the next one if (!itr->second.second) @@ -1218,24 +1217,28 @@ void CreateSpellOrAuraScripts(uint32 spellId, std::list<T*>& scriptVector, F&& e continue; T* script = (*tmpscript.*extractor)(); - if (!script) continue; script->_Init(&tmpscript->GetName(), spellId); + if (!script->_Load(objectInvoker)) + { + delete script; + continue; + } scriptVector.push_back(script); } } -void ScriptMgr::CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector) +void ScriptMgr::CreateSpellScripts(uint32 spellId, std::vector<SpellScript*>& scriptVector, Spell* invoker) const { - CreateSpellOrAuraScripts(spellId, scriptVector, &SpellScriptLoader::GetSpellScript); + CreateSpellOrAuraScripts(spellId, scriptVector, &SpellScriptLoader::GetSpellScript, invoker); } -void ScriptMgr::CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector) +void ScriptMgr::CreateAuraScripts(uint32 spellId, std::vector<AuraScript*>& scriptVector, Aura* invoker) const { - CreateSpellOrAuraScripts(spellId, scriptVector, &SpellScriptLoader::GetAuraScript); + CreateSpellOrAuraScripts(spellId, scriptVector, &SpellScriptLoader::GetAuraScript, invoker); } SpellScriptLoader* ScriptMgr::GetSpellScriptLoader(uint32 scriptId) diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index a6cce299645..22a84804089 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -29,6 +29,7 @@ class AccountMgr; class AuctionHouseObject; +class Aura; class AuraScript; class Battleground; class BattlegroundMap; @@ -896,8 +897,8 @@ class TC_GAME_API ScriptMgr public: /* SpellScriptLoader */ - void CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector); - void CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector); + void CreateSpellScripts(uint32 spellId, std::vector<SpellScript*>& scriptVector, Spell* invoker) const; + void CreateAuraScripts(uint32 spellId, std::vector<AuraScript*>& scriptVector, Aura* invoker) const; SpellScriptLoader* GetSpellScriptLoader(uint32 scriptId); public: /* ServerScript */ diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 997c2904e9e..8631c94c98a 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -204,7 +204,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleNoImmediateEffect, //144 SPELL_AURA_SAFE_FALL implemented in WorldSession::HandleMovementOpcodes &AuraEffect::HandleAuraModPetTalentsPoints, //145 SPELL_AURA_MOD_PET_TALENT_POINTS &AuraEffect::HandleNoImmediateEffect, //146 SPELL_AURA_ALLOW_TAME_PET_TYPE - &AuraEffect::HandleModStateImmunityMask, //147 SPELL_AURA_MECHANIC_IMMUNITY_MASK + &AuraEffect::HandleModMechanicImmunityMask, //147 SPELL_AURA_MECHANIC_IMMUNITY_MASK &AuraEffect::HandleAuraRetainComboPoints, //148 SPELL_AURA_RETAIN_COMBO_POINTS &AuraEffect::HandleNoImmediateEffect, //149 SPELL_AURA_REDUCE_PUSHBACK &AuraEffect::HandleShieldBlockValue, //150 SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT @@ -890,14 +890,12 @@ bool AuraEffect::IsAffectedOnSpell(SpellInfo const* spell) const { if (!spell) return false; - // Check family name - if (spell->SpellFamilyName != m_spellInfo->SpellFamilyName) + + // Check family name and EffectClassMask + if (!spell->IsAffected(m_spellInfo->SpellFamilyName, m_spellInfo->Effects[m_effIndex].SpellClassMask)) return false; - // Check EffectClassMask - if (m_spellInfo->Effects[m_effIndex].SpellClassMask & spell->SpellFamilyFlags) - return true; - return false; + return true; } void AuraEffect::SendTickImmune(Unit* target, Unit* caster) const @@ -976,15 +974,12 @@ bool AuraEffect::CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventIn return false; // Spell own damage at apply won't break CC - if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + if (spellInfo && spellInfo == GetSpellInfo()) { - if (spellInfo == GetSpellInfo()) - { - Aura* aura = GetBase(); - // called from spellcast, should not have ticked yet - if (aura->GetDuration() == aura->GetMaxDuration()) - return false; - } + Aura* aura = GetBase(); + // called from spellcast, should not have ticked yet + if (aura->GetDuration() == aura->GetMaxDuration()) + return false; } break; } @@ -1779,7 +1774,12 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo if (aurApp->GetRemoveMode()) return; + ShapeshiftForm prevForm = target->GetShapeshiftForm(); target->SetShapeshiftForm(form); + // add the shapeshift aura's boosts + if (prevForm != form) + HandleShapeshiftBoosts(target, true); + if (modelid > 0) { SpellInfo const* transformSpellInfo = sSpellMgr->GetSpellInfo(target->getTransForm()); @@ -1851,11 +1851,10 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo default: break; } - } - // adding/removing linked auras - // add/remove the shapeshift aura's boosts - HandleShapeshiftBoosts(target, apply); + // remove the shapeshift aura's boosts + HandleShapeshiftBoosts(target, false); + } if (target->GetTypeId() == TYPEID_PLAYER) target->ToPlayer()->InitDataForForm(); @@ -3069,249 +3068,13 @@ void AuraEffect::HandleAuraModUseNormalSpeed(AuraApplication const* aurApp, uint /*** IMMUNITY ***/ /*********************************************************/ -void AuraEffect::HandleModStateImmunityMask(AuraApplication const* aurApp, uint8 mode, bool apply) const +void AuraEffect::HandleModMechanicImmunityMask(AuraApplication const* aurApp, uint8 mode, bool apply) const { if (!(mode & AURA_EFFECT_HANDLE_REAL)) return; Unit* target = aurApp->GetTarget(); - std::list <AuraType> aura_immunity_list; - uint32 mechanic_immunity_list = 0; - int32 miscVal = GetMiscValue(); - - switch (miscVal) - { - case 27: - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SILENCE, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_SILENCE); - break; - case 96: - case 1615: - { - if (GetAmount()) - { - mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT) - | (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN) - | (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM) - | (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR) - | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED) - | (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT); - aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE); - aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR); - } - break; - } - case 679: - { - if (GetId() == 57742) - { - mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT) - | (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN) - | (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM) - | (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR) - | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED) - | (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT); - aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE); - aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR); - } - break; - } - case 1557: - { - if (GetId() == 64187) - { - mechanic_immunity_list = (1 << MECHANIC_STUN); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - } - else - { - mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT) - | (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN) - | (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM) - | (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR) - | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED) - | (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT); - aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE); - aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR); - } - break; - } - case 1614: - case 1694: - { - target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_TAUNT); - break; - } - case 1630: - { - if (!GetAmount()) - { - target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_TAUNT); - } - else - { - mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT) - | (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN) - | (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM) - | (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR) - | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED) - | (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT); - aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE); - aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR); - } - break; - } - case 477: - case 1733: - { - if (!GetAmount()) - { - mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT) - | (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN) - | (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM) - | (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR) - | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED) - | (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK_DEST, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT); - aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE); - aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR); - } - break; - } - case 878: - { - if (GetAmount() == 1) - { - mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_STUN) - | (1 << MECHANIC_DISORIENTED) | (1 << MECHANIC_FREEZE); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - } - break; - } - default: - break; - } - - if (aura_immunity_list.empty()) - { - if (miscVal & (1<<10)) - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - if (miscVal & (1<<1)) - aura_immunity_list.push_back(SPELL_AURA_TRANSFORM); - - // These flag can be recognized wrong: - if (miscVal & (1<<6)) - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - if (miscVal & (1<<0)) - aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT); - if (miscVal & (1<<2)) - aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE); - if (miscVal & (1<<9)) - aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR); - if (miscVal & (1<<7)) - aura_immunity_list.push_back(SPELL_AURA_MOD_DISARM); - } - - // apply immunities - for (std::list <AuraType>::iterator iter = aura_immunity_list.begin(); iter != aura_immunity_list.end(); ++iter) - target->ApplySpellImmune(GetId(), IMMUNITY_STATE, *iter, apply); - - if (apply && GetSpellInfo()->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) - { - target->RemoveAurasWithMechanic(mechanic_immunity_list, AURA_REMOVE_BY_DEFAULT, GetId()); - for (std::list <AuraType>::iterator iter = aura_immunity_list.begin(); iter != aura_immunity_list.end(); ++iter) - target->RemoveAurasByType(*iter); - } + m_spellInfo->ApplyAllSpellImmunitiesTo(target, GetEffIndex(), apply); } void AuraEffect::HandleModMechanicImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3320,52 +3083,7 @@ void AuraEffect::HandleModMechanicImmunity(AuraApplication const* aurApp, uint8 return; Unit* target = aurApp->GetTarget(); - uint32 mechanic; - - switch (GetId()) - { - case 34471: // The Beast Within - case 19574: // Bestial Wrath - mechanic = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_KNOCKOUT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_BANISH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SHACKLE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DAZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply); - break; - case 42292: // PvP trinket - case 59752: // Every Man for Himself - case 53490: // Bullheaded - mechanic = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; - // Actually we should apply immunities here, too, but the aura has only 100 ms duration, so there is practically no point - break; - case 54508: // Demonic Empowerment - mechanic = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - break; - default: - if (GetMiscValue() < 1) - return; - mechanic = 1 << GetMiscValue(); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, GetMiscValue(), apply); - break; - } - - if (apply && GetSpellInfo()->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) - target->RemoveAurasWithMechanic(mechanic, AURA_REMOVE_BY_DEFAULT, GetId()); + m_spellInfo->ApplyAllSpellImmunitiesTo(target, GetEffIndex(), apply); } void AuraEffect::HandleAuraModEffectImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3374,8 +3092,7 @@ void AuraEffect::HandleAuraModEffectImmunity(AuraApplication const* aurApp, uint return; Unit* target = aurApp->GetTarget(); - - target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, GetMiscValue(), apply); + m_spellInfo->ApplyAllSpellImmunitiesTo(target, GetEffIndex(), apply); // when removing flag aura, handle flag drop Player* player = target->ToPlayer(); @@ -3397,11 +3114,7 @@ void AuraEffect::HandleAuraModStateImmunity(AuraApplication const* aurApp, uint8 return; Unit* target = aurApp->GetTarget(); - - target->ApplySpellImmune(GetId(), IMMUNITY_STATE, GetMiscValue(), apply); - - if (apply && GetSpellInfo()->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) - target->RemoveAurasByType(AuraType(GetMiscValue()), ObjectGuid::Empty, GetBase()); + m_spellInfo->ApplyAllSpellImmunitiesTo(target, GetEffIndex(), apply); } void AuraEffect::HandleAuraModSchoolImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3410,8 +3123,7 @@ void AuraEffect::HandleAuraModSchoolImmunity(AuraApplication const* aurApp, uint return; Unit* target = aurApp->GetTarget(); - - target->ApplySpellImmune(GetId(), IMMUNITY_SCHOOL, GetMiscValue(), (apply)); + m_spellInfo->ApplyAllSpellImmunitiesTo(target, GetEffIndex(), apply); if (GetSpellInfo()->Mechanic == MECHANIC_BANISH) { @@ -3421,12 +3133,15 @@ void AuraEffect::HandleAuraModSchoolImmunity(AuraApplication const* aurApp, uint { bool banishFound = false; Unit::AuraEffectList const& banishAuras = target->GetAuraEffectsByType(GetAuraType()); - for (Unit::AuraEffectList::const_iterator i = banishAuras.begin(); i != banishAuras.end(); ++i) - if ((*i)->GetSpellInfo()->Mechanic == MECHANIC_BANISH) + for (AuraEffect const* aurEff : banishAuras) + { + if (aurEff->GetSpellInfo()->Mechanic == MECHANIC_BANISH) { banishFound = true; break; } + } + if (!banishFound) target->ClearUnitState(UNIT_STATE_ISOLATED); } @@ -3439,23 +3154,6 @@ void AuraEffect::HandleAuraModSchoolImmunity(AuraApplication const* aurApp, uint if (GetSpellInfo()->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY) && GetSpellInfo()->HasAttribute(SPELL_ATTR2_DAMAGE_REDUCED_SHIELD)) target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); - - /// @todo optimalize this cycle - use RemoveAurasWithInterruptFlags call or something else - if (apply - && GetSpellInfo()->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY) - && GetSpellInfo()->IsPositive()) // Only positive immunity removes auras - { - uint32 schoolMask = GetMiscValue(); - target->RemoveAppliedAuras([this, schoolMask](AuraApplication const* aurApp) - { - SpellInfo const* spell = aurApp->GetBase()->GetSpellInfo(); - return (spell->GetSchoolMask() & schoolMask) // Check for school mask - && GetSpellInfo()->CanDispelAura(spell) - && !aurApp->IsPositive() // Don't remove positive spells - && !spell->IsPassive() // Don't remove passive auras - && spell->Id != GetId(); // Don't remove self - }); - } } void AuraEffect::HandleAuraModDmgImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3464,8 +3162,7 @@ void AuraEffect::HandleAuraModDmgImmunity(AuraApplication const* aurApp, uint8 m return; Unit* target = aurApp->GetTarget(); - - target->ApplySpellImmune(GetId(), IMMUNITY_DAMAGE, GetMiscValue(), apply); + m_spellInfo->ApplyAllSpellImmunitiesTo(target, GetEffIndex(), apply); } void AuraEffect::HandleAuraModDispelImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3474,8 +3171,7 @@ void AuraEffect::HandleAuraModDispelImmunity(AuraApplication const* aurApp, uint return; Unit* target = aurApp->GetTarget(); - - target->ApplySpellDispelImmunity(m_spellInfo, DispelType(GetMiscValue()), (apply)); + m_spellInfo->ApplyAllSpellImmunitiesTo(target, GetEffIndex(), apply); } /*********************************************************/ @@ -5446,13 +5142,11 @@ void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const { // Master of Subtlety case 31666: - if (!target->HasAuraType(SPELL_AURA_MOD_STEALTH)) - target->RemoveAurasDueToSpell(31665); + target->RemoveAurasDueToSpell(31665); break; // Overkill case 58428: - if (!target->HasAuraType(SPELL_AURA_MOD_STEALTH)) - target->RemoveAurasDueToSpell(58427); + target->RemoveAurasDueToSpell(58427); break; } break; @@ -5849,8 +5543,6 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const } } - uint32 absorb = 0; - uint32 resist = 0; CleanDamage cleanDamage = CleanDamage(0, 0, BASE_ATTACK, MELEE_HIT_NORMAL); // AOE spells are not affected by the new periodic system. @@ -5945,13 +5637,16 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const damage = caster->SpellCriticalDamageBonus(m_spellInfo, damage, target); int32 dmg = damage; - if (!GetSpellInfo()->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) - caster->ApplyResilience(target, NULL, &dmg, crit, CR_CRIT_TAKEN_SPELL); + caster->ApplyResilience(target, nullptr, &dmg, crit, CR_CRIT_TAKEN_SPELL); damage = dmg; - caster->CalcAbsorbResist(target, GetSpellInfo()->GetSchoolMask(), DOT, damage, &absorb, &resist, GetSpellInfo()); + DamageInfo damageInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask(), DOT, BASE_ATTACK); + caster->CalcAbsorbResist(damageInfo); + damage = damageInfo.GetDamage(); + uint32 absorb = damageInfo.GetAbsorb(); + uint32 resist = damageInfo.GetResist(); TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s attacked %s for %u dmg inflicted by %u absorb is %u", GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), damage, GetId(), absorb); @@ -5960,10 +5655,12 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const // Set trigger flag uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; - uint32 hitMask = crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL; - damage = (damage <= absorb+resist) ? 0 : (damage-absorb-resist); + uint32 hitMask = damageInfo.GetHitMask(); if (damage) + { + hitMask |= crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL; procVictim |= PROC_FLAG_TAKEN_DAMAGE; + } int32 overkill = damage - target->GetHealth(); if (overkill < 0) @@ -5972,7 +5669,6 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const SpellPeriodicAuraLogInfo pInfo(this, damage, overkill, absorb, resist, 0.0f, crit); target->SendPeriodicAuraLog(&pInfo); - DamageInfo damageInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask(), DOT, BASE_ATTACK); caster->ProcSkillsAndAuras(target, procAttacker, procVictim, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_NONE, hitMask, nullptr, &damageInfo, nullptr); caster->DealDamage(target, damage, &cleanDamage, DOT, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), true); @@ -5993,8 +5689,6 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c caster->SpellHitResult(target, GetSpellInfo(), false) != SPELL_MISS_NONE) return; - uint32 absorb = 0; - uint32 resist = 0; CleanDamage cleanDamage = CleanDamage(0, 0, BASE_ATTACK, MELEE_HIT_NORMAL); bool isAreaAura = m_spellInfo->Effects[m_effIndex].IsAreaAuraEffect() || m_spellInfo->Effects[m_effIndex].IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA); @@ -6037,29 +5731,33 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c int32 dmg = damage; if (!GetSpellInfo()->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) - caster->ApplyResilience(target, NULL, &dmg, crit, CR_CRIT_TAKEN_SPELL); + caster->ApplyResilience(target, nullptr, &dmg, crit, CR_CRIT_TAKEN_SPELL); damage = dmg; - caster->CalcAbsorbResist(target, GetSpellInfo()->GetSchoolMask(), DOT, damage, &absorb, &resist, m_spellInfo); + DamageInfo damageInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask(), DOT, BASE_ATTACK); + caster->CalcAbsorbResist(damageInfo); + uint32 absorb = damageInfo.GetAbsorb(); + uint32 resist = damageInfo.GetResist(); TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s health leech of %s for %u dmg inflicted by %u abs is %u", GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), damage, GetId(), absorb); + // SendSpellNonMeleeDamageLog expects non-absorbed/non-resisted damage caster->SendSpellNonMeleeDamageLog(target, GetId(), damage, GetSpellInfo()->GetSchoolMask(), absorb, resist, false, 0, crit); + damage = damageInfo.GetDamage(); // Set trigger flag uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; - uint32 hitMask = crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL; - damage = (damage <= absorb+resist) ? 0 : (damage-absorb-resist); + uint32 hitMask = damageInfo.GetHitMask(); if (damage) + { + hitMask |= crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL; procVictim |= PROC_FLAG_TAKEN_DAMAGE; + } if (caster->IsAlive()) - { - DamageInfo damageInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask(), DOT, BASE_ATTACK); caster->ProcSkillsAndAuras(target, procAttacker, procVictim, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_NONE, hitMask, nullptr, &damageInfo, nullptr); - } int32 new_damage = caster->DealDamage(target, damage, &cleanDamage, DOT, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), false); if (caster->IsAlive()) diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h index 2bfdde97b4b..05bfe7c0534 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.h +++ b/src/server/game/Spells/Auras/SpellAuraEffects.h @@ -198,7 +198,7 @@ class TC_GAME_API AuraEffect void HandleAuraModDecreaseSpeed(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleAuraModUseNormalSpeed(AuraApplication const* aurApp, uint8 mode, bool apply) const; // immunity - void HandleModStateImmunityMask(AuraApplication const* aurApp, uint8 mode, bool apply) const; + void HandleModMechanicImmunityMask(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleModMechanicImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleAuraModEffectImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleAuraModStateImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const; diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 2a6545520ae..39797269c2b 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -360,7 +360,7 @@ m_procCooldown(std::chrono::steady_clock::time_point::min()) AuraScript* Aura::GetScriptByName(std::string const& scriptName) const { - for (std::list<AuraScript*>::const_iterator itr = m_loadedScripts.begin(); itr != m_loadedScripts.end(); ++itr) + for (auto itr = m_loadedScripts.begin(); itr != m_loadedScripts.end(); ++itr) if ((*itr)->_GetScriptName()->compare(scriptName) == 0) return *itr; return NULL; @@ -381,12 +381,10 @@ void Aura::_InitEffects(uint8 effMask, Unit* caster, int32 *baseAmount) Aura::~Aura() { // unload scripts - while (!m_loadedScripts.empty()) + for (auto itr = m_loadedScripts.begin(); itr != m_loadedScripts.end(); ++itr) { - std::list<AuraScript*>::iterator itr = m_loadedScripts.begin(); (*itr)->_Unload(); delete (*itr); - m_loadedScripts.erase(itr); } // free effects memory @@ -490,37 +488,36 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) m_updateTargetMapInterval = UPDATE_TARGET_MAP_INTERVAL; // fill up to date target list - // target, effMask - std::map<Unit*, uint8> targets; - + // target, effMask + std::unordered_map<Unit*, uint8> targets; FillTargetMap(targets, caster); - UnitList targetsToRemove; + std::deque<Unit*> targetsToRemove; // mark all auras as ready to remove for (ApplicationMap::iterator appIter = m_applications.begin(); appIter != m_applications.end();++appIter) { - std::map<Unit*, uint8>::iterator existing = targets.find(appIter->second->GetTarget()); + auto itr = targets.find(appIter->second->GetTarget()); // not found in current area - remove the aura - if (existing == targets.end()) + if (itr == targets.end()) targetsToRemove.push_back(appIter->second->GetTarget()); else { // needs readding - remove now, will be applied in next update cycle // (dbcs do not have auras which apply on same type of targets but have different radius, so this is not really needed) - if (appIter->second->GetEffectMask() != existing->second || !CanBeAppliedOn(existing->first)) + if (appIter->second->GetEffectMask() != itr->second || !CanBeAppliedOn(itr->first)) targetsToRemove.push_back(appIter->second->GetTarget()); // nothing todo - aura already applied // remove from auras to register list - targets.erase(existing); + targets.erase(itr); } } // register auras for units - for (std::map<Unit*, uint8>::iterator itr = targets.begin(); itr!= targets.end();) + for (auto itr = targets.begin(); itr!= targets.end();) { // aura mustn't be already applied on target - if (AuraApplication * aurApp = GetApplicationOfTarget(itr->first->GetGUID())) + if (AuraApplication* aurApp = GetApplicationOfTarget(itr->first->GetGUID())) { // the core created 2 different units with same guid // this is a major failue, which i can't fix right now @@ -530,7 +527,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) if (aurApp->GetTarget() != itr->first) { // remove from auras to register list - targets.erase(itr++); + itr = targets.erase(itr); continue; } else @@ -584,7 +581,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) } } if (!addUnit) - targets.erase(itr++); + itr = targets.erase(itr); else { // owner has to be in world, or effect has to be applied to self @@ -602,17 +599,17 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) } // remove auras from units no longer needing them - for (UnitList::iterator itr = targetsToRemove.begin(); itr != targetsToRemove.end();++itr) - if (AuraApplication * aurApp = GetApplicationOfTarget((*itr)->GetGUID())) - (*itr)->_UnapplyAura(aurApp, AURA_REMOVE_BY_DEFAULT); + for (Unit* unit : targetsToRemove) + if (AuraApplication* aurApp = GetApplicationOfTarget(unit->GetGUID())) + unit->_UnapplyAura(aurApp, AURA_REMOVE_BY_DEFAULT); if (!apply) return; // apply aura effects for units - for (std::map<Unit*, uint8>::iterator itr = targets.begin(); itr!= targets.end();++itr) + for (auto itr = targets.begin(); itr!= targets.end(); ++itr) { - if (AuraApplication * aurApp = GetApplicationOfTarget(itr->first->GetGUID())) + if (AuraApplication* aurApp = GetApplicationOfTarget(itr->first->GetGUID())) { // owner has to be in world, or effect has to be applied to self ASSERT((!GetOwner()->IsInWorld() && GetOwner() == itr->first) || GetOwner()->IsInMap(itr->first)); @@ -988,6 +985,10 @@ bool Aura::CanBeSaved() const if (IsUsingCharges() && !GetCharges()) return false; + // don't save permanent auras triggered by items, they'll be recasted on login if necessary + if (GetCastItemGUID() && IsPermanent()) + return false; + return true; } @@ -1592,6 +1593,9 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b target->CastSpell(target, 31666, true); else { + // Remove counter aura + target->RemoveAurasDueToSpell(31666); + int32 basepoints0 = aurEff->GetAmount(); target->CastCustomSpell(target, 31665, &basepoints0, NULL, NULL, true); } @@ -1602,7 +1606,12 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b if (!apply) target->CastSpell(target, 58428, true); else + { + // Remove counter aura + target->RemoveAurasDueToSpell(58428); + target->CastSpell(target, 58427, true); + } } break; } @@ -1893,8 +1902,16 @@ uint8 Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& event if (!sSpellMgr->CanSpellTriggerProcOnEvent(*procEntry, eventInfo)) return 0; - // check if aura can proc when spell is triggered - if (!(procEntry->AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC)) + // check don't break stealth attr present + if (m_spellInfo->HasAura(SPELL_AURA_MOD_STEALTH)) + { + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + if (spellInfo->HasAttribute(SPELL_ATTR0_CU_DONT_BREAK_STEALTH)) + return 0; + } + + // check if aura can proc when spell is triggered (exception for hunter auto shot & wands) + if (!(procEntry->AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC) && !(eventInfo.GetTypeMask() & AUTO_ATTACK_PROC_FLAG_MASK)) if (Spell const* spell = eventInfo.GetProcSpell()) if (spell->IsTriggered()) if (!GetSpellInfo()->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) @@ -2025,30 +2042,21 @@ void Aura::_DeleteRemovedApplications() void Aura::LoadScripts() { - sScriptMgr->CreateAuraScripts(m_spellInfo->Id, m_loadedScripts); - for (std::list<AuraScript*>::iterator itr = m_loadedScripts.begin(); itr != m_loadedScripts.end();) + sScriptMgr->CreateAuraScripts(m_spellInfo->Id, m_loadedScripts, this); + for (auto itr = m_loadedScripts.begin(); itr != m_loadedScripts.end(); ++itr) { - if (!(*itr)->_Load(this)) - { - std::list<AuraScript*>::iterator bitr = itr; - ++itr; - delete (*bitr); - m_loadedScripts.erase(bitr); - continue; - } TC_LOG_DEBUG("spells", "Aura::LoadScripts: Script `%s` for aura `%u` is loaded now", (*itr)->_GetScriptName()->c_str(), m_spellInfo->Id); (*itr)->Register(); - ++itr; } } bool Aura::CallScriptCheckAreaTargetHandlers(Unit* target) { bool result = true; - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_AREA_TARGET); - std::list<AuraScript::CheckAreaTargetHandler>::iterator hookItrEnd = (*scritr)->DoCheckAreaTarget.end(), hookItr = (*scritr)->DoCheckAreaTarget.begin(); + auto hookItrEnd = (*scritr)->DoCheckAreaTarget.end(), hookItr = (*scritr)->DoCheckAreaTarget.begin(); for (; hookItr != hookItrEnd; ++hookItr) result &= hookItr->Call(*scritr, target); @@ -2059,10 +2067,10 @@ bool Aura::CallScriptCheckAreaTargetHandlers(Unit* target) void Aura::CallScriptDispel(DispelInfo* dispelInfo) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_DISPEL); - std::list<AuraScript::AuraDispelHandler>::iterator hookItrEnd = (*scritr)->OnDispel.end(), hookItr = (*scritr)->OnDispel.begin(); + auto hookItrEnd = (*scritr)->OnDispel.end(), hookItr = (*scritr)->OnDispel.begin(); for (; hookItr != hookItrEnd; ++hookItr) hookItr->Call(*scritr, dispelInfo); @@ -2072,10 +2080,10 @@ void Aura::CallScriptDispel(DispelInfo* dispelInfo) void Aura::CallScriptAfterDispel(DispelInfo* dispelInfo) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_AFTER_DISPEL); - std::list<AuraScript::AuraDispelHandler>::iterator hookItrEnd = (*scritr)->AfterDispel.end(), hookItr = (*scritr)->AfterDispel.begin(); + auto hookItrEnd = (*scritr)->AfterDispel.end(), hookItr = (*scritr)->AfterDispel.begin(); for (; hookItr != hookItrEnd; ++hookItr) hookItr->Call(*scritr, dispelInfo); @@ -2086,10 +2094,10 @@ void Aura::CallScriptAfterDispel(DispelInfo* dispelInfo) bool Aura::CallScriptEffectApplyHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode) { bool preventDefault = false; - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_APPLY, aurApp); - std::list<AuraScript::EffectApplyHandler>::iterator effEndItr = (*scritr)->OnEffectApply.end(), effItr = (*scritr)->OnEffectApply.begin(); + auto effEndItr = (*scritr)->OnEffectApply.end(), effItr = (*scritr)->OnEffectApply.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff, mode); @@ -2106,10 +2114,10 @@ bool Aura::CallScriptEffectApplyHandlers(AuraEffect const* aurEff, AuraApplicati bool Aura::CallScriptEffectRemoveHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode) { bool preventDefault = false; - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_REMOVE, aurApp); - std::list<AuraScript::EffectApplyHandler>::iterator effEndItr = (*scritr)->OnEffectRemove.end(), effItr = (*scritr)->OnEffectRemove.begin(); + auto effEndItr = (*scritr)->OnEffectRemove.end(), effItr = (*scritr)->OnEffectRemove.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff, mode); @@ -2124,10 +2132,10 @@ bool Aura::CallScriptEffectRemoveHandlers(AuraEffect const* aurEff, AuraApplicat void Aura::CallScriptAfterEffectApplyHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_AFTER_APPLY, aurApp); - std::list<AuraScript::EffectApplyHandler>::iterator effEndItr = (*scritr)->AfterEffectApply.end(), effItr = (*scritr)->AfterEffectApply.begin(); + auto effEndItr = (*scritr)->AfterEffectApply.end(), effItr = (*scritr)->AfterEffectApply.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff, mode); @@ -2138,10 +2146,10 @@ void Aura::CallScriptAfterEffectApplyHandlers(AuraEffect const* aurEff, AuraAppl void Aura::CallScriptAfterEffectRemoveHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_AFTER_REMOVE, aurApp); - std::list<AuraScript::EffectApplyHandler>::iterator effEndItr = (*scritr)->AfterEffectRemove.end(), effItr = (*scritr)->AfterEffectRemove.begin(); + auto effEndItr = (*scritr)->AfterEffectRemove.end(), effItr = (*scritr)->AfterEffectRemove.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff, mode); @@ -2153,10 +2161,10 @@ void Aura::CallScriptAfterEffectRemoveHandlers(AuraEffect const* aurEff, AuraApp bool Aura::CallScriptEffectPeriodicHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp) { bool preventDefault = false; - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_PERIODIC, aurApp); - std::list<AuraScript::EffectPeriodicHandler>::iterator effEndItr = (*scritr)->OnEffectPeriodic.end(), effItr = (*scritr)->OnEffectPeriodic.begin(); + auto effEndItr = (*scritr)->OnEffectPeriodic.end(), effItr = (*scritr)->OnEffectPeriodic.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff); @@ -2172,10 +2180,10 @@ bool Aura::CallScriptEffectPeriodicHandlers(AuraEffect const* aurEff, AuraApplic void Aura::CallScriptEffectUpdatePeriodicHandlers(AuraEffect* aurEff) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_UPDATE_PERIODIC); - std::list<AuraScript::EffectUpdatePeriodicHandler>::iterator effEndItr = (*scritr)->OnEffectUpdatePeriodic.end(), effItr = (*scritr)->OnEffectUpdatePeriodic.begin(); + auto effEndItr = (*scritr)->OnEffectUpdatePeriodic.end(), effItr = (*scritr)->OnEffectUpdatePeriodic.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff); @@ -2186,10 +2194,10 @@ void Aura::CallScriptEffectUpdatePeriodicHandlers(AuraEffect* aurEff) void Aura::CallScriptEffectCalcAmountHandlers(AuraEffect const* aurEff, int32 & amount, bool & canBeRecalculated) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_CALC_AMOUNT); - std::list<AuraScript::EffectCalcAmountHandler>::iterator effEndItr = (*scritr)->DoEffectCalcAmount.end(), effItr = (*scritr)->DoEffectCalcAmount.begin(); + auto effEndItr = (*scritr)->DoEffectCalcAmount.end(), effItr = (*scritr)->DoEffectCalcAmount.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff, amount, canBeRecalculated); @@ -2200,10 +2208,10 @@ void Aura::CallScriptEffectCalcAmountHandlers(AuraEffect const* aurEff, int32 & void Aura::CallScriptEffectCalcPeriodicHandlers(AuraEffect const* aurEff, bool & isPeriodic, int32 & amplitude) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_CALC_PERIODIC); - std::list<AuraScript::EffectCalcPeriodicHandler>::iterator effEndItr = (*scritr)->DoEffectCalcPeriodic.end(), effItr = (*scritr)->DoEffectCalcPeriodic.begin(); + auto effEndItr = (*scritr)->DoEffectCalcPeriodic.end(), effItr = (*scritr)->DoEffectCalcPeriodic.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff, isPeriodic, amplitude); @@ -2214,10 +2222,10 @@ void Aura::CallScriptEffectCalcPeriodicHandlers(AuraEffect const* aurEff, bool & void Aura::CallScriptEffectCalcSpellModHandlers(AuraEffect const* aurEff, SpellModifier* & spellMod) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_CALC_SPELLMOD); - std::list<AuraScript::EffectCalcSpellModHandler>::iterator effEndItr = (*scritr)->DoEffectCalcSpellMod.end(), effItr = (*scritr)->DoEffectCalcSpellMod.begin(); + auto effEndItr = (*scritr)->DoEffectCalcSpellMod.end(), effItr = (*scritr)->DoEffectCalcSpellMod.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff, spellMod); @@ -2228,10 +2236,10 @@ void Aura::CallScriptEffectCalcSpellModHandlers(AuraEffect const* aurEff, SpellM void Aura::CallScriptEffectAbsorbHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount, bool& defaultPrevented) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_ABSORB, aurApp); - std::list<AuraScript::EffectAbsorbHandler>::iterator effEndItr = (*scritr)->OnEffectAbsorb.end(), effItr = (*scritr)->OnEffectAbsorb.begin(); + auto effEndItr = (*scritr)->OnEffectAbsorb.end(), effItr = (*scritr)->OnEffectAbsorb.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) @@ -2246,10 +2254,10 @@ void Aura::CallScriptEffectAbsorbHandlers(AuraEffect* aurEff, AuraApplication co void Aura::CallScriptEffectAfterAbsorbHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_AFTER_ABSORB, aurApp); - std::list<AuraScript::EffectAbsorbHandler>::iterator effEndItr = (*scritr)->AfterEffectAbsorb.end(), effItr = (*scritr)->AfterEffectAbsorb.begin(); + auto effEndItr = (*scritr)->AfterEffectAbsorb.end(), effItr = (*scritr)->AfterEffectAbsorb.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff, dmgInfo, absorbAmount); @@ -2260,10 +2268,10 @@ void Aura::CallScriptEffectAfterAbsorbHandlers(AuraEffect* aurEff, AuraApplicati void Aura::CallScriptEffectManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount, bool & /*defaultPrevented*/) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_MANASHIELD, aurApp); - std::list<AuraScript::EffectManaShieldHandler>::iterator effEndItr = (*scritr)->OnEffectManaShield.end(), effItr = (*scritr)->OnEffectManaShield.begin(); + auto effEndItr = (*scritr)->OnEffectManaShield.end(), effItr = (*scritr)->OnEffectManaShield.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff, dmgInfo, absorbAmount); @@ -2274,10 +2282,10 @@ void Aura::CallScriptEffectManaShieldHandlers(AuraEffect* aurEff, AuraApplicatio void Aura::CallScriptEffectAfterManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_AFTER_MANASHIELD, aurApp); - std::list<AuraScript::EffectManaShieldHandler>::iterator effEndItr = (*scritr)->AfterEffectManaShield.end(), effItr = (*scritr)->AfterEffectManaShield.begin(); + auto effEndItr = (*scritr)->AfterEffectManaShield.end(), effItr = (*scritr)->AfterEffectManaShield.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff, dmgInfo, absorbAmount); @@ -2288,10 +2296,10 @@ void Aura::CallScriptEffectAfterManaShieldHandlers(AuraEffect* aurEff, AuraAppli void Aura::CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & splitAmount) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_SPLIT, aurApp); - std::list<AuraScript::EffectSplitHandler>::iterator effEndItr = (*scritr)->OnEffectSplit.end(), effItr = (*scritr)->OnEffectSplit.begin(); + auto effEndItr = (*scritr)->OnEffectSplit.end(), effItr = (*scritr)->OnEffectSplit.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff, dmgInfo, splitAmount); @@ -2303,10 +2311,10 @@ void Aura::CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication con bool Aura::CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) { bool result = true; - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_PROC, aurApp); - std::list<AuraScript::CheckProcHandler>::iterator hookItrEnd = (*scritr)->DoCheckProc.end(), hookItr = (*scritr)->DoCheckProc.begin(); + auto hookItrEnd = (*scritr)->DoCheckProc.end(), hookItr = (*scritr)->DoCheckProc.begin(); for (; hookItr != hookItrEnd; ++hookItr) result &= hookItr->Call(*scritr, eventInfo); @@ -2319,10 +2327,10 @@ bool Aura::CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventI bool Aura::CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) { bool prepare = true; - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_PREPARE_PROC, aurApp); - std::list<AuraScript::AuraProcHandler>::iterator effEndItr = (*scritr)->DoPrepareProc.end(), effItr = (*scritr)->DoPrepareProc.begin(); + auto effEndItr = (*scritr)->DoPrepareProc.end(), effItr = (*scritr)->DoPrepareProc.begin(); for (; effItr != effEndItr; ++effItr) effItr->Call(*scritr, eventInfo); @@ -2338,10 +2346,10 @@ bool Aura::CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEven bool Aura::CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) { bool handled = false; - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_PROC, aurApp); - std::list<AuraScript::AuraProcHandler>::iterator hookItrEnd = (*scritr)->OnProc.end(), hookItr = (*scritr)->OnProc.begin(); + auto hookItrEnd = (*scritr)->OnProc.end(), hookItr = (*scritr)->OnProc.begin(); for (; hookItr != hookItrEnd; ++hookItr) hookItr->Call(*scritr, eventInfo); @@ -2354,10 +2362,10 @@ bool Aura::CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& void Aura::CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_AFTER_PROC, aurApp); - std::list<AuraScript::AuraProcHandler>::iterator hookItrEnd = (*scritr)->AfterProc.end(), hookItr = (*scritr)->AfterProc.begin(); + auto hookItrEnd = (*scritr)->AfterProc.end(), hookItr = (*scritr)->AfterProc.begin(); for (; hookItr != hookItrEnd; ++hookItr) hookItr->Call(*scritr, eventInfo); @@ -2368,10 +2376,10 @@ void Aura::CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventI bool Aura::CallScriptCheckEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo) { bool result = true; - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC, aurApp); - std::list<AuraScript::CheckEffectProcHandler>::iterator hookItrEnd = (*scritr)->DoCheckEffectProc.end(), hookItr = (*scritr)->DoCheckEffectProc.begin(); + auto hookItrEnd = (*scritr)->DoCheckEffectProc.end(), hookItr = (*scritr)->DoCheckEffectProc.begin(); for (; hookItr != hookItrEnd; ++hookItr) if (hookItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) result &= hookItr->Call(*scritr, aurEff, eventInfo); @@ -2385,10 +2393,10 @@ bool Aura::CallScriptCheckEffectProcHandlers(AuraEffect const* aurEff, AuraAppli bool Aura::CallScriptEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo) { bool preventDefault = false; - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_PROC, aurApp); - std::list<AuraScript::EffectProcHandler>::iterator effEndItr = (*scritr)->OnEffectProc.end(), effItr = (*scritr)->OnEffectProc.begin(); + auto effEndItr = (*scritr)->OnEffectProc.end(), effItr = (*scritr)->OnEffectProc.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff, eventInfo); @@ -2403,10 +2411,10 @@ bool Aura::CallScriptEffectProcHandlers(AuraEffect const* aurEff, AuraApplicatio void Aura::CallScriptAfterEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo) { - for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_AFTER_PROC, aurApp); - std::list<AuraScript::EffectProcHandler>::iterator effEndItr = (*scritr)->AfterEffectProc.end(), effItr = (*scritr)->AfterEffectProc.begin(); + auto effEndItr = (*scritr)->AfterEffectProc.end(), effItr = (*scritr)->AfterEffectProc.begin(); for (; effItr != effEndItr; ++effItr) if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) effItr->Call(*scritr, aurEff, eventInfo); @@ -2449,18 +2457,17 @@ void UnitAura::Remove(AuraRemoveMode removeMode) GetUnitOwner()->RemoveOwnedAura(this, removeMode); } -void UnitAura::FillTargetMap(std::map<Unit*, uint8> & targets, Unit* caster) +void UnitAura::FillTargetMap(std::unordered_map<Unit*, uint8>& targets, Unit* caster) { for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) { if (!HasEffect(effIndex)) continue; - UnitList targetList; + + std::deque<Unit*> units; // non-area aura if (GetSpellInfo()->Effects[effIndex].Effect == SPELL_EFFECT_APPLY_AURA) - { - targetList.push_back(GetUnitOwner()); - } + units.push_back(GetUnitOwner()); else { float radius = GetSpellInfo()->Effects[effIndex].CalcRadius(caster); @@ -2472,48 +2479,48 @@ void UnitAura::FillTargetMap(std::map<Unit*, uint8> & targets, Unit* caster) case SPELL_EFFECT_APPLY_AREA_AURA_PARTY: case SPELL_EFFECT_APPLY_AREA_AURA_RAID: { - targetList.push_back(GetUnitOwner()); - Trinity::AnyGroupedUnitInObjectRangeCheck u_check(GetUnitOwner(), GetUnitOwner(), radius, GetSpellInfo()->Effects[effIndex].Effect == SPELL_EFFECT_APPLY_AREA_AURA_RAID); - Trinity::UnitListSearcher<Trinity::AnyGroupedUnitInObjectRangeCheck> searcher(GetUnitOwner(), targetList, u_check); + units.push_back(GetUnitOwner()); + Trinity::AnyGroupedUnitInObjectRangeCheck u_check(GetUnitOwner(), GetUnitOwner(), radius, m_spellInfo->Effects[effIndex].Effect == SPELL_EFFECT_APPLY_AREA_AURA_RAID, m_spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS)); + Trinity::UnitListSearcher<Trinity::AnyGroupedUnitInObjectRangeCheck> searcher(GetUnitOwner(), units, u_check); GetUnitOwner()->VisitNearbyObject(radius, searcher); break; } case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND: { - targetList.push_back(GetUnitOwner()); - Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(GetUnitOwner(), GetUnitOwner(), radius); - Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(GetUnitOwner(), targetList, u_check); + units.push_back(GetUnitOwner()); + Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(GetUnitOwner(), GetUnitOwner(), radius, m_spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS)); + Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(GetUnitOwner(), units, u_check); GetUnitOwner()->VisitNearbyObject(radius, searcher); break; } case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY: { - Trinity::AnyAoETargetUnitInObjectRangeCheck u_check(GetUnitOwner(), GetUnitOwner(), radius); // No GetCharmer in searcher - Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck> searcher(GetUnitOwner(), targetList, u_check); + Trinity::AnyAoETargetUnitInObjectRangeCheck u_check(GetUnitOwner(), GetUnitOwner(), radius, m_spellInfo); // No GetCharmer in searcher + Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck> searcher(GetUnitOwner(), units, u_check); GetUnitOwner()->VisitNearbyObject(radius, searcher); break; } case SPELL_EFFECT_APPLY_AREA_AURA_PET: - targetList.push_back(GetUnitOwner()); + units.push_back(GetUnitOwner()); // no break case SPELL_EFFECT_APPLY_AREA_AURA_OWNER: { if (Unit* owner = GetUnitOwner()->GetCharmerOrOwner()) if (GetUnitOwner()->IsWithinDistInMap(owner, radius)) - targetList.push_back(owner); + units.push_back(owner); break; } } } } - for (UnitList::iterator itr = targetList.begin(); itr!= targetList.end();++itr) + for (Unit* unit : units) { - std::map<Unit*, uint8>::iterator existing = targets.find(*itr); - if (existing != targets.end()) - existing->second |= 1<<effIndex; + auto itr = targets.find(unit); + if (itr != targets.end()) + itr->second |= 1 << effIndex; else - targets[*itr] = 1<<effIndex; + targets[unit] = 1 << effIndex; } } } @@ -2536,7 +2543,7 @@ void DynObjAura::Remove(AuraRemoveMode removeMode) _Remove(removeMode); } -void DynObjAura::FillTargetMap(std::map<Unit*, uint8> & targets, Unit* /*caster*/) +void DynObjAura::FillTargetMap(std::unordered_map<Unit*, uint8>& targets, Unit* /*caster*/) { Unit* dynObjOwnerCaster = GetDynobjOwner()->GetCaster(); float radius = GetDynobjOwner()->GetRadius(); @@ -2545,28 +2552,29 @@ void DynObjAura::FillTargetMap(std::map<Unit*, uint8> & targets, Unit* /*caster* { if (!HasEffect(effIndex)) continue; - UnitList targetList; + + std::deque<Unit*> units; if (GetSpellInfo()->Effects[effIndex].TargetB.GetTarget() == TARGET_DEST_DYNOBJ_ALLY || GetSpellInfo()->Effects[effIndex].TargetB.GetTarget() == TARGET_UNIT_DEST_AREA_ALLY) { - Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(GetDynobjOwner(), dynObjOwnerCaster, radius); - Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(GetDynobjOwner(), targetList, u_check); + Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(GetDynobjOwner(), dynObjOwnerCaster, radius, m_spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS)); + Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(GetDynobjOwner(), units, u_check); GetDynobjOwner()->VisitNearbyObject(radius, searcher); } else { Trinity::AnyAoETargetUnitInObjectRangeCheck u_check(GetDynobjOwner(), dynObjOwnerCaster, radius); - Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck> searcher(GetDynobjOwner(), targetList, u_check); + Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck> searcher(GetDynobjOwner(), units, u_check); GetDynobjOwner()->VisitNearbyObject(radius, searcher); } - for (UnitList::iterator itr = targetList.begin(); itr!= targetList.end();++itr) + for (Unit* unit : units) { - std::map<Unit*, uint8>::iterator existing = targets.find(*itr); - if (existing != targets.end()) - existing->second |= 1<<effIndex; + auto itr = targets.find(unit); + if (itr != targets.end()) + itr->second |= 1 << effIndex; else - targets[*itr] = 1<<effIndex; + targets[unit] = 1 << effIndex; } } } diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index a526dd8e340..450a8c4dde2 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -113,7 +113,7 @@ class TC_GAME_API Aura void _Remove(AuraRemoveMode removeMode); virtual void Remove(AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT) = 0; - virtual void FillTargetMap(std::map<Unit*, uint8> & targets, Unit* caster) = 0; + virtual void FillTargetMap(std::unordered_map<Unit*, uint8>& targets, Unit* caster) = 0; void UpdateTargetMap(Unit* caster, bool apply = true); void _RegisterForTargets() {Unit* caster = GetCaster(); UpdateTargetMap(caster, false);} @@ -238,9 +238,11 @@ class TC_GAME_API Aura AuraScript* GetScriptByName(std::string const& scriptName) const; - std::list<AuraScript*> m_loadedScripts; + std::vector<AuraScript*> m_loadedScripts; + private: void _DeleteRemovedApplications(); + protected: SpellInfo const* const m_spellInfo; ObjectGuid const m_casterGuid; @@ -283,7 +285,7 @@ class TC_GAME_API UnitAura : public Aura void Remove(AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT) override; - void FillTargetMap(std::map<Unit*, uint8> & targets, Unit* caster) override; + void FillTargetMap(std::unordered_map<Unit*, uint8>& targets, Unit* caster) override; // Allow Apply Aura Handler to modify and access m_AuraDRGroup void SetDiminishGroup(DiminishingGroup group) { m_AuraDRGroup = group; } @@ -301,7 +303,7 @@ class TC_GAME_API DynObjAura : public Aura public: void Remove(AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT) override; - void FillTargetMap(std::map<Unit*, uint8> & targets, Unit* caster) override; + void FillTargetMap(std::unordered_map<Unit*, uint8>& targets, Unit* caster) override; }; class TC_GAME_API ChargeDropEvent : public BasicEvent diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index f072d71d9ff..a35f36f5e81 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -586,8 +586,6 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO destTarget = NULL; damage = 0; effectHandleMode = SPELL_EFFECT_HANDLE_LAUNCH; - m_diminishLevel = DIMINISHING_LEVEL_1; - m_diminishGroup = DIMINISHING_NONE; m_damage = 0; m_healing = 0; m_procAttacker = 0; @@ -628,12 +626,10 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO Spell::~Spell() { // unload scripts - while (!m_loadedScripts.empty()) + for (auto itr = m_loadedScripts.begin(); itr != m_loadedScripts.end(); ++itr) { - std::list<SpellScript*>::iterator itr = m_loadedScripts.begin(); (*itr)->_Unload(); delete (*itr); - m_loadedScripts.erase(itr); } if (m_referencedFromCurrentSpell && m_selfContainer && *m_selfContainer == this) @@ -649,7 +645,8 @@ Spell::~Spell() delete m_spellValue; - CheckEffectExecuteData(); + // missing cleanup somewhere, mem leaks so let's crash + AssertEffectExecuteData(); } void Spell::InitExplicitTargets(SpellCastTargets const& targets) @@ -1121,8 +1118,14 @@ void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTarge SpellTargetObjectTypes objectType = targetType.GetObjectType(); SpellTargetCheckTypes selectionType = targetType.GetCheckType(); ConditionContainer* condList = m_spellInfo->Effects[effIndex].ImplicitTargetConditions; - float coneAngle = float(M_PI) / 2; - float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod; + float coneAngle = float(M_PI) / 2.f; + + float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster); + // Workaround for some spells that don't have RadiusEntry set in dbc (but SpellRange instead) + if (G3D::fuzzyEq(radius, 0.f)) + radius = m_spellInfo->GetMaxRange(m_spellInfo->IsPositiveEffect(effIndex), m_caster, this); + + radius *= m_spellValue->RadiusMod; if (uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList)) { @@ -1208,7 +1211,13 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge return; } std::list<WorldObject*> targets; - float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod; + float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster); + // Workaround for some spells that don't have RadiusEntry set in dbc (but SpellRange instead) + if (G3D::fuzzyEq(radius, 0.f)) + radius = m_spellInfo->GetMaxRange(m_spellInfo->IsPositiveEffect(effIndex), m_caster, this); + + radius *= m_spellValue->RadiusMod; + SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), m_spellInfo->Effects[effIndex].ImplicitTargetConditions); CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType); @@ -1994,11 +2003,17 @@ void Spell::prepareDataForTriggerSystem() // Hunter trap spells - activation proc for Lock and Load, Entrapment and Misdirection if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && - (m_spellInfo->SpellFamilyFlags[0] & 0x18 || // Freezing and Frost Trap, Freezing Arrow - m_spellInfo->Id == 57879 || // Snake Trap - done this way to avoid double proc - m_spellInfo->SpellFamilyFlags[2] & 0x00024000)) // Explosive and Immolation Trap + (m_spellInfo->SpellFamilyFlags[0] & 0x18 || // Freezing and Frost Trap, Freezing Arrow + m_spellInfo->Id == 57879 || // Snake Trap - done this way to avoid double proc + m_spellInfo->SpellFamilyFlags[2] & 0x00024000)) // Explosive and Immolation Trap + { m_procAttacker |= PROC_FLAG_DONE_TRAP_ACTIVATION; + // also fill up other flags (DoAllEffectOnTarget only fills up flag if both are not set) + m_procAttacker |= PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG; + m_procVictim |= PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG; + } + // Hellfire Effect - trigger as DOT if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags[0] & 0x00000040) { @@ -2052,7 +2067,7 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= return; if (checkIfValid) - if (m_spellInfo->CheckTarget(m_caster, target, implicit) != SPELL_CAST_OK) + if (m_spellInfo->CheckTarget(m_caster, target, implicit || m_caster->GetEntry() == WORLD_TRIGGER) != SPELL_CAST_OK) // skip stealth checks for GO casts return; // Check for effect immune skip if immuned @@ -2354,7 +2369,19 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) if (m_damage > 0) positive = false; else if (!m_healing) - positive = m_spellInfo->IsPositive(); + { + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (!(target->effectMask & (1 << i))) + continue; + + if (!m_spellInfo->IsPositiveEffect(i)) + { + positive = false; + break; + } + } + } switch (m_spellInfo->DmgClass) { @@ -2517,15 +2544,12 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA effectMask &= ~(1 << effectNumber); else if (m_spellInfo->Effects[effectNumber].IsAura() && !m_spellInfo->IsPositiveEffect(effectNumber)) { - int32 debuff_resist_chance = unit->GetMaxPositiveAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(m_spellInfo->Dispel)); - debuff_resist_chance += unit->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(m_spellInfo->Dispel)); - - if (debuff_resist_chance > 0) - if (irand(0, 10000) <= (debuff_resist_chance * 100)) - { - effectMask &= ~(1 << effectNumber); - returnVal = SPELL_MISS_RESIST; - } + int32 debuff_resist_chance = unit->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, static_cast<int32>(m_spellInfo->Dispel)); + if (debuff_resist_chance > 0 && roll_chance_i(debuff_resist_chance)) + { + effectMask &= ~(1 << effectNumber); + returnVal = SPELL_MISS_RESIST; + } } } } @@ -2556,12 +2580,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA return SPELL_MISS_EVADE; if (m_caster->_IsValidAttackTarget(unit, m_spellInfo)) - { unit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_HITBYSPELL); - - if (!m_spellInfo->HasAttribute(SPELL_ATTR0_CU_DONT_BREAK_STEALTH)) - unit->RemoveAurasByType(SPELL_AURA_MOD_STEALTH); - } else if (m_caster->IsFriendlyTo(unit)) { // for delayed spells ignore negative spells (after duel end) for friendly targets @@ -2591,16 +2610,19 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA aura_effmask |= 1 << i; // Get Data Needed for Diminishing Returns, some effects may have multiple auras, so this must be done on spell hit, not aura add - m_diminishGroup = GetDiminishingReturnsGroupForSpell(m_spellInfo, m_triggeredByAuraSpell != nullptr); - if (m_diminishGroup && aura_effmask) + bool const triggered = m_triggeredByAuraSpell != nullptr; + DiminishingGroup const diminishGroup = m_spellInfo->GetDiminishingReturnsGroupForSpell(triggered); + + DiminishingLevels diminishLevel = DIMINISHING_LEVEL_1; + if (diminishGroup && aura_effmask) { - m_diminishLevel = unit->GetDiminishing(m_diminishGroup); - DiminishingReturnsType type = GetDiminishingReturnsGroupType(m_diminishGroup); + diminishLevel = unit->GetDiminishing(diminishGroup); + DiminishingReturnsType type = m_spellInfo->GetDiminishingReturnsGroupType(triggered); // Increase Diminishing on unit, current informations for actually casts will use values above if ((type == DRTYPE_PLAYER && (unit->GetCharmerOrOwnerPlayerOrPlayerItself() || (unit->GetTypeId() == TYPEID_UNIT && unit->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_ALL_DIMINISH))) || type == DRTYPE_ALL) - unit->IncrDiminishing(m_diminishGroup); + unit->IncrDiminishing(m_spellInfo, triggered); } if (aura_effmask) @@ -2643,8 +2665,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA // Now Reduce spell duration using data received at spell hit int32 duration = m_spellAura->GetMaxDuration(); - int32 limitduration = GetDiminishingReturnsLimitDuration(m_diminishGroup, aurSpellInfo); - float diminishMod = unit->ApplyDiminishingToDuration(m_diminishGroup, duration, m_originalCaster, m_diminishLevel, limitduration); + float diminishMod = unit->ApplyDiminishingToDuration(aurSpellInfo, triggered, duration, m_originalCaster, diminishLevel); // unit is immune to aura if it was diminished to 0 duration if (diminishMod == 0.0f) @@ -2659,7 +2680,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA } else { - ((UnitAura*)m_spellAura)->SetDiminishGroup(m_diminishGroup); + ((UnitAura*)m_spellAura)->SetDiminishGroup(diminishGroup); bool positive = m_spellAura->GetSpellInfo()->IsPositive(); if (AuraApplication* aurApp = m_spellAura->GetApplicationOfTarget(m_originalCaster->GetGUID())) @@ -2936,7 +2957,8 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered if ((_triggeredCastFlags & TRIGGERED_IGNORE_COMBO_POINTS) || m_CastItem || !m_caster->m_playerMovingMe) m_needComboPoints = false; - SpellCastResult result = CheckCast(true); + uint32 param1 = 0, param2 = 0; + SpellCastResult result = CheckCast(true, ¶m1, ¶m2); if (result != SPELL_CAST_OK && !IsAutoRepeat()) //always cast autorepeat dummy for triggering { // Periodic auras should be interrupted when aura triggers a spell which can't be cast @@ -2957,7 +2979,10 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered m_caster->ToPlayer()->SetSpellModTakingSpell(this, false); } - SendCastResult(result); + if (param1 || param2) + SendCastResult(result, ¶m1, ¶m2); + else + SendCastResult(result); finish(false); return; @@ -2982,7 +3007,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered m_casttime = m_spellInfo->CalcCastTime(this); if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) // _UNIT actually means creature. for some reason. - if (!(IsNextMeleeSwingSpell() || IsAutoRepeat() || _triggeredCastFlags & TRIGGERED_IGNORE_SET_FACING)) + if (!(m_spellInfo->IsNextMeleeSwingSpell() || IsAutoRepeat() || (_triggeredCastFlags & TRIGGERED_IGNORE_SET_FACING))) { if (m_targets.GetObjectTarget() && m_caster != m_targets.GetObjectTarget()) m_caster->ToCreature()->FocusTarget(this, m_targets.GetObjectTarget()); @@ -2995,8 +3020,8 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered // (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in) if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && !(m_caster->IsCharmed() && m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)) { - // 1. Is a channel spell, 2. Has no casttime, 3. And has flag to allow movement during channel - if (!(m_spellInfo->IsChanneled() && !m_casttime && m_spellInfo->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING))) + // 1. Has casttime, 2. Or doesn't have flag to allow movement during channel + if (m_casttime || !m_spellInfo->IsMoveAllowedChannel()) { SendCastResult(SPELL_FAILED_MOVING); finish(false); @@ -3144,10 +3169,11 @@ void Spell::cast(bool skipCheck) // skip check if done already (for instant cast spells for example) if (!skipCheck) { - SpellCastResult castResult = CheckCast(false); + uint32 param1 = 0, param2 = 0; + SpellCastResult castResult = CheckCast(false, ¶m1, ¶m2); if (castResult != SPELL_CAST_OK) { - SendCastResult(castResult); + SendCastResult(castResult, ¶m1, ¶m2); SendInterrupted(0); //restore spell mods if (m_caster->GetTypeId() == TYPEID_PLAYER) @@ -3460,9 +3486,6 @@ uint64 Spell::handle_delayed(uint64 t_offset) void Spell::_handle_immediate_phase() { m_spellAura = NULL; - // initialize Diminishing Returns Data - m_diminishLevel = DIMINISHING_LEVEL_1; - m_diminishGroup = DIMINISHING_NONE; // handle some immediate features of the spell here HandleThreatSpells(); @@ -3554,7 +3577,7 @@ void Spell::update(uint32 difftime) (m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)))) { // don't cancel for melee, autorepeat, triggered and instant spells - if (!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING))) + if (!m_spellInfo->IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->IsMoveAllowedChannel())) { // if charmed by creature, trust the AI not to cheat and allow the cast to proceed // @todo this is a hack, "creature" movesplines don't differentiate turning/moving right now @@ -3576,7 +3599,7 @@ void Spell::update(uint32 difftime) m_timer -= difftime; } - if (m_timer == 0 && !IsNextMeleeSwingSpell() && !IsAutoRepeat()) + if (m_timer == 0 && !m_spellInfo->IsNextMeleeSwingSpell() && !IsAutoRepeat()) // don't CheckCast for instant spells - done in spell::prepare, skip duplicate checks, needed for range checks for example cast(!m_casttime); break; @@ -3589,8 +3612,12 @@ void Spell::update(uint32 difftime) if (!UpdateChanneledTargetList()) { TC_LOG_DEBUG("spells", "Channeled spell %d is removed due to lack of targets", m_spellInfo->Id); - SendChannelUpdate(0); - finish(); + m_timer = 0; + + // Also remove applied auras + for (TargetInfo const& target : m_UniqueTargetInfo) + if (Unit* unit = m_caster->GetGUID() == target.targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target.targetGUID)) + unit->RemoveOwnedAura(m_spellInfo->Id, m_originalCasterGUID, 0, AURA_REMOVE_BY_CANCEL); } if (m_timer > 0) @@ -3692,7 +3719,7 @@ void Spell::finish(bool ok) m_caster->AttackStop(); } -void Spell::WriteCastResultInfo(WorldPacket& data, Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError) +void Spell::WriteCastResultInfo(WorldPacket& data, Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) { data << uint8(castCount); // single cast or multi 2.3 (0/1) data << uint32(spellInfo->Id); @@ -3700,115 +3727,185 @@ void Spell::WriteCastResultInfo(WorldPacket& data, Player* caster, SpellInfo con switch (result) { case SPELL_FAILED_REQUIRES_SPELL_FOCUS: - data << uint32(spellInfo->RequiresSpellFocus); // SpellFocusObject.dbc id + if (param1) + data << uint32(*param1); + else + data << uint32(spellInfo->RequiresSpellFocus); // SpellFocusObject.dbc id break; case SPELL_FAILED_REQUIRES_AREA: // AreaTable.dbc id - // hardcode areas limitation case - switch (spellInfo->Id) + if (param1) + data << uint32(*param1); + else { - case 41617: // Cenarion Mana Salve - case 41619: // Cenarion Healing Salve - data << uint32(3905); - break; - case 41618: // Bottled Nethergon Energy - case 41620: // Bottled Nethergon Vapor - data << uint32(3842); - break; - case 45373: // Bloodberry Elixir - data << uint32(4075); - break; - default: // default case (don't must be) - data << uint32(0); - break; + // hardcode areas limitation case + switch (spellInfo->Id) + { + case 41617: // Cenarion Mana Salve + case 41619: // Cenarion Healing Salve + data << uint32(3905); + break; + case 41618: // Bottled Nethergon Energy + case 41620: // Bottled Nethergon Vapor + data << uint32(3842); + break; + case 45373: // Bloodberry Elixir + data << uint32(4075); + break; + default: // default case (don't must be) + data << uint32(0); + break; + } } break; case SPELL_FAILED_TOTEMS: - if (spellInfo->Totem[0]) - data << uint32(spellInfo->Totem[0]); - if (spellInfo->Totem[1]) - data << uint32(spellInfo->Totem[1]); + if (param1) + { + data << uint32(*param1); + if (param2) + data << uint32(*param2); + } + else + { + if (spellInfo->Totem[0]) + data << uint32(spellInfo->Totem[0]); + if (spellInfo->Totem[1]) + data << uint32(spellInfo->Totem[1]); + } break; case SPELL_FAILED_TOTEM_CATEGORY: - if (spellInfo->TotemCategory[0]) - data << uint32(spellInfo->TotemCategory[0]); - if (spellInfo->TotemCategory[1]) - data << uint32(spellInfo->TotemCategory[1]); + if (param1) + { + data << uint32(*param1); + if (param2) + data << uint32(*param2); + } + else + { + if (spellInfo->TotemCategory[0]) + data << uint32(spellInfo->TotemCategory[0]); + if (spellInfo->TotemCategory[1]) + data << uint32(spellInfo->TotemCategory[1]); + } break; case SPELL_FAILED_EQUIPPED_ITEM_CLASS: case SPELL_FAILED_EQUIPPED_ITEM_CLASS_MAINHAND: case SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND: - data << uint32(spellInfo->EquippedItemClass); - data << uint32(spellInfo->EquippedItemSubClassMask); + if (param1 && param2) + { + data << uint32(*param1); + data << uint32(*param2); + } + else + { + data << uint32(spellInfo->EquippedItemClass); + data << uint32(spellInfo->EquippedItemSubClassMask); + } break; case SPELL_FAILED_TOO_MANY_OF_ITEM: { - uint32 item = 0; - for (int8 eff = 0; eff < MAX_SPELL_EFFECTS; eff++) - if (spellInfo->Effects[eff].ItemType) - item = spellInfo->Effects[eff].ItemType; - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item); - if (proto && proto->ItemLimitCategory) - data << uint32(proto->ItemLimitCategory); - break; + if (param1) + data << uint32(*param1); + else + { + uint32 item = 0; + for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS && !item; ++effIndex) + if (uint32 itemType = spellInfo->Effects[effIndex].ItemType) + item = itemType; + + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item); + if (proto && proto->ItemLimitCategory) + data << uint32(proto->ItemLimitCategory); + } + break; } case SPELL_FAILED_CUSTOM_ERROR: data << uint32(customError); break; case SPELL_FAILED_REAGENTS: { - uint32 missingItem = 0; - for (uint32 i = 0; i < MAX_SPELL_REAGENTS; i++) + if (param1) + data << uint32(*param1); + else { - if (spellInfo->Reagent[i] <= 0) - continue; + uint32 missingItem = 0; + for (uint32 i = 0; i < MAX_SPELL_REAGENTS; i++) + { + if (spellInfo->Reagent[i] <= 0) + continue; - uint32 itemid = spellInfo->Reagent[i]; - uint32 itemcount = spellInfo->ReagentCount[i]; + uint32 itemid = spellInfo->Reagent[i]; + uint32 itemcount = spellInfo->ReagentCount[i]; - if (!caster->HasItemCount(itemid, itemcount)) - { - missingItem = itemid; - break; + if (!caster->HasItemCount(itemid, itemcount)) + { + missingItem = itemid; + break; + } } - } - data << uint32(missingItem); // first missing item + data << uint32(missingItem); // first missing item + } break; } case SPELL_FAILED_PREVENTED_BY_MECHANIC: - data << uint32(spellInfo->Mechanic); + if (param1) + data << uint32(*param1); + else + data << uint32(spellInfo->Mechanic); break; case SPELL_FAILED_NEED_EXOTIC_AMMO: - data << uint32(spellInfo->EquippedItemSubClassMask); + if (param1) + data << uint32(*param1); + else + data << uint32(spellInfo->EquippedItemSubClassMask); break; case SPELL_FAILED_NEED_MORE_ITEMS: - data << uint32(0); // Item entry - data << uint32(0); // Count + if (param1 && param2) + { + data << uint32(*param1); + data << uint32(*param2); + } + else + { + data << uint32(0); // Item entry + data << uint32(0); // Count + } break; case SPELL_FAILED_MIN_SKILL: - data << uint32(0); // SkillLine.dbc Id - data << uint32(0); // Amount + if (param1 && param2) + { + data << uint32(*param1); + data << uint32(*param2); + } + else + { + data << uint32(0); // SkillLine.dbc Id + data << uint32(0); // Amount + } break; case SPELL_FAILED_FISHING_TOO_LOW: - data << uint32(0); // Skill level + if (param1) + data << uint32(*param1); + else + data << uint32(0); // Skill level break; default: break; } } -void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError /*= SPELL_CUSTOM_ERROR_NONE*/) +void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError /*= SPELL_CUSTOM_ERROR_NONE*/, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) { if (result == SPELL_CAST_OK) return; WorldPacket data(SMSG_CAST_FAILED, 1 + 4 + 1); - WriteCastResultInfo(data, caster, spellInfo, castCount, result, customError); + WriteCastResultInfo(data, caster, spellInfo, castCount, result, customError, param1, param2); - caster->GetSession()->SendPacket(&data); + caster->SendDirectMessage(&data); } -void Spell::SendCastResult(SpellCastResult result) +void Spell::SendCastResult(SpellCastResult result, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) const { if (result == SPELL_CAST_OK) return; @@ -3816,13 +3913,13 @@ void Spell::SendCastResult(SpellCastResult result) if (m_caster->GetTypeId() != TYPEID_PLAYER) return; - if (m_caster->ToPlayer()->GetSession()->PlayerLoading()) // don't send cast results at loading time + if (m_caster->ToPlayer()->IsLoading()) // don't send cast results at loading time return; if (_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) result = SPELL_FAILED_DONT_REPORT; - SendCastResult(m_caster->ToPlayer(), m_spellInfo, m_cast_count, result, m_customError); + SendCastResult(m_caster->ToPlayer(), m_spellInfo, m_cast_count, result, m_customError, param1, param2); } void Spell::SendPetCastResult(SpellCastResult result) @@ -4471,7 +4568,7 @@ void Spell::TakeAmmo() } } -SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) +SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) const { if (m_spellInfo->PowerType != POWER_RUNE || !runeCostID) return SPELL_CAST_OK; @@ -4496,7 +4593,7 @@ SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) { runeCost[i] = src->RuneCost[i]; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod<SPELLMOD_COST>(m_spellInfo->Id, runeCost[i], this); + modOwner->ApplySpellMod<SPELLMOD_COST>(m_spellInfo->Id, runeCost[i], const_cast<Spell*>(this)); } runeCost[RUNE_DEATH] = MAX_RUNES; // calculated later @@ -4616,7 +4713,7 @@ void Spell::TakeReagents() ItemTemplate const* castItemTemplate = m_CastItem ? m_CastItem->GetTemplate() : NULL; // do not take reagents for these item casts - if (castItemTemplate && castItemTemplate->Flags & ITEM_PROTO_FLAG_TRIGGERED_CAST) + if (castItemTemplate && castItemTemplate->Flags & ITEM_FLAG_NO_REAGENT_COST) return; Player* p_caster = m_caster->ToPlayer(); @@ -4695,7 +4792,7 @@ void Spell::HandleThreatSpells() continue; // positive spells distribute threat among all units that are in combat with target, like healing - if (m_spellInfo->_IsPositiveSpell()) + if (m_spellInfo->IsPositive()) target->getHostileRefManager().threatAssist(m_caster, threatToAdd, m_spellInfo); // for negative spells threat gets distributed among affected targets else @@ -4706,7 +4803,7 @@ void Spell::HandleThreatSpells() target->AddThreat(m_caster, threatToAdd, m_spellInfo->GetSchoolMask(), m_spellInfo); } } - TC_LOG_DEBUG("spells", "Spell %u, added an additional %f threat for %s %u target(s)", m_spellInfo->Id, threat, m_spellInfo->_IsPositiveSpell() ? "assisting" : "harming", uint32(m_UniqueTargetInfo.size())); + TC_LOG_DEBUG("spells", "Spell %u, added an additional %f threat for %s %u target(s)", m_spellInfo->Id, threat, m_spellInfo->IsPositive() ? "assisting" : "harming", uint32(m_UniqueTargetInfo.size())); } void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOTarget, uint32 i, SpellEffectHandleMode mode) @@ -4732,7 +4829,7 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOT } } -SpellCastResult Spell::CheckCast(bool strict) +SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) { // check death state if (!m_caster->IsAlive() && !m_spellInfo->IsPassive() && !(m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell))) @@ -4795,13 +4892,15 @@ SpellCastResult Spell::CheckCast(bool strict) bool checkForm = true; // Ignore form req aura Unit::AuraEffectList const& ignore = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT); - for (Unit::AuraEffectList::const_iterator i = ignore.begin(); i != ignore.end(); ++i) + for (AuraEffect const* aurEff : ignore) { - if (!(*i)->IsAffectedOnSpell(m_spellInfo)) + if (!aurEff->IsAffectedOnSpell(m_spellInfo)) continue; + checkForm = false; break; } + if (checkForm) { // Cannot be used in this stance/form @@ -4897,14 +4996,18 @@ SpellCastResult Spell::CheckCast(bool strict) if (!(m_spellInfo->IsPassive() && (!m_targets.GetUnitTarget() || m_targets.GetUnitTarget() == m_caster))) { // Check explicit target for m_originalCaster - todo: get rid of such workarounds - SpellCastResult castResult = m_spellInfo->CheckExplicitTarget(m_originalCaster ? m_originalCaster : m_caster, m_targets.GetObjectTarget(), m_targets.GetItemTarget()); + Unit* caster = m_caster; + if (m_originalCaster && m_caster->GetEntry() != WORLD_TRIGGER) // Do a simplified check for gameobject casts + caster = m_originalCaster; + + SpellCastResult castResult = m_spellInfo->CheckExplicitTarget(caster, m_targets.GetObjectTarget(), m_targets.GetItemTarget()); if (castResult != SPELL_CAST_OK) return castResult; } if (Unit* target = m_targets.GetUnitTarget()) { - SpellCastResult castResult = m_spellInfo->CheckTarget(m_caster, target, false); + SpellCastResult castResult = m_spellInfo->CheckTarget(m_caster, target, m_caster->GetEntry() == WORLD_TRIGGER); // skip stealth checks for GO casts if (castResult != SPELL_CAST_OK) return castResult; @@ -5005,7 +5108,7 @@ SpellCastResult Spell::CheckCast(bool strict) // always (except passive spells) check items (only player related checks) if (!m_spellInfo->IsPassive()) { - castResult = CheckItems(); + castResult = CheckItems(param1, param2); if (castResult != SPELL_CAST_OK) return castResult; } @@ -5025,7 +5128,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURAS)) { - castResult = CheckCasterAuras(); + castResult = CheckCasterAuras(param1); if (castResult != SPELL_CAST_OK) return castResult; } @@ -5039,6 +5142,7 @@ SpellCastResult Spell::CheckCast(bool strict) bool hasNonDispelEffect = false; uint32 dispelMask = 0; for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { if (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_DISPEL) { if (m_spellInfo->Effects[i].IsTargetingArea() || m_spellInfo->HasAttribute(SPELL_ATTR1_MELEE_COMBAT_START)) @@ -5054,6 +5158,7 @@ SpellCastResult Spell::CheckCast(bool strict) hasNonDispelEffect = true; break; } + } if (!hasNonDispelEffect && !hasDispellableAura && dispelMask && !IsTriggered()) { @@ -5168,7 +5273,7 @@ SpellCastResult Spell::CheckCast(bool strict) m_caster->RemoveMovementImpairingAuras(); } - if (m_caster->HasUnitState(UNIT_STATE_ROOT)) + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURAS) && m_caster->HasUnitState(UNIT_STATE_ROOT)) return SPELL_FAILED_ROOTED; if (GetSpellInfo()->NeedsExplicitUnitTarget()) @@ -5637,139 +5742,110 @@ SpellCastResult Spell::CheckPetCast(Unit* target) return CheckCast(true); } -SpellCastResult Spell::CheckCasterAuras() const +SpellCastResult Spell::CheckCasterAuras(uint32* param1) const { // spells totally immuned to caster auras (wsg flag drop, give marks etc) if (m_spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CASTER_AURAS)) return SPELL_CAST_OK; - uint8 school_immune = 0; - uint32 mechanic_immune = 0; - uint32 dispel_immune = 0; - - // Check if the spell grants school or mechanic immunity. - // We use bitmasks so the loop is done only once and not on every aura check below. - if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) + bool usableWhileStunned = m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_STUNNED); + bool usableWhileFeared = m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_FEARED); + bool usableWhileConfused = m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_CONFUSED); + if (m_spellInfo->HasAttribute(SPELL_ATTR7_USABLE_IN_STUN_FEAR_CONFUSION)) { - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_SCHOOL_IMMUNITY) - school_immune |= uint32(m_spellInfo->Effects[i].MiscValue); - else if (m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MECHANIC_IMMUNITY) - mechanic_immune |= 1 << uint32(m_spellInfo->Effects[i].MiscValue); - else if (m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_DISPEL_IMMUNITY) - dispel_immune |= SpellInfo::GetDispelMask(DispelType(m_spellInfo->Effects[i].MiscValue)); - } - // immune movement impairment and loss of control - if (m_spellInfo->Id == 42292 || m_spellInfo->Id == 59752 || m_spellInfo->Id == 19574 || m_spellInfo->Id == 53490) - mechanic_immune = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; + usableWhileStunned = true; + usableWhileFeared = true; + usableWhileConfused = true; } - bool usableInStun = m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_STUNNED); - // Glyph of Pain Suppression // there is no other way to handle it if (m_spellInfo->Id == 33206 && !m_caster->HasAura(63248)) - usableInStun = false; + usableWhileStunned = false; // Check whether the cast should be prevented by any state you might have. - SpellCastResult prevented_reason = SPELL_CAST_OK; - // Have to check if there is a stun aura. Otherwise will have problems with ghost aura apply while logging out - uint32 unitflag = m_caster->GetUInt32Value(UNIT_FIELD_FLAGS); // Get unit state - if (unitflag & UNIT_FLAG_STUNNED) - { - // spell is usable while stunned, check if caster has allowed stun auras, another stun types must prevent cast spell - if (usableInStun) - { - static uint32 const allowedStunMask = - 1 << MECHANIC_STUN - | 1 << MECHANIC_FREEZE - | 1 << MECHANIC_SAPPED - | 1 << MECHANIC_SLEEP; - - bool foundNotStun = false; - Unit::AuraEffectList const& stunAuras = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_STUN); - for (Unit::AuraEffectList::const_iterator i = stunAuras.begin(); i != stunAuras.end(); ++i) - { - uint32 mechanicMask = (*i)->GetSpellInfo()->GetAllEffectsMechanicMask(); - if (mechanicMask && !(mechanicMask & allowedStunMask)) - { - foundNotStun = true; - break; - } - } - if (foundNotStun) - prevented_reason = SPELL_FAILED_STUNNED; - } - else - prevented_reason = SPELL_FAILED_STUNNED; - } - else if (unitflag & UNIT_FLAG_CONFUSED && !m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_CONFUSED)) - prevented_reason = SPELL_FAILED_CONFUSED; - else if (unitflag & UNIT_FLAG_FLEEING && !m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_FEARED)) - prevented_reason = SPELL_FAILED_FLEEING; - else if (unitflag & UNIT_FLAG_SILENCED && m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE) - prevented_reason = SPELL_FAILED_SILENCED; - else if (unitflag & UNIT_FLAG_PACIFIED && m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY) - prevented_reason = SPELL_FAILED_PACIFIED; + SpellCastResult result = SPELL_CAST_OK; + + // Get unit state + uint32 const unitflag = m_caster->GetUInt32Value(UNIT_FIELD_FLAGS); + if (m_caster->GetCharmerGUID() && !CheckCasterNotImmunedCharmAuras(param1)) + result = SPELL_FAILED_CHARMED; + else if (unitflag & UNIT_FLAG_STUNNED && !usableWhileStunned && CheckCasterNotImmunedStunAuras(param1)) + result = SPELL_FAILED_STUNNED; + else if (unitflag & UNIT_FLAG_SILENCED && m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE && CheckCasterNotImmunedSilenceAuras(param1)) + result = SPELL_FAILED_SILENCED; + else if (unitflag & UNIT_FLAG_PACIFIED && m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY && CheckCasterNotImmunedPacifyAuras(param1)) + result = SPELL_FAILED_PACIFIED; + else if (unitflag & UNIT_FLAG_FLEEING && !usableWhileFeared && CheckCasterNotImmunedFearAuras(param1)) + result = SPELL_FAILED_FLEEING; + else if (unitflag & UNIT_FLAG_CONFUSED && !usableWhileConfused && CheckCasterNotImmunedDisorientAuras(param1)) + result = SPELL_FAILED_CONFUSED; // Attr must make flag drop spell totally immune from all effects - if (prevented_reason != SPELL_CAST_OK) + if (result != SPELL_CAST_OK) + return (param1 && *param1) ? SPELL_FAILED_PREVENTED_BY_MECHANIC : result; + + return SPELL_CAST_OK; +} + +// based on sub_00804430 from 12340 client +bool Spell::CheckCasterHasNotImmunedAuraType(AuraType auraType, uint32* param1) const +{ + // Checking auras is needed now, because you are prevented by some state but the spell grants immunity. + Unit::AuraEffectList const& auraEffects = m_caster->GetAuraEffectsByType(auraType); + if (auraEffects.empty()) + return false; + + for (AuraEffect const* aurEff : auraEffects) { - if (school_immune || mechanic_immune || dispel_immune) - { - //Checking auras is needed now, because you are prevented by some state but the spell grants immunity. - Unit::AuraApplicationMap const& auras = m_caster->GetAppliedAuras(); - for (Unit::AuraApplicationMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - Aura const* aura = itr->second->GetBase(); - SpellInfo const* auraInfo = aura->GetSpellInfo(); - if (auraInfo->GetAllEffectsMechanicMask() & mechanic_immune) - continue; - if (auraInfo->GetSchoolMask() & school_immune && !auraInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE)) - continue; - if (auraInfo->GetDispelMask() & dispel_immune) - continue; + SpellInfo const* auraInfo = aurEff->GetSpellInfo(); + if (m_spellInfo->CanSpellCastOverrideAuraEffect(auraInfo, aurEff->GetEffIndex())) + continue; - //Make a second check for spell failed so the right SPELL_FAILED message is returned. - //That is needed when your casting is prevented by multiple states and you are only immune to some of them. - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (AuraEffect* part = aura->GetEffect(i)) - { - switch (part->GetAuraType()) - { - case SPELL_AURA_MOD_STUN: - if (!usableInStun || !(auraInfo->GetAllEffectsMechanicMask() & (1<<MECHANIC_STUN))) - return SPELL_FAILED_STUNNED; - break; - case SPELL_AURA_MOD_CONFUSE: - if (!m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_CONFUSED)) - return SPELL_FAILED_CONFUSED; - break; - case SPELL_AURA_MOD_FEAR: - if (!m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_FEARED)) - return SPELL_FAILED_FLEEING; - break; - case SPELL_AURA_MOD_SILENCE: - case SPELL_AURA_MOD_PACIFY: - case SPELL_AURA_MOD_PACIFY_SILENCE: - if (m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY) - return SPELL_FAILED_PACIFIED; - else if (m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE) - return SPELL_FAILED_SILENCED; - break; - default: break; - } - } - } - } + if (param1) + { + *param1 = auraInfo->Effects[aurEff->GetEffIndex()].Mechanic; + if (!*param1) + *param1 = auraInfo->Mechanic; } - // You are prevented from casting and the spell cast does not grant immunity. Return a failed error. - else - return prevented_reason; + return true; } - return SPELL_CAST_OK; + + return false; +} + +bool Spell::CheckCasterNotImmunedCharmAuras(uint32* param1) const +{ + return CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_CHARM, param1) || + CheckCasterHasNotImmunedAuraType(SPELL_AURA_AOE_CHARM, param1) || + CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_POSSESS, param1); +} + +bool Spell::CheckCasterNotImmunedStunAuras(uint32* param1) const +{ + return CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_STUN, param1); +} + +bool Spell::CheckCasterNotImmunedSilenceAuras(uint32* param1) const +{ + return CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_SILENCE, param1) || + CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_PACIFY_SILENCE, param1); +} + +bool Spell::CheckCasterNotImmunedPacifyAuras(uint32* param1) const +{ + return CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_PACIFY, param1) || + CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_PACIFY_SILENCE, param1); +} + +bool Spell::CheckCasterNotImmunedFearAuras(uint32* param1) const +{ + return CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_FEAR, param1); +} + +bool Spell::CheckCasterNotImmunedDisorientAuras(uint32* param1) const +{ + return CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_CONFUSE, param1); } bool Spell::CanAutoCast(Unit* target) @@ -5828,7 +5904,7 @@ bool Spell::CanAutoCast(Unit* target) return false; } -SpellCastResult Spell::CheckRange(bool strict) +SpellCastResult Spell::CheckRange(bool strict) const { // Don't check for instant cast spells if (!strict && m_casttime == 0) @@ -5858,20 +5934,20 @@ SpellCastResult Spell::CheckRange(bool strict) if (m_targets.HasDst() && !m_targets.HasTraj()) { if (m_caster->GetExactDistSq(m_targets.GetDstPos()) > maxRange) - return !(_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_OUT_OF_RANGE : SPELL_FAILED_DONT_REPORT; + return SPELL_FAILED_OUT_OF_RANGE; if (minRange > 0.0f && m_caster->GetExactDistSq(m_targets.GetDstPos()) < minRange) - return !(_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_OUT_OF_RANGE : SPELL_FAILED_DONT_REPORT; + return SPELL_FAILED_OUT_OF_RANGE; } return SPELL_CAST_OK; } -std::pair<float, float> Spell::GetMinMaxRange(bool strict) +std::pair<float, float> Spell::GetMinMaxRange(bool strict) const { float rangeMod = 0.0f; float minRange = 0.0f; float maxRange = 0.0f; - if (strict && IsNextMeleeSwingSpell()) + if (strict && m_spellInfo->IsNextMeleeSwingSpell()) { maxRange = 100.0f; return std::pair<float, float>(minRange, maxRange); @@ -5913,14 +5989,14 @@ std::pair<float, float> Spell::GetMinMaxRange(bool strict) maxRange *= ranged->GetTemplate()->RangedModRange * 0.01f; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod<SPELLMOD_RANGE>(m_spellInfo->Id, maxRange, this); + modOwner->ApplySpellMod<SPELLMOD_RANGE>(m_spellInfo->Id, maxRange, const_cast<Spell*>(this)); maxRange += rangeMod; return std::pair<float, float>(minRange, maxRange); } -SpellCastResult Spell::CheckPower() +SpellCastResult Spell::CheckPower() const { // item cast not used power if (m_CastItem) @@ -5956,12 +6032,15 @@ SpellCastResult Spell::CheckPower() return SPELL_CAST_OK; } -SpellCastResult Spell::CheckItems() +SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) const { Player* player = m_caster->ToPlayer(); if (!player) return SPELL_CAST_OK; + if (m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_ITEM_CHECK)) + return SPELL_CAST_OK; + if (!m_CastItem) { if (m_castItemGUID) @@ -6037,10 +6116,11 @@ SpellCastResult Spell::CheckItems() // check target item if (m_targets.GetItemTargetGUID()) { - if (!m_targets.GetItemTarget()) + Item* item = m_targets.GetItemTarget(); + if (!item) return SPELL_FAILED_ITEM_GONE; - if (!m_targets.GetItemTarget()->IsFitToSpellRequirements(m_spellInfo)) + if (!item->IsFitToSpellRequirements(m_spellInfo)) return SPELL_FAILED_EQUIPPED_ITEM_CLASS; } // if not item target then required item must be equipped @@ -6052,7 +6132,7 @@ SpellCastResult Spell::CheckItems() } // do not take reagents for these item casts - if (!(m_CastItem && m_CastItem->GetTemplate()->Flags & ITEM_PROTO_FLAG_TRIGGERED_CAST)) + if (!(m_CastItem && m_CastItem->GetTemplate()->Flags & ITEM_FLAG_NO_REAGENT_COST)) { bool checkReagents = !(_triggeredCastFlags & TRIGGERED_IGNORE_POWER_AND_REAGENT_COST) && !player->CanNoReagentCast(m_spellInfo); // Not own traded item (in trader trade slot) requires reagents even if triggered spell @@ -6090,7 +6170,11 @@ SpellCastResult Spell::CheckItems() } } if (!player->HasItemCount(itemid, itemcount)) + { + if (param1) + *param1 = itemid; return SPELL_FAILED_REAGENTS; + } } } @@ -6174,7 +6258,7 @@ SpellCastResult Spell::CheckItems() if (m_targets.GetItemTarget()->GetOwner() != m_caster) return SPELL_FAILED_NOT_TRADEABLE; // do not allow to enchant vellum from scroll made by vellum-prevent exploit - if (m_CastItem && m_CastItem->GetTemplate()->Flags & ITEM_PROTO_FLAG_TRIGGERED_CAST) + if (m_CastItem && m_CastItem->GetTemplate()->Flags & ITEM_FLAG_NO_REAGENT_COST) return SPELL_FAILED_TOTEM_CATEGORY; ItemPosCountVec dest; InventoryResult msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, m_spellInfo->Effects[i].ItemType, 1); @@ -6294,21 +6378,29 @@ SpellCastResult Spell::CheckItems() } case SPELL_EFFECT_PROSPECTING: { - if (!m_targets.GetItemTarget()) + Item* item = m_targets.GetItemTarget(); + if (!item) return SPELL_FAILED_CANT_BE_PROSPECTED; //ensure item is a prospectable ore - if (!(m_targets.GetItemTarget()->GetTemplate()->Flags & ITEM_PROTO_FLAG_PROSPECTABLE)) + if (!(item->GetTemplate()->Flags & ITEM_FLAG_IS_PROSPECTABLE)) return SPELL_FAILED_CANT_BE_PROSPECTED; //prevent prospecting in trade slot - if (m_targets.GetItemTarget()->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != m_caster->GetGUID()) return SPELL_FAILED_CANT_BE_PROSPECTED; //Check for enough skill in jewelcrafting - uint32 item_prospectingskilllevel = m_targets.GetItemTarget()->GetTemplate()->RequiredSkillRank; - if (item_prospectingskilllevel >player->GetSkillValue(SKILL_JEWELCRAFTING)) + uint32 item_prospectingskilllevel = item->GetTemplate()->RequiredSkillRank; + if (item_prospectingskilllevel > player->GetSkillValue(SKILL_JEWELCRAFTING)) return SPELL_FAILED_LOW_CASTLEVEL; //make sure the player has the required ores in inventory - if (m_targets.GetItemTarget()->GetCount() < 5) + if (item->GetCount() < 5) + { + if (param1 && param2) + { + *param1 = item->GetEntry(); + *param2 = 5; + } return SPELL_FAILED_NEED_MORE_ITEMS; + } if (!LootTemplates_Prospecting.HaveLootFor(m_targets.GetItemTargetEntry())) return SPELL_FAILED_CANT_BE_PROSPECTED; @@ -6317,21 +6409,29 @@ SpellCastResult Spell::CheckItems() } case SPELL_EFFECT_MILLING: { - if (!m_targets.GetItemTarget()) + Item* item = m_targets.GetItemTarget(); + if (!item) return SPELL_FAILED_CANT_BE_MILLED; //ensure item is a millable herb - if (!(m_targets.GetItemTarget()->GetTemplate()->Flags & ITEM_PROTO_FLAG_MILLABLE)) + if (!(item->GetTemplate()->Flags & ITEM_FLAG_IS_MILLABLE)) return SPELL_FAILED_CANT_BE_MILLED; //prevent milling in trade slot - if (m_targets.GetItemTarget()->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != m_caster->GetGUID()) return SPELL_FAILED_CANT_BE_MILLED; //Check for enough skill in inscription - uint32 item_millingskilllevel = m_targets.GetItemTarget()->GetTemplate()->RequiredSkillRank; + uint32 item_millingskilllevel = item->GetTemplate()->RequiredSkillRank; if (item_millingskilllevel > player->GetSkillValue(SKILL_INSCRIPTION)) return SPELL_FAILED_LOW_CASTLEVEL; //make sure the player has the required herbs in inventory - if (m_targets.GetItemTarget()->GetCount() < 5) + if (item->GetCount() < 5) + { + if (param1 && param2) + { + *param1 = item->GetEntry(); + *param2 = 5; + } return SPELL_FAILED_NEED_MORE_ITEMS; + } if (!LootTemplates_Milling.HaveLootFor(m_targets.GetItemTargetEntry())) return SPELL_FAILED_CANT_BE_MILLED; @@ -6603,14 +6703,14 @@ bool Spell::UpdatePointers() CurrentSpellTypes Spell::GetCurrentContainer() const { - if (IsNextMeleeSwingSpell()) - return(CURRENT_MELEE_SPELL); + if (m_spellInfo->IsNextMeleeSwingSpell()) + return CURRENT_MELEE_SPELL; else if (IsAutoRepeat()) - return(CURRENT_AUTOREPEAT_SPELL); + return CURRENT_AUTOREPEAT_SPELL; else if (m_spellInfo->IsChanneled()) - return(CURRENT_CHANNELED_SPELL); - else - return(CURRENT_GENERIC_SPELL); + return CURRENT_CHANNELED_SPELL; + + return CURRENT_GENERIC_SPELL; } bool Spell::CheckEffectTarget(Unit const* target, uint32 eff, Position const* losPosition) const @@ -6689,11 +6789,6 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff, Position const* lo return true; } -bool Spell::IsNextMeleeSwingSpell() const -{ - return m_spellInfo->HasAttribute(SPELL_ATTR0_ON_NEXT_SWING); -} - bool Spell::IsAutoActionResetSpell() const { /// @todo changed SPELL_INTERRUPT_FLAG_AUTOATTACK -> SPELL_INTERRUPT_FLAG_INTERRUPT to fix compile - is this check correct at all? @@ -7071,7 +7166,7 @@ void Spell::SetSpellValue(SpellValueMod mod, int32 value) void Spell::PrepareTargetProcessing() { - CheckEffectExecuteData(); + AssertEffectExecuteData(); } void Spell::FinishTargetProcessing() @@ -7096,7 +7191,7 @@ void Spell::InitEffectExecuteData(uint8 effIndex) } } -void Spell::CheckEffectExecuteData() +void Spell::AssertEffectExecuteData() const { for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) ASSERT(!m_effectExecuteData[i]); @@ -7104,29 +7199,20 @@ void Spell::CheckEffectExecuteData() void Spell::LoadScripts() { - sScriptMgr->CreateSpellScripts(m_spellInfo->Id, m_loadedScripts); - for (std::list<SpellScript*>::iterator itr = m_loadedScripts.begin(); itr != m_loadedScripts.end();) + sScriptMgr->CreateSpellScripts(m_spellInfo->Id, m_loadedScripts, this); + for (auto itr = m_loadedScripts.begin(); itr != m_loadedScripts.end(); ++itr) { - if (!(*itr)->_Load(this)) - { - std::list<SpellScript*>::iterator bitr = itr; - ++itr; - delete (*bitr); - m_loadedScripts.erase(bitr); - continue; - } TC_LOG_DEBUG("spells", "Spell::LoadScripts: Script `%s` for spell `%u` is loaded now", (*itr)->_GetScriptName()->c_str(), m_spellInfo->Id); (*itr)->Register(); - ++itr; } } void Spell::CallScriptBeforeCastHandlers() { - for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_BEFORE_CAST); - std::list<SpellScript::CastHandler>::iterator hookItrEnd = (*scritr)->BeforeCast.end(), hookItr = (*scritr)->BeforeCast.begin(); + auto hookItrEnd = (*scritr)->BeforeCast.end(), hookItr = (*scritr)->BeforeCast.begin(); for (; hookItr != hookItrEnd; ++hookItr) (*hookItr).Call(*scritr); @@ -7136,10 +7222,10 @@ void Spell::CallScriptBeforeCastHandlers() void Spell::CallScriptOnCastHandlers() { - for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_ON_CAST); - std::list<SpellScript::CastHandler>::iterator hookItrEnd = (*scritr)->OnCast.end(), hookItr = (*scritr)->OnCast.begin(); + auto hookItrEnd = (*scritr)->OnCast.end(), hookItr = (*scritr)->OnCast.begin(); for (; hookItr != hookItrEnd; ++hookItr) (*hookItr).Call(*scritr); @@ -7149,10 +7235,10 @@ void Spell::CallScriptOnCastHandlers() void Spell::CallScriptAfterCastHandlers() { - for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_AFTER_CAST); - std::list<SpellScript::CastHandler>::iterator hookItrEnd = (*scritr)->AfterCast.end(), hookItr = (*scritr)->AfterCast.begin(); + auto hookItrEnd = (*scritr)->AfterCast.end(), hookItr = (*scritr)->AfterCast.begin(); for (; hookItr != hookItrEnd; ++hookItr) (*hookItr).Call(*scritr); @@ -7163,10 +7249,10 @@ void Spell::CallScriptAfterCastHandlers() SpellCastResult Spell::CallScriptCheckCastHandlers() { SpellCastResult retVal = SPELL_CAST_OK; - for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_CHECK_CAST); - std::list<SpellScript::CheckCastHandler>::iterator hookItrEnd = (*scritr)->OnCheckCast.end(), hookItr = (*scritr)->OnCheckCast.begin(); + auto hookItrEnd = (*scritr)->OnCheckCast.end(), hookItr = (*scritr)->OnCheckCast.begin(); for (; hookItr != hookItrEnd; ++hookItr) { SpellCastResult tempResult = (*hookItr).Call(*scritr); @@ -7181,7 +7267,7 @@ SpellCastResult Spell::CallScriptCheckCastHandlers() void Spell::PrepareScriptHitHandlers() { - for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) (*scritr)->_InitHit(); } @@ -7189,9 +7275,9 @@ bool Spell::CallScriptEffectHandlers(SpellEffIndex effIndex, SpellEffectHandleMo { // execute script effect handler hooks and check if effects was prevented bool preventDefault = false; - for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { - std::list<SpellScript::EffectHandler>::iterator effItr, effEndItr; + HookList<SpellScript::EffectHandler>::iterator effItr, effEndItr; SpellScriptHookType hookType; switch (mode) { @@ -7235,10 +7321,10 @@ bool Spell::CallScriptEffectHandlers(SpellEffIndex effIndex, SpellEffectHandleMo void Spell::CallScriptSuccessfulDispel(SpellEffIndex effIndex) { - for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_EFFECT_SUCCESSFUL_DISPEL); - std::list<SpellScript::EffectHandler>::iterator hookItrEnd = (*scritr)->OnEffectSuccessfulDispel.end(), hookItr = (*scritr)->OnEffectSuccessfulDispel.begin(); + auto hookItrEnd = (*scritr)->OnEffectSuccessfulDispel.end(), hookItr = (*scritr)->OnEffectSuccessfulDispel.begin(); for (; hookItr != hookItrEnd; ++hookItr) hookItr->Call(*scritr, effIndex); @@ -7248,10 +7334,10 @@ void Spell::CallScriptSuccessfulDispel(SpellEffIndex effIndex) void Spell::CallScriptBeforeHitHandlers() { - for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_BEFORE_HIT); - std::list<SpellScript::HitHandler>::iterator hookItrEnd = (*scritr)->BeforeHit.end(), hookItr = (*scritr)->BeforeHit.begin(); + auto hookItrEnd = (*scritr)->BeforeHit.end(), hookItr = (*scritr)->BeforeHit.begin(); for (; hookItr != hookItrEnd; ++hookItr) (*hookItr).Call(*scritr); @@ -7261,10 +7347,10 @@ void Spell::CallScriptBeforeHitHandlers() void Spell::CallScriptOnHitHandlers() { - for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_HIT); - std::list<SpellScript::HitHandler>::iterator hookItrEnd = (*scritr)->OnHit.end(), hookItr = (*scritr)->OnHit.begin(); + auto hookItrEnd = (*scritr)->OnHit.end(), hookItr = (*scritr)->OnHit.begin(); for (; hookItr != hookItrEnd; ++hookItr) (*hookItr).Call(*scritr); @@ -7274,10 +7360,10 @@ void Spell::CallScriptOnHitHandlers() void Spell::CallScriptAfterHitHandlers() { - for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_AFTER_HIT); - std::list<SpellScript::HitHandler>::iterator hookItrEnd = (*scritr)->AfterHit.end(), hookItr = (*scritr)->AfterHit.begin(); + auto hookItrEnd = (*scritr)->AfterHit.end(), hookItr = (*scritr)->AfterHit.begin(); for (; hookItr != hookItrEnd; ++hookItr) (*hookItr).Call(*scritr); @@ -7287,10 +7373,10 @@ void Spell::CallScriptAfterHitHandlers() void Spell::CallScriptObjectAreaTargetSelectHandlers(std::list<WorldObject*>& targets, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType) { - for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_OBJECT_AREA_TARGET_SELECT); - std::list<SpellScript::ObjectAreaTargetSelectHandler>::iterator hookItrEnd = (*scritr)->OnObjectAreaTargetSelect.end(), hookItr = (*scritr)->OnObjectAreaTargetSelect.begin(); + auto hookItrEnd = (*scritr)->OnObjectAreaTargetSelect.end(), hookItr = (*scritr)->OnObjectAreaTargetSelect.begin(); for (; hookItr != hookItrEnd; ++hookItr) if (hookItr->IsEffectAffected(m_spellInfo, effIndex) && targetType.GetTarget() == hookItr->GetTarget()) hookItr->Call(*scritr, targets); @@ -7301,10 +7387,10 @@ void Spell::CallScriptObjectAreaTargetSelectHandlers(std::list<WorldObject*>& ta void Spell::CallScriptObjectTargetSelectHandlers(WorldObject*& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType) { - for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_OBJECT_TARGET_SELECT); - std::list<SpellScript::ObjectTargetSelectHandler>::iterator hookItrEnd = (*scritr)->OnObjectTargetSelect.end(), hookItr = (*scritr)->OnObjectTargetSelect.begin(); + auto hookItrEnd = (*scritr)->OnObjectTargetSelect.end(), hookItr = (*scritr)->OnObjectTargetSelect.begin(); for (; hookItr != hookItrEnd; ++hookItr) if (hookItr->IsEffectAffected(m_spellInfo, effIndex) && targetType.GetTarget() == hookItr->GetTarget()) hookItr->Call(*scritr, target); @@ -7315,10 +7401,10 @@ void Spell::CallScriptObjectTargetSelectHandlers(WorldObject*& target, SpellEffI void Spell::CallScriptDestinationTargetSelectHandlers(SpellDestination& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType) { - for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_DESTINATION_TARGET_SELECT); - std::list<SpellScript::DestinationTargetSelectHandler>::iterator hookItrEnd = (*scritr)->OnDestinationTargetSelect.end(), hookItr = (*scritr)->OnDestinationTargetSelect.begin(); + auto hookItrEnd = (*scritr)->OnDestinationTargetSelect.end(), hookItr = (*scritr)->OnDestinationTargetSelect.begin(); for (; hookItr != hookItrEnd; ++hookItr) if (hookItr->IsEffectAffected(m_spellInfo, effIndex) && targetType.GetTarget() == hookItr->GetTarget()) hookItr->Call(*scritr, target); @@ -7333,15 +7419,15 @@ bool Spell::CheckScriptEffectImplicitTargets(uint32 effIndex, uint32 effIndexToC if (m_loadedScripts.empty()) return true; - for (std::list<SpellScript*>::iterator itr = m_loadedScripts.begin(); itr != m_loadedScripts.end(); ++itr) + for (auto itr = m_loadedScripts.begin(); itr != m_loadedScripts.end(); ++itr) { - std::list<SpellScript::ObjectTargetSelectHandler>::iterator targetSelectHookEnd = (*itr)->OnObjectTargetSelect.end(), targetSelectHookItr = (*itr)->OnObjectTargetSelect.begin(); + auto targetSelectHookEnd = (*itr)->OnObjectTargetSelect.end(), targetSelectHookItr = (*itr)->OnObjectTargetSelect.begin(); for (; targetSelectHookItr != targetSelectHookEnd; ++targetSelectHookItr) if (((*targetSelectHookItr).IsEffectAffected(m_spellInfo, effIndex) && !(*targetSelectHookItr).IsEffectAffected(m_spellInfo, effIndexToCheck)) || (!(*targetSelectHookItr).IsEffectAffected(m_spellInfo, effIndex) && (*targetSelectHookItr).IsEffectAffected(m_spellInfo, effIndexToCheck))) return false; - std::list<SpellScript::ObjectAreaTargetSelectHandler>::iterator areaTargetSelectHookEnd = (*itr)->OnObjectAreaTargetSelect.end(), areaTargetSelectHookItr = (*itr)->OnObjectAreaTargetSelect.begin(); + auto areaTargetSelectHookEnd = (*itr)->OnObjectAreaTargetSelect.end(), areaTargetSelectHookItr = (*itr)->OnObjectAreaTargetSelect.begin(); for (; areaTargetSelectHookItr != areaTargetSelectHookEnd; ++areaTargetSelectHookItr) if (((*areaTargetSelectHookItr).IsEffectAffected(m_spellInfo, effIndex) && !(*areaTargetSelectHookItr).IsEffectAffected(m_spellInfo, effIndexToCheck)) || (!(*areaTargetSelectHookItr).IsEffectAffected(m_spellInfo, effIndex) && (*areaTargetSelectHookItr).IsEffectAffected(m_spellInfo, effIndexToCheck))) diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 5823ea72dbd..881e95fc9a4 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -397,7 +397,7 @@ class TC_GAME_API Spell void TakeReagents(); void TakeCastItem(); - SpellCastResult CheckCast(bool strict); + SpellCastResult CheckCast(bool strict, uint32* param1 = nullptr, uint32* param2 = nullptr); SpellCastResult CheckPetCast(Unit* target); // handlers @@ -407,11 +407,19 @@ class TC_GAME_API Spell void _handle_immediate_phase(); void _handle_finish_phase(); - SpellCastResult CheckItems(); - SpellCastResult CheckRange(bool strict); - SpellCastResult CheckPower(); - SpellCastResult CheckRuneCost(uint32 runeCostID); - SpellCastResult CheckCasterAuras() const; + SpellCastResult CheckItems(uint32* param1, uint32* param2) const; + SpellCastResult CheckRange(bool strict) const; + SpellCastResult CheckPower() const; + SpellCastResult CheckRuneCost(uint32 runeCostID) const; + SpellCastResult CheckCasterAuras(uint32* param1) const; + + bool CheckCasterHasNotImmunedAuraType(AuraType auraType, uint32* param1) const; + bool CheckCasterNotImmunedCharmAuras(uint32* param1) const; + bool CheckCasterNotImmunedStunAuras(uint32* param1) const; + bool CheckCasterNotImmunedSilenceAuras(uint32* param1) const; + bool CheckCasterNotImmunedPacifyAuras(uint32* param1) const; + bool CheckCasterNotImmunedFearAuras(uint32* param1) const; + bool CheckCasterNotImmunedDisorientAuras(uint32* param1) const; int32 CalculateDamage(uint8 i, Unit const* target) const { return m_caster->CalculateSpellDamage(target, m_spellInfo, i, &m_spellValue->EffectBasePoints[i]); } @@ -430,9 +438,9 @@ class TC_GAME_API Spell void CheckSrc() { if (!m_targets.HasSrc()) m_targets.SetSrc(*m_caster); } void CheckDst() { if (!m_targets.HasDst()) m_targets.SetDst(*m_caster); } - static void WriteCastResultInfo(WorldPacket& data, Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError); - static void SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError = SPELL_CUSTOM_ERROR_NONE); - void SendCastResult(SpellCastResult result); + static void WriteCastResultInfo(WorldPacket& data, Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError, uint32* param1 = nullptr, uint32* param2 = nullptr); + static void SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError = SPELL_CUSTOM_ERROR_NONE, uint32* param1 = nullptr, uint32* param2 = nullptr); + void SendCastResult(SpellCastResult result, uint32* param1 = nullptr, uint32* param2 = nullptr) const; void SendPetCastResult(SpellCastResult result); void SendSpellStart(); void SendSpellGo(); @@ -473,9 +481,9 @@ class TC_GAME_API Spell bool IsAutoRepeat() const { return m_autoRepeat; } void SetAutoRepeat(bool rep) { m_autoRepeat = rep; } void ReSetTimer() { m_timer = m_casttime > 0 ? m_casttime : 0; } - bool IsNextMeleeSwingSpell() const; bool IsTriggered() const { return (_triggeredCastFlags & TRIGGERED_FULL_MASK) != 0; } bool IsIgnoringCooldowns() const { return (_triggeredCastFlags & TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD) != 0; } + bool IsProcDisabled() const { return (_triggeredCastFlags & TRIGGERED_DISALLOW_PROC_EVENTS) != 0; } bool IsChannelActive() const { return m_caster->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0; } bool IsAutoActionResetSpell() const; @@ -507,7 +515,7 @@ class TC_GAME_API Spell void CancelGlobalCooldown(); void SendLoot(ObjectGuid guid, LootType loottype); - std::pair<float, float> GetMinMaxRange(bool strict); + std::pair<float, float> GetMinMaxRange(bool strict) const; Unit* const m_caster; @@ -561,10 +569,6 @@ class TC_GAME_API Spell // used in effects handlers Aura* m_spellAura; - // this is set in Spell Hit, but used in Apply Aura handler - DiminishingLevels m_diminishLevel; - DiminishingGroup m_diminishGroup; - // ------------------------------------------- GameObject* focusObject; @@ -640,7 +644,7 @@ class TC_GAME_API Spell // spell execution log void InitEffectExecuteData(uint8 effIndex); - void CheckEffectExecuteData(); + void AssertEffectExecuteData() const; // Scripting system void LoadScripts(); @@ -658,7 +662,7 @@ class TC_GAME_API Spell void CallScriptObjectTargetSelectHandlers(WorldObject*& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType); void CallScriptDestinationTargetSelectHandlers(SpellDestination& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType); bool CheckScriptEffectImplicitTargets(uint32 effIndex, uint32 effIndexToCheck); - std::list<SpellScript*> m_loadedScripts; + std::vector<SpellScript*> m_loadedScripts; struct HitTriggerSpell { diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 1b570e7778c..c570d5fcf21 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -292,14 +292,18 @@ void Spell::EffectEnvironmentalDMG(SpellEffIndex /*effIndex*/) if (!unitTarget || !unitTarget->IsAlive()) return; - uint32 absorb = 0; - uint32 resist = 0; - - m_caster->CalcAbsorbResist(unitTarget, m_spellInfo->GetSchoolMask(), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist, m_spellInfo); - - m_caster->SendSpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_spellInfo->GetSchoolMask(), absorb, resist, false, 0, false); + // CalcAbsorbResist already in Player::EnvironmentalDamage if (unitTarget->GetTypeId() == TYPEID_PLAYER) unitTarget->ToPlayer()->EnvironmentalDamage(DAMAGE_FIRE, damage); + else + { + DamageInfo damageInfo(m_caster, unitTarget, damage, m_spellInfo, m_spellInfo->GetSchoolMask(), SPELL_DIRECT_DAMAGE, BASE_ATTACK); + m_caster->CalcAbsorbResist(damageInfo); + + uint32 absorb = damageInfo.GetAbsorb(); + uint32 resist = damageInfo.GetResist(); + m_caster->SendSpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_spellInfo->GetSchoolMask(), absorb, resist, false, 0, false); + } } void Spell::EffectSchoolDMG(SpellEffIndex effIndex) @@ -2023,7 +2027,7 @@ void Spell::EffectOpenLock(SpellEffIndex effIndex) if (gameObjTarget) SendLoot(guid, LOOT_SKINNING); else if (itemTarget) - itemTarget->SetFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_UNLOCKED); + itemTarget->SetFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_UNLOCKED); // not allow use skill grow at item base open if (!m_CastItem && skillId != SKILL_NONE) @@ -2686,7 +2690,7 @@ void Spell::EffectEnchantItemPerm(SpellEffIndex effIndex) else { // do not increase skill if vellum used - if (!(m_CastItem && m_CastItem->GetTemplate()->Flags & ITEM_PROTO_FLAG_TRIGGERED_CAST)) + if (!(m_CastItem && m_CastItem->GetTemplate()->Flags & ITEM_FLAG_NO_REAGENT_COST)) player->UpdateCraftSkill(m_spellInfo->Id); uint32 enchant_id = m_spellInfo->Effects[effIndex].MiscValue; @@ -3531,21 +3535,12 @@ void Spell::EffectSummonObjectWild(SpellEffIndex effIndex) if (Battleground* bg = player->GetBattleground()) bg->SetDroppedFlagGUID(pGameObj->GetGUID(), player->GetTeam() == ALLIANCE ? TEAM_HORDE: TEAM_ALLIANCE); - if (uint32 linkedEntry = pGameObj->GetGOInfo()->GetLinkedGameObjectEntry()) + if (GameObject* linkedTrap = pGameObj->GetLinkedTrap()) { - GameObject* linkedGO = new GameObject(); - if (linkedGO->Create(map->GenerateLowGuid<HighGuid::GameObject>(), linkedEntry, map, m_caster->GetPhaseMask(), Position(x, y, z, target->GetOrientation()), rot, 255, GO_STATE_READY)) - { - linkedGO->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0); - linkedGO->SetSpellId(m_spellInfo->Id); + linkedTrap->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0); + linkedTrap->SetSpellId(m_spellInfo->Id); - ExecuteLogEffectSummonObject(effIndex, linkedGO); - - // Wild object not have owner and check clickable by players - map->AddToMap(linkedGO); - } - else - delete linkedGO; + ExecuteLogEffectSummonObject(effIndex, linkedTrap); } } @@ -4579,8 +4574,8 @@ void Spell::EffectQuestComplete(SpellEffIndex effIndex) uint16 logSlot = player->FindQuestSlot(questId); if (logSlot < MAX_QUEST_LOG_SIZE) player->AreaExploredOrEventHappens(questId); - else if (player->CanTakeQuest(quest, false)) // Check if the quest has already been turned in. - player->SetRewardedQuest(questId); // If not, set status to rewarded without broadcasting it to client. + else if (quest->HasFlag(QUEST_FLAGS_TRACKING)) // Check if the quest is used as a serverside flag. + player->SetRewardedQuest(questId); // If so, set status to rewarded without broadcasting it to client. } } @@ -4757,7 +4752,7 @@ void Spell::EffectLeapBack(SpellEffIndex effIndex) float speedxy = m_spellInfo->Effects[effIndex].MiscValue / 10.f; float speedz = damage/ 10.f; //1891: Disengage - m_caster->JumpTo(speedxy, speedz, m_spellInfo->SpellIconID != 1891); + unitTarget->JumpTo(speedxy, speedz, m_spellInfo->SpellIconID != 1891); } void Spell::EffectQuestClear(SpellEffIndex effIndex) @@ -5137,26 +5132,14 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) cMap->AddToMap(pGameObj); - if (uint32 linkedEntry = pGameObj->GetGOInfo()->GetLinkedGameObjectEntry()) + if (GameObject* linkedTrap = pGameObj->GetLinkedTrap()) { - GameObject* linkedGO = new GameObject; - if (linkedGO->Create(cMap->GenerateLowGuid<HighGuid::GameObject>(), linkedEntry, cMap, m_caster->GetPhaseMask(), pos, rot, 255, GO_STATE_READY)) - { - linkedGO->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0); - //linkedGO->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); - linkedGO->SetSpellId(m_spellInfo->Id); - linkedGO->SetOwnerGUID(m_caster->GetGUID()); + linkedTrap->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0); + //linkedTrap->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); + linkedTrap->SetSpellId(m_spellInfo->Id); + linkedTrap->SetOwnerGUID(m_caster->GetGUID()); - ExecuteLogEffectSummonObject(effIndex, linkedGO); - - linkedGO->GetMap()->AddToMap(linkedGO); - } - else - { - delete linkedGO; - linkedGO = NULL; - return; - } + ExecuteLogEffectSummonObject(effIndex, linkedTrap); } } @@ -5169,7 +5152,7 @@ void Spell::EffectProspecting(SpellEffIndex /*effIndex*/) if (!player) return; - if (!itemTarget || !(itemTarget->GetTemplate()->Flags & ITEM_PROTO_FLAG_PROSPECTABLE)) + if (!itemTarget || !(itemTarget->GetTemplate()->Flags & ITEM_FLAG_IS_PROSPECTABLE)) return; if (itemTarget->GetCount() < 5) @@ -5194,7 +5177,7 @@ void Spell::EffectMilling(SpellEffIndex /*effIndex*/) if (!player) return; - if (!itemTarget || !(itemTarget->GetTemplate()->Flags & ITEM_PROTO_FLAG_MILLABLE)) + if (!itemTarget || !(itemTarget->GetTemplate()->Flags & ITEM_FLAG_IS_MILLABLE)) return; if (itemTarget->GetCount() < 5) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 1e3eeee676a..a7b86d2d433 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -16,6 +16,7 @@ */ #include "SpellAuraDefines.h" +#include "SpellAuras.h" #include "SpellInfo.h" #include "SpellMgr.h" #include "Spell.h" @@ -1181,12 +1182,22 @@ bool SpellInfo::IsPositiveEffect(uint8 effIndex) const bool SpellInfo::IsChanneled() const { - return HasAttribute(SPELL_ATTR1_CHANNELED_1) || HasAttribute(SPELL_ATTR1_CHANNELED_2); + return HasAttribute(SpellAttr1(SPELL_ATTR1_CHANNELED_1 | SPELL_ATTR1_CHANNELED_2)); +} + +bool SpellInfo::IsMoveAllowedChannel() const +{ + return IsChanneled() && HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING); } bool SpellInfo::NeedsComboPoints() const { - return HasAttribute(SPELL_ATTR1_REQ_COMBO_POINTS1) || HasAttribute(SPELL_ATTR1_REQ_COMBO_POINTS2); + return HasAttribute(SpellAttr1(SPELL_ATTR1_REQ_COMBO_POINTS1 | SPELL_ATTR1_REQ_COMBO_POINTS2)); +} + +bool SpellInfo::IsNextMeleeSwingSpell() const +{ + return HasAttribute(SpellAttr0(SPELL_ATTR0_ON_NEXT_SWING | SPELL_ATTR0_ON_NEXT_SWING_2)); } bool SpellInfo::IsBreakingStealth() const @@ -1210,6 +1221,20 @@ bool SpellInfo::HasInitialAggro() const return !(HasAttribute(SPELL_ATTR1_NO_THREAT) || HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO)); } +bool SpellInfo::IsAffected(uint32 familyName, flag96 const& familyFlags) const +{ + if (!familyName) + return true; + + if (familyName != SpellFamilyName) + return false; + + if (familyFlags && !(familyFlags & SpellFamilyFlags)) + return false; + + return true; +} + bool SpellInfo::IsAffectedBySpellMods() const { return !HasAttribute(SPELL_ATTR3_NO_DONE_BONUS); @@ -1221,42 +1246,41 @@ bool SpellInfo::IsAffectedBySpellMod(SpellModifier const* mod) const return false; SpellInfo const* affectSpell = sSpellMgr->GetSpellInfo(mod->spellId); - // False if affect_spell == NULL or spellFamily not equal - if (!affectSpell || affectSpell->SpellFamilyName != SpellFamilyName) + if (!affectSpell) return false; - // true - if (mod->mask & SpellFamilyFlags) - return true; - - return false; + return IsAffected(affectSpell->SpellFamilyName, mod->mask); } -bool SpellInfo::CanPierceImmuneAura(SpellInfo const* aura) const +bool SpellInfo::CanPierceImmuneAura(SpellInfo const* auraSpellInfo) const { - // these spells pierce all avalible spells (Resurrection Sickness for example) + // aura can't be pierced + if (auraSpellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) + return false; + + // these spells pierce all available spells (Resurrection Sickness for example) if (HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return true; - // these spells (Cyclone for example) can pierce all... // ...but not these (Divine shield, Ice block, Cyclone and Banish for example) - if (HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) && !(aura && (aura->Mechanic == MECHANIC_IMMUNE_SHIELD || aura->Mechanic == MECHANIC_INVULNERABILITY || aura->Mechanic == MECHANIC_BANISH))) + // Dispels other auras on immunity, check if this spell makes the unit immune to aura + if (HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY) && CanSpellProvideImmunityAgainstAura(auraSpellInfo)) return true; return false; } -bool SpellInfo::CanDispelAura(SpellInfo const* aura) const +bool SpellInfo::CanDispelAura(SpellInfo const* auraSpellInfo) const { - // These spells (like Mass Dispel) can dispell all auras, except death persistent ones (like Dungeon and Battleground Deserter) - if (HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) && !aura->IsDeathPersistent()) - return true; - // These auras (like Divine Shield) can't be dispelled - if (aura->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) + if (auraSpellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return false; + // These spells (like Mass Dispel) can dispel all auras + if (HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) + return true; + // These auras (Cyclone for example) are not dispelable - if (aura->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE)) + if (auraSpellInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) || auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) return false; return true; @@ -1411,9 +1435,11 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a // continent limitation (virtual continent) if (HasAttribute(SPELL_ATTR4_CAST_ONLY_IN_OUTLAND)) { - uint32 v_map = GetVirtualMapForMapAndZone(map_id, zone_id); - MapEntry const* mapEntry = sMapStore.LookupEntry(v_map); - if (!mapEntry || mapEntry->addon < 1 || !mapEntry->IsContinent()) + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(area_id); + if (!areaEntry) + areaEntry = sAreaTableStore.LookupEntry(zone_id); + + if (!areaEntry || !areaEntry->IsFlyable() || !player->CanFlyInZone(map_id, zone_id)) return SPELL_FAILED_INCORRECT_AREA; } @@ -1982,9 +2008,27 @@ void SpellInfo::_LoadSpellSpecific() case 8115: // Agility case 8091: // Armor return SPELL_SPECIFIC_SCROLL; + default: + break; + } + + switch (Id) + { case 12880: // Enrage (Enrage) + case 14201: + case 14202: + case 14203: + case 14204: case 57518: // Enrage (Wrecking Crew) + case 57519: + case 57520: + case 57521: + case 57522: + case 57514: // Enrage (Imp. Defensive Stance) + case 57516: return SPELL_SPECIFIC_WARRIOR_ENRAGE; + default: + break; } } break; @@ -2108,6 +2152,770 @@ void SpellInfo::_LoadSpellSpecific() }(); } +void SpellInfo::_LoadSpellDiminishInfo() +{ + auto diminishingGroupCompute = [this](bool triggered) -> DiminishingGroup + { + if (IsPositive()) + return DIMINISHING_NONE; + + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (Effects[i].ApplyAuraName == SPELL_AURA_MOD_TAUNT) + return DIMINISHING_TAUNT; + } + + // Explicit Diminishing Groups + switch (SpellFamilyName) + { + case SPELLFAMILY_GENERIC: + { + // Pet charge effects (Infernal Awakening, Demon Charge) + if (SpellVisual[0] == 2816 && SpellIconID == 15) + return DIMINISHING_CONTROLLED_STUN; + // Frost Tomb + else if (Id == 48400) + return DIMINISHING_NONE; + // Gnaw + else if (Id == 47481) + return DIMINISHING_CONTROLLED_STUN; + // ToC Icehowl Arctic Breath + else if (SpellVisual[0] == 14153) + return DIMINISHING_NONE; + // Black Plague + else if (Id == 64155) + return DIMINISHING_NONE; + // Screams of the Dead (King Ymiron) + else if (Id == 51750) + return DIMINISHING_NONE; + break; + } + // Event spells + case SPELLFAMILY_UNK1: + return DIMINISHING_NONE; + case SPELLFAMILY_MAGE: + { + // Frostbite + if (SpellFamilyFlags[1] & 0x80000000) + return DIMINISHING_ROOT; + // Shattered Barrier + else if (SpellVisual[0] == 12297) + return DIMINISHING_ROOT; + // Deep Freeze + else if (SpellIconID == 2939 && SpellVisual[0] == 9963) + return DIMINISHING_CONTROLLED_STUN; + // Frost Nova / Freeze (Water Elemental) + else if (SpellIconID == 193) + return DIMINISHING_CONTROLLED_ROOT; + // Dragon's Breath + else if (SpellFamilyFlags[0] & 0x800000) + return DIMINISHING_DRAGONS_BREATH; + break; + } + case SPELLFAMILY_WARRIOR: + { + // Hamstring - limit duration to 10s in PvP + if (SpellFamilyFlags[0] & 0x2) + return DIMINISHING_LIMITONLY; + // Charge Stun (own diminishing) + else if (SpellFamilyFlags[0] & 0x01000000) + return DIMINISHING_CHARGE; + break; + } + case SPELLFAMILY_WARLOCK: + { + // Curses/etc + if ((SpellFamilyFlags[0] & 0x80000000) || (SpellFamilyFlags[1] & 0x200)) + return DIMINISHING_LIMITONLY; + // Seduction + else if (SpellFamilyFlags[1] & 0x10000000) + return DIMINISHING_FEAR; + break; + } + case SPELLFAMILY_DRUID: + { + // Pounce + if (SpellFamilyFlags[0] & 0x20000) + return DIMINISHING_OPENING_STUN; + // Cyclone + else if (SpellFamilyFlags[1] & 0x20) + return DIMINISHING_CYCLONE; + // Entangling Roots + // Nature's Grasp + else if (SpellFamilyFlags[0] & 0x00000200) + return DIMINISHING_CONTROLLED_ROOT; + // Faerie Fire + else if (SpellFamilyFlags[0] & 0x400) + return DIMINISHING_LIMITONLY; + break; + } + case SPELLFAMILY_ROGUE: + { + // Gouge + if (SpellFamilyFlags[0] & 0x8) + return DIMINISHING_DISORIENT; + // Blind + else if (SpellFamilyFlags[0] & 0x1000000) + return DIMINISHING_FEAR; + // Cheap Shot + else if (SpellFamilyFlags[0] & 0x400) + return DIMINISHING_OPENING_STUN; + // Crippling poison - Limit to 10 seconds in PvP (No SpellFamilyFlags) + else if (SpellIconID == 163) + return DIMINISHING_LIMITONLY; + break; + } + case SPELLFAMILY_HUNTER: + { + // Hunter's Mark + if ((SpellFamilyFlags[0] & 0x400) && SpellIconID == 538) + return DIMINISHING_LIMITONLY; + // Scatter Shot (own diminishing) + else if ((SpellFamilyFlags[0] & 0x40000) && SpellIconID == 132) + return DIMINISHING_SCATTER_SHOT; + // Entrapment (own diminishing) + else if (SpellVisual[0] == 7484 && SpellIconID == 20) + return DIMINISHING_ENTRAPMENT; + // Wyvern Sting mechanic is MECHANIC_SLEEP but the diminishing is DIMINISHING_DISORIENT + else if ((SpellFamilyFlags[1] & 0x1000) && SpellIconID == 1721) + return DIMINISHING_DISORIENT; + // Freezing Arrow + else if (SpellFamilyFlags[0] & 0x8) + return DIMINISHING_DISORIENT; + break; + } + case SPELLFAMILY_PALADIN: + { + // Judgement of Justice - limit duration to 10s in PvP + if (SpellFamilyFlags[0] & 0x100000) + return DIMINISHING_LIMITONLY; + // Turn Evil + else if ((SpellFamilyFlags[1] & 0x804000) && SpellIconID == 309) + return DIMINISHING_FEAR; + break; + } + case SPELLFAMILY_SHAMAN: + { + // Storm, Earth and Fire - Earthgrab + if (SpellFamilyFlags[2] & 0x4000) + return DIMINISHING_NONE; + break; + } + case SPELLFAMILY_DEATHKNIGHT: + { + // Hungering Cold (no flags) + if (SpellIconID == 2797) + return DIMINISHING_DISORIENT; + // Mark of Blood + else if ((SpellFamilyFlags[0] & 0x10000000) && SpellIconID == 2285) + return DIMINISHING_LIMITONLY; + break; + } + default: + break; + } + + // Lastly - Set diminishing depending on mechanic + uint32 mechanic = GetAllEffectsMechanicMask(); + if (mechanic & (1 << MECHANIC_CHARM)) + return DIMINISHING_MIND_CONTROL; + if (mechanic & (1 << MECHANIC_SILENCE)) + return DIMINISHING_SILENCE; + if (mechanic & (1 << MECHANIC_SLEEP)) + return DIMINISHING_SLEEP; + if (mechanic & ((1 << MECHANIC_SAPPED) | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_SHACKLE))) + return DIMINISHING_DISORIENT; + // Mechanic Knockout, except Blast Wave + if (mechanic & (1 << MECHANIC_KNOCKOUT) && SpellIconID != 292) + return DIMINISHING_DISORIENT; + if (mechanic & (1 << MECHANIC_DISARM)) + return DIMINISHING_DISARM; + if (mechanic & (1 << MECHANIC_FEAR)) + return DIMINISHING_FEAR; + if (mechanic & (1 << MECHANIC_STUN)) + return triggered ? DIMINISHING_STUN : DIMINISHING_CONTROLLED_STUN; + if (mechanic & (1 << MECHANIC_BANISH)) + return DIMINISHING_BANISH; + if (mechanic & (1 << MECHANIC_ROOT)) + return triggered ? DIMINISHING_ROOT : DIMINISHING_CONTROLLED_ROOT; + if (mechanic & (1 << MECHANIC_HORROR)) + return DIMINISHING_HORROR; + + return DIMINISHING_NONE; + }; + + auto diminishingTypeCompute = [](DiminishingGroup group) -> DiminishingReturnsType + { + switch (group) + { + case DIMINISHING_TAUNT: + case DIMINISHING_CONTROLLED_STUN: + case DIMINISHING_STUN: + case DIMINISHING_OPENING_STUN: + case DIMINISHING_CYCLONE: + case DIMINISHING_CHARGE: + return DRTYPE_ALL; + case DIMINISHING_LIMITONLY: + case DIMINISHING_NONE: + return DRTYPE_NONE; + default: + return DRTYPE_PLAYER; + } + }; + + auto diminishingMaxLevelCompute = [](DiminishingGroup group) -> DiminishingLevels + { + switch (group) + { + case DIMINISHING_TAUNT: + return DIMINISHING_LEVEL_TAUNT_IMMUNE; + default: + return DIMINISHING_LEVEL_IMMUNE; + } + }; + + auto diminishingLimitDurationCompute = [this](DiminishingGroup group) -> int32 + { + auto isGroupDurationLimited = [group]() -> bool + { + switch (group) + { + case DIMINISHING_BANISH: + case DIMINISHING_CONTROLLED_STUN: + case DIMINISHING_CONTROLLED_ROOT: + case DIMINISHING_CYCLONE: + case DIMINISHING_DISORIENT: + case DIMINISHING_ENTRAPMENT: + case DIMINISHING_FEAR: + case DIMINISHING_HORROR: + case DIMINISHING_MIND_CONTROL: + case DIMINISHING_OPENING_STUN: + case DIMINISHING_ROOT: + case DIMINISHING_STUN: + case DIMINISHING_SLEEP: + case DIMINISHING_LIMITONLY: + return true; + default: + return false; + } + }; + + if (!isGroupDurationLimited()) + return 0; + + // Explicit diminishing duration + switch (SpellFamilyName) + { + case SPELLFAMILY_DRUID: + { + // Faerie Fire - limit to 40 seconds in PvP (3.1) + if (SpellFamilyFlags[0] & 0x400) + return 40 * IN_MILLISECONDS; + break; + } + case SPELLFAMILY_HUNTER: + { + // Wyvern Sting + if (SpellFamilyFlags[1] & 0x1000) + return 6 * IN_MILLISECONDS; + // Hunter's Mark + if (SpellFamilyFlags[0] & 0x400) + return 120 * IN_MILLISECONDS; + break; + } + case SPELLFAMILY_PALADIN: + { + // Repentance - limit to 6 seconds in PvP + if (SpellFamilyFlags[0] & 0x4) + return 6 * IN_MILLISECONDS; + break; + } + case SPELLFAMILY_WARLOCK: + { + // Banish - limit to 6 seconds in PvP + if (SpellFamilyFlags[1] & 0x8000000) + return 6 * IN_MILLISECONDS; + // Curse of Tongues - limit to 12 seconds in PvP + else if (SpellFamilyFlags[2] & 0x800) + return 12 * IN_MILLISECONDS; + // Curse of Elements - limit to 120 seconds in PvP + else if (SpellFamilyFlags[1] & 0x200) + return 120 * IN_MILLISECONDS; + break; + } + default: + break; + } + + return 10 * IN_MILLISECONDS; + }; + + SpellDiminishInfo triggeredInfo, normalInfo; + triggeredInfo.DiminishGroup = diminishingGroupCompute(true); + triggeredInfo.DiminishReturnType = diminishingTypeCompute(triggeredInfo.DiminishGroup); + triggeredInfo.DiminishMaxLevel = diminishingMaxLevelCompute(triggeredInfo.DiminishGroup); + triggeredInfo.DiminishDurationLimit = diminishingLimitDurationCompute(triggeredInfo.DiminishGroup); + + normalInfo.DiminishGroup = diminishingGroupCompute(false); + normalInfo.DiminishReturnType = diminishingTypeCompute(normalInfo.DiminishGroup); + normalInfo.DiminishMaxLevel = diminishingMaxLevelCompute(normalInfo.DiminishGroup); + normalInfo.DiminishDurationLimit = diminishingLimitDurationCompute(normalInfo.DiminishGroup); + + _diminishInfoTriggered = triggeredInfo; + _diminishInfoNonTriggered = normalInfo; +} + +DiminishingGroup SpellInfo::GetDiminishingReturnsGroupForSpell(bool triggered) const +{ + return triggered ? _diminishInfoTriggered.DiminishGroup : _diminishInfoNonTriggered.DiminishGroup; +} + +DiminishingReturnsType SpellInfo::GetDiminishingReturnsGroupType(bool triggered) const +{ + return triggered ? _diminishInfoTriggered.DiminishReturnType : _diminishInfoNonTriggered.DiminishReturnType; +} + +DiminishingLevels SpellInfo::GetDiminishingReturnsMaxLevel(bool triggered) const +{ + return triggered ? _diminishInfoTriggered.DiminishMaxLevel : _diminishInfoNonTriggered.DiminishMaxLevel; +} + +int32 SpellInfo::GetDiminishingReturnsLimitDuration(bool triggered) const +{ + return triggered ? _diminishInfoTriggered.DiminishDurationLimit : _diminishInfoNonTriggered.DiminishDurationLimit; +} + +void SpellInfo::_LoadImmunityInfo() +{ + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + uint32 schoolImmunityMask = 0; + uint32 applyHarmfulAuraImmunityMask = 0; + uint32 mechanicImmunityMask = 0; + uint32 dispelImmunity = 0; + uint32 damageImmunityMask = 0; + + int32 miscVal = Effects[i].MiscValue; + int32 amount = Effects[i].CalcValue(); + + ImmunityInfo& immuneInfo = _immunityInfo[i]; + + switch (Effects[i].ApplyAuraName) + { + case SPELL_AURA_MECHANIC_IMMUNITY_MASK: + { + switch (miscVal) + { + case 96: // Free Friend, Uncontrollable Frenzy, Warlord's Presence + { + mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; + + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); + break; + } + case 1615: // Incite Rage, Wolf Spirit, Overload, Lightning Tendrils + { + switch (Id) + { + case 43292: // Incite Rage + case 49172: // Wolf Spirit + mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; + + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); + // no break intended + case 61869: // Overload + case 63481: + case 61887: // Lightning Tendrils + case 63486: + mechanicImmunityMask |= (1 << MECHANIC_INTERRUPT) | (1 << MECHANIC_SILENCE); + + immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_KNOCK_BACK); + immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_KNOCK_BACK_DEST); + break; + default: + break; + } + break; + } + case 679: // Mind Control, Avenging Fury + { + if (Id == 57742) // Avenging Fury + { + mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; + + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); + } + break; + } + case 1557: // Startling Roar, Warlord Roar, Break Bonds, Stormshield + { + if (Id == 64187) // Stormshield + { + mechanicImmunityMask |= (1 << MECHANIC_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + } + else + { + mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; + + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); + } + break; + } + case 1614: // Fixate + case 1694: // Fixated, Lightning Tendrils + { + immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_ATTACK_ME); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_TAUNT); + break; + } + case 1630: // Fervor, Berserk + { + if (Id == 64112) // Berserk + { + immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_ATTACK_ME); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_TAUNT); + } + else + { + mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; + + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); + } + break; + } + case 477: // Bladestorm + case 1733: // Bladestorm, Killing Spree + { + if (!amount) + { + mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; + + immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_KNOCK_BACK); + immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_KNOCK_BACK_DEST); + + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); + } + break; + } + case 878: // Whirlwind, Fog of Corruption, Determination + { + if (Id == 66092) // Determination + { + mechanicImmunityMask |= (1 << MECHANIC_SNARE) | (1 << MECHANIC_STUN) + | (1 << MECHANIC_DISORIENTED) | (1 << MECHANIC_FREEZE); + + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + } + break; + } + default: + break; + } + + if (immuneInfo.AuraTypeImmune.empty()) + { + if (miscVal & (1 << 10)) + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + if (miscVal & (1 << 1)) + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_TRANSFORM); + + // These flag can be recognized wrong: + if (miscVal & (1 << 6)) + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + if (miscVal & (1 << 0)) + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + if (miscVal & (1 << 2)) + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); + if (miscVal & (1 << 9)) + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); + if (miscVal & (1 << 7)) + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DISARM); + } + break; + } + case SPELL_AURA_MECHANIC_IMMUNITY: + { + switch (Id) + { + case 34471: // The Beast Within + case 19574: // Bestial Wrath + case 42292: // PvP trinket + case 59752: // Every Man for Himself + case 53490: // Bullheaded + mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; + break; + case 54508: // Demonic Empowerment + mechanicImmunityMask |= (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT) | (1 << MECHANIC_STUN); + break; + default: + if (miscVal < 1) + continue; + + mechanicImmunityMask |= 1 << miscVal; + break; + } + break; + } + case SPELL_AURA_EFFECT_IMMUNITY: + { + immuneInfo.SpellEffectImmune.insert(static_cast<SpellEffects>(miscVal)); + break; + } + case SPELL_AURA_STATE_IMMUNITY: + { + immuneInfo.AuraTypeImmune.insert(static_cast<AuraType>(miscVal)); + break; + } + case SPELL_AURA_SCHOOL_IMMUNITY: + { + schoolImmunityMask |= uint32(miscVal); + break; + } + case SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL: + { + applyHarmfulAuraImmunityMask |= uint32(miscVal); + break; + } + case SPELL_AURA_DAMAGE_IMMUNITY: + { + damageImmunityMask |= uint32(miscVal); + break; + } + case SPELL_AURA_DISPEL_IMMUNITY: + { + dispelImmunity = uint32(miscVal); + break; + } + default: + break; + } + + immuneInfo.SchoolImmuneMask = schoolImmunityMask; + immuneInfo.ApplyHarmfulAuraImmuneMask = applyHarmfulAuraImmunityMask; + immuneInfo.MechanicImmuneMask = mechanicImmunityMask; + immuneInfo.DispelImmune = dispelImmunity; + immuneInfo.DamageSchoolMask = damageImmunityMask; + + immuneInfo.AuraTypeImmune.shrink_to_fit(); + immuneInfo.SpellEffectImmune.shrink_to_fit(); + } +} + +void SpellInfo::ApplyAllSpellImmunitiesTo(Unit* target, uint8 effIndex, bool apply) const +{ + ImmunityInfo const* immuneInfo = _immunityInfo + effIndex; + + if (uint32 schoolImmunity = immuneInfo->SchoolImmuneMask) + { + target->ApplySpellImmune(Id, IMMUNITY_SCHOOL, schoolImmunity, apply); + + if (apply && HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) + { + target->RemoveAppliedAuras([this, schoolImmunity](AuraApplication const* aurApp) -> bool + { + SpellInfo const* auraSpellInfo = aurApp->GetBase()->GetSpellInfo(); + return ((auraSpellInfo->GetSchoolMask() & schoolImmunity) != 0 && // Check for school mask + CanDispelAura(auraSpellInfo) && + (IsPositive() != aurApp->IsPositive()) && // Check spell vs aura possitivity + !auraSpellInfo->IsPassive() && // Don't remove passive auras + auraSpellInfo->Id != Id); // Don't remove self + }); + } + } + + if (uint32 mechanicImmunity = immuneInfo->MechanicImmuneMask) + { + for (uint32 i = 0; i < MAX_MECHANIC; ++i) + if (mechanicImmunity & (1 << i)) + target->ApplySpellImmune(Id, IMMUNITY_MECHANIC, i, apply); + + if (apply && HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) + target->RemoveAurasWithMechanic(mechanicImmunity, AURA_REMOVE_BY_DEFAULT, Id); + } + + if (uint32 dispelImmunity = immuneInfo->DispelImmune) + { + target->ApplySpellImmune(Id, IMMUNITY_DISPEL, dispelImmunity, apply); + + if (apply && HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) + { + target->RemoveAppliedAuras([dispelImmunity](AuraApplication const* aurApp) -> bool + { + SpellInfo const* spellInfo = aurApp->GetBase()->GetSpellInfo(); + if (spellInfo->Dispel == dispelImmunity) + return true; + + return false; + }); + } + } + + if (uint32 damageImmunity = immuneInfo->DamageSchoolMask) + target->ApplySpellImmune(Id, IMMUNITY_DAMAGE, damageImmunity, apply); + + for (AuraType auraType : immuneInfo->AuraTypeImmune) + { + target->ApplySpellImmune(Id, IMMUNITY_STATE, auraType, apply); + if (apply && HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) + target->RemoveAurasByType(auraType); + } + + for (SpellEffects effectType : immuneInfo->SpellEffectImmune) + target->ApplySpellImmune(Id, IMMUNITY_EFFECT, effectType, apply); +} + +bool SpellInfo::CanSpellProvideImmunityAgainstAura(SpellInfo const* auraSpellInfo) const +{ + if (!auraSpellInfo) + return false; + + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + ImmunityInfo const* immuneInfo = _immunityInfo + i; + + if (!auraSpellInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) && !auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) + { + if (uint32 schoolImmunity = immuneInfo->SchoolImmuneMask) + if ((auraSpellInfo->SchoolMask & schoolImmunity) != 0) + return true; + } + + if (uint32 mechanicImmunity = immuneInfo->MechanicImmuneMask) + if ((mechanicImmunity & (1 << auraSpellInfo->Mechanic)) != 0) + return true; + + if (uint32 dispelImmunity = immuneInfo->DispelImmune) + if (auraSpellInfo->Dispel == dispelImmunity) + return true; + + bool immuneToAllEffects = true; + for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) + { + uint32 effectName = auraSpellInfo->Effects[effIndex].Effect; + if (!effectName) + continue; + + auto spellImmuneItr = immuneInfo->SpellEffectImmune.find(static_cast<SpellEffects>(effectName)); + if (spellImmuneItr == immuneInfo->SpellEffectImmune.cend()) + { + immuneToAllEffects = false; + break; + } + + if (uint32 mechanic = auraSpellInfo->Effects[effIndex].Mechanic) + { + if (!(immuneInfo->MechanicImmuneMask & (1 << mechanic))) + { + immuneToAllEffects = false; + break; + } + } + + if (!auraSpellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) + { + if (uint32 auraName = auraSpellInfo->Effects[effIndex].ApplyAuraName) + { + bool isImmuneToAuraEffectApply = false; + auto auraImmuneItr = immuneInfo->AuraTypeImmune.find(static_cast<AuraType>(auraName)); + if (auraImmuneItr != immuneInfo->AuraTypeImmune.cend()) + isImmuneToAuraEffectApply = true; + + if (!isImmuneToAuraEffectApply && !auraSpellInfo->IsPositiveEffect(effIndex) && !auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) + { + if (uint32 applyHarmfulAuraImmunityMask = immuneInfo->ApplyHarmfulAuraImmuneMask) + if ((auraSpellInfo->GetSchoolMask() & applyHarmfulAuraImmunityMask) != 0) + isImmuneToAuraEffectApply = true; + } + + if (!isImmuneToAuraEffectApply) + { + immuneToAllEffects = false; + break; + } + } + } + } + + if (immuneToAllEffects) + return true; + } + + return false; +} + +// based on client sub_007FDFA0 +bool SpellInfo::CanSpellCastOverrideAuraEffect(SpellInfo const* auraSpellInfo, uint8 auraEffIndex) const +{ + if (!HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) + return false; + + if (auraSpellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) + return false; + + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (Effects[i].Effect != SPELL_EFFECT_APPLY_AURA) + continue; + + uint32 const miscValue = static_cast<uint32>(Effects[i].MiscValue); + switch (Effects[i].ApplyAuraName) + { + case SPELL_AURA_STATE_IMMUNITY: + if (miscValue != auraSpellInfo->Effects[auraEffIndex].ApplyAuraName) + continue; + break; + case SPELL_AURA_SCHOOL_IMMUNITY: + case SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL: + if (auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE) || !(auraSpellInfo->SchoolMask & miscValue)) + continue; + break; + case SPELL_AURA_DISPEL_IMMUNITY: + if (miscValue != auraSpellInfo->Dispel) + continue; + break; + case SPELL_AURA_MECHANIC_IMMUNITY: + if (miscValue != auraSpellInfo->Mechanic) + { + if (miscValue != auraSpellInfo->Effects[auraEffIndex].Mechanic) + continue; + } + break; + default: + continue; + } + + return true; + } + + return false; +} + float SpellInfo::GetMinRange(bool positive) const { if (!RangeEntry) @@ -2455,6 +3263,9 @@ bool SpellInfo::_IsPositiveEffect(uint8 effIndex, bool deep) const // Impact if (SpellIconID == 45) return true; + // Arcane Missiles + if (SpellFamilyFlags[0] == 0x00000800) + return false; break; case SPELLFAMILY_PRIEST: switch (Id) @@ -2484,6 +3295,9 @@ bool SpellInfo::_IsPositiveEffect(uint8 effIndex, bool deep) const case 32684: // Envenom (Rank 2) case 57992: // Envenom (Rank 3) case 57993: // Envenom (Rank 4) + // Slice and Dice. Prevents breaking Stealth + case 5171: // Slice and Dice (Rank 1) + case 6774: // Slice and Dice (Rank 2) return true; default: break; diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 2f94555e493..4b2eff28bd6 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -24,10 +24,13 @@ #include "Object.h" #include "SpellAuraDefines.h" +#include <boost/container/flat_set.hpp> + class Unit; class Player; class Item; class Spell; +class SpellMgr; class SpellInfo; struct SpellChainNode; struct SpellTargetPosition; @@ -291,207 +294,251 @@ private: static StaticData _data[TOTAL_SPELL_EFFECTS]; }; -class TC_GAME_API SpellInfo +struct TC_GAME_API SpellDiminishInfo { -public: - uint32 Id; - SpellCategoryEntry const* CategoryEntry; - uint32 Dispel; - uint32 Mechanic; - uint32 Attributes; - uint32 AttributesEx; - uint32 AttributesEx2; - uint32 AttributesEx3; - uint32 AttributesEx4; - uint32 AttributesEx5; - uint32 AttributesEx6; - uint32 AttributesEx7; - uint32 AttributesCu; - uint64 Stances; - uint64 StancesNot; - uint32 Targets; - uint32 TargetCreatureType; - uint32 RequiresSpellFocus; - uint32 FacingCasterFlags; - uint32 CasterAuraState; - uint32 TargetAuraState; - uint32 CasterAuraStateNot; - uint32 TargetAuraStateNot; - uint32 CasterAuraSpell; - uint32 TargetAuraSpell; - uint32 ExcludeCasterAuraSpell; - uint32 ExcludeTargetAuraSpell; - SpellCastTimesEntry const* CastTimeEntry; - uint32 RecoveryTime; - uint32 CategoryRecoveryTime; - uint32 StartRecoveryCategory; - uint32 StartRecoveryTime; - uint32 InterruptFlags; - uint32 AuraInterruptFlags; - uint32 ChannelInterruptFlags; - uint32 ProcFlags; - uint32 ProcChance; - uint32 ProcCharges; - uint32 MaxLevel; - uint32 BaseLevel; - uint32 SpellLevel; - SpellDurationEntry const* DurationEntry; - uint32 PowerType; - uint32 ManaCost; - uint32 ManaCostPerlevel; - uint32 ManaPerSecond; - uint32 ManaPerSecondPerLevel; - uint32 ManaCostPercentage; - uint32 RuneCostID; - SpellRangeEntry const* RangeEntry; - float Speed; - uint32 StackAmount; - uint32 Totem[2]; - int32 Reagent[MAX_SPELL_REAGENTS]; - uint32 ReagentCount[MAX_SPELL_REAGENTS]; - int32 EquippedItemClass; - int32 EquippedItemSubClassMask; - int32 EquippedItemInventoryTypeMask; - uint32 TotemCategory[2]; - uint32 SpellVisual[2]; - uint32 SpellIconID; - uint32 ActiveIconID; - char* SpellName[16]; - char* Rank[16]; - uint32 MaxTargetLevel; - uint32 MaxAffectedTargets; - uint32 SpellFamilyName; - flag96 SpellFamilyFlags; - uint32 DmgClass; - uint32 PreventionType; - int32 AreaGroupId; - uint32 SchoolMask; - SpellEffectInfo Effects[MAX_SPELL_EFFECTS]; - uint32 ExplicitTargetMask; - SpellChainNode const* ChainEntry; - - SpellInfo(SpellEntry const* spellEntry); - ~SpellInfo(); - - uint32 GetCategory() const; - bool HasEffect(SpellEffects effect) const; - bool HasAura(AuraType aura) const; - bool HasAreaAuraEffect() const; - - inline bool HasAttribute(SpellAttr0 attribute) const { return !!(Attributes & attribute); } - inline bool HasAttribute(SpellAttr1 attribute) const { return !!(AttributesEx & attribute); } - inline bool HasAttribute(SpellAttr2 attribute) const { return !!(AttributesEx2 & attribute); } - inline bool HasAttribute(SpellAttr3 attribute) const { return !!(AttributesEx3 & attribute); } - inline bool HasAttribute(SpellAttr4 attribute) const { return !!(AttributesEx4 & attribute); } - inline bool HasAttribute(SpellAttr5 attribute) const { return !!(AttributesEx5 & attribute); } - inline bool HasAttribute(SpellAttr6 attribute) const { return !!(AttributesEx6 & attribute); } - inline bool HasAttribute(SpellAttr7 attribute) const { return !!(AttributesEx7 & attribute); } - inline bool HasAttribute(SpellCustomAttributes customAttribute) const { return !!(AttributesCu & customAttribute); } - - bool IsExplicitDiscovery() const; - bool IsLootCrafting() const; - bool IsQuestTame() const; - bool IsProfessionOrRiding() const; - bool IsProfession() const; - bool IsPrimaryProfession() const; - bool IsPrimaryProfessionFirstRank() const; - bool IsAbilityLearnedWithProfession() const; - bool IsAbilityOfSkillType(uint32 skillType) const; - - bool IsAffectingArea() const; - bool IsTargetingArea() const; - bool NeedsExplicitUnitTarget() const; - bool NeedsToBeTriggeredByCaster(SpellInfo const* triggeringSpell) const; - - bool IsPassive() const; - bool IsAutocastable() const; - bool IsStackableWithRanks() const; - bool IsPassiveStackableWithRanks() const; - bool IsMultiSlotAura() const; - bool IsStackableOnOneSlotWithDifferentCasters() const; - bool IsCooldownStartedOnEvent() const; - bool IsDeathPersistent() const; - bool IsRequiringDeadTarget() const; - bool IsAllowingDeadTarget() const; - bool IsGroupBuff() const; - bool CanBeUsedInCombat() const; - bool IsPositive() const; - bool IsPositiveEffect(uint8 effIndex) const; - bool IsChanneled() const; - bool NeedsComboPoints() const; - bool IsBreakingStealth() const; - bool IsRangedWeaponSpell() const; - bool IsAutoRepeatRangedSpell() const; - bool HasInitialAggro() const; - - bool IsAffectedBySpellMods() const; - bool IsAffectedBySpellMod(SpellModifier const* mod) const; - - bool CanPierceImmuneAura(SpellInfo const* aura) const; - bool CanDispelAura(SpellInfo const* aura) const; - - bool IsSingleTarget() const; - bool IsAuraExclusiveBySpecificWith(SpellInfo const* spellInfo) const; - bool IsAuraExclusiveBySpecificPerCasterWith(SpellInfo const* spellInfo) const; - - SpellCastResult CheckShapeshift(uint32 form) const; - SpellCastResult CheckLocation(uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player = NULL) const; - SpellCastResult CheckTarget(Unit const* caster, WorldObject const* target, bool implicit = true) const; - SpellCastResult CheckExplicitTarget(Unit const* caster, WorldObject const* target, Item const* itemTarget = NULL) const; - SpellCastResult CheckVehicle(Unit const* caster) const; - bool CheckTargetCreatureType(Unit const* target) const; - - SpellSchoolMask GetSchoolMask() const; - uint32 GetAllEffectsMechanicMask() const; - uint32 GetEffectMechanicMask(uint8 effIndex) const; - uint32 GetSpellMechanicMaskByEffectMask(uint32 effectMask) const; - Mechanics GetEffectMechanic(uint8 effIndex) const; - bool HasAnyEffectMechanic() const; - uint32 GetDispelMask() const; - static uint32 GetDispelMask(DispelType type); - uint32 GetExplicitTargetMask() const; - - AuraStateType GetAuraState() const; - SpellSpecificType GetSpellSpecific() const; - - float GetMinRange(bool positive = false) const; - float GetMaxRange(bool positive = false, Unit* caster = NULL, Spell* spell = NULL) const; - - int32 GetDuration() const; - int32 GetMaxDuration() const; - - uint32 GetMaxTicks() const; - - uint32 CalcCastTime(Spell* spell = NULL) const; - uint32 GetRecoveryTime() const; - - int32 CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) const; - - bool IsRanked() const; - uint8 GetRank() const; - SpellInfo const* GetFirstRankSpell() const; - SpellInfo const* GetLastRankSpell() const; - SpellInfo const* GetNextRankSpell() const; - SpellInfo const* GetPrevRankSpell() const; - SpellInfo const* GetAuraRankForLevel(uint8 level) const; - bool IsRankOf(SpellInfo const* spellInfo) const; - bool IsDifferentRankOf(SpellInfo const* spellInfo) const; - bool IsHighRankOf(SpellInfo const* spellInfo) const; - - // loading helpers - void _InitializeExplicitTargetMask(); - bool _IsPositiveEffect(uint8 effIndex, bool deep) const; - bool _IsPositiveSpell() const; - static bool _IsPositiveTarget(uint32 targetA, uint32 targetB); - void _LoadSpellSpecific(); - void _LoadAuraState(); - - // unloading helpers - void _UnloadImplicitTargetConditionLists(); + DiminishingGroup DiminishGroup = DIMINISHING_NONE; + DiminishingReturnsType DiminishReturnType = DRTYPE_NONE; + DiminishingLevels DiminishMaxLevel = DIMINISHING_LEVEL_IMMUNE; + int32 DiminishDurationLimit = 0; +}; -private: - SpellSpecificType _spellSpecific; - AuraStateType _auraState; +struct TC_GAME_API ImmunityInfo +{ + uint32 SchoolImmuneMask = 0; + uint32 ApplyHarmfulAuraImmuneMask = 0; + uint32 MechanicImmuneMask = 0; + uint32 DispelImmune = 0; + uint32 DamageSchoolMask = 0; + + boost::container::flat_set<AuraType> AuraTypeImmune; + boost::container::flat_set<SpellEffects> SpellEffectImmune; +}; + +class TC_GAME_API SpellInfo +{ + friend class SpellMgr; + + public: + uint32 Id; + SpellCategoryEntry const* CategoryEntry; + uint32 Dispel; + uint32 Mechanic; + uint32 Attributes; + uint32 AttributesEx; + uint32 AttributesEx2; + uint32 AttributesEx3; + uint32 AttributesEx4; + uint32 AttributesEx5; + uint32 AttributesEx6; + uint32 AttributesEx7; + uint32 AttributesCu; + uint64 Stances; + uint64 StancesNot; + uint32 Targets; + uint32 TargetCreatureType; + uint32 RequiresSpellFocus; + uint32 FacingCasterFlags; + uint32 CasterAuraState; + uint32 TargetAuraState; + uint32 CasterAuraStateNot; + uint32 TargetAuraStateNot; + uint32 CasterAuraSpell; + uint32 TargetAuraSpell; + uint32 ExcludeCasterAuraSpell; + uint32 ExcludeTargetAuraSpell; + SpellCastTimesEntry const* CastTimeEntry; + uint32 RecoveryTime; + uint32 CategoryRecoveryTime; + uint32 StartRecoveryCategory; + uint32 StartRecoveryTime; + uint32 InterruptFlags; + uint32 AuraInterruptFlags; + uint32 ChannelInterruptFlags; + uint32 ProcFlags; + uint32 ProcChance; + uint32 ProcCharges; + uint32 MaxLevel; + uint32 BaseLevel; + uint32 SpellLevel; + SpellDurationEntry const* DurationEntry; + uint32 PowerType; + uint32 ManaCost; + uint32 ManaCostPerlevel; + uint32 ManaPerSecond; + uint32 ManaPerSecondPerLevel; + uint32 ManaCostPercentage; + uint32 RuneCostID; + SpellRangeEntry const* RangeEntry; + float Speed; + uint32 StackAmount; + uint32 Totem[2]; + int32 Reagent[MAX_SPELL_REAGENTS]; + uint32 ReagentCount[MAX_SPELL_REAGENTS]; + int32 EquippedItemClass; + int32 EquippedItemSubClassMask; + int32 EquippedItemInventoryTypeMask; + uint32 TotemCategory[2]; + uint32 SpellVisual[2]; + uint32 SpellIconID; + uint32 ActiveIconID; + char* SpellName[16]; + char* Rank[16]; + uint32 MaxTargetLevel; + uint32 MaxAffectedTargets; + uint32 SpellFamilyName; + flag96 SpellFamilyFlags; + uint32 DmgClass; + uint32 PreventionType; + int32 AreaGroupId; + uint32 SchoolMask; + SpellEffectInfo Effects[MAX_SPELL_EFFECTS]; + uint32 ExplicitTargetMask; + SpellChainNode const* ChainEntry; + + SpellInfo(SpellEntry const* spellEntry); + ~SpellInfo(); + + uint32 GetCategory() const; + bool HasEffect(SpellEffects effect) const; + bool HasAura(AuraType aura) const; + bool HasAreaAuraEffect() const; + + inline bool HasAttribute(SpellAttr0 attribute) const { return !!(Attributes & attribute); } + inline bool HasAttribute(SpellAttr1 attribute) const { return !!(AttributesEx & attribute); } + inline bool HasAttribute(SpellAttr2 attribute) const { return !!(AttributesEx2 & attribute); } + inline bool HasAttribute(SpellAttr3 attribute) const { return !!(AttributesEx3 & attribute); } + inline bool HasAttribute(SpellAttr4 attribute) const { return !!(AttributesEx4 & attribute); } + inline bool HasAttribute(SpellAttr5 attribute) const { return !!(AttributesEx5 & attribute); } + inline bool HasAttribute(SpellAttr6 attribute) const { return !!(AttributesEx6 & attribute); } + inline bool HasAttribute(SpellAttr7 attribute) const { return !!(AttributesEx7 & attribute); } + inline bool HasAttribute(SpellCustomAttributes customAttribute) const { return !!(AttributesCu & customAttribute); } + + bool IsExplicitDiscovery() const; + bool IsLootCrafting() const; + bool IsQuestTame() const; + bool IsProfessionOrRiding() const; + bool IsProfession() const; + bool IsPrimaryProfession() const; + bool IsPrimaryProfessionFirstRank() const; + bool IsAbilityLearnedWithProfession() const; + bool IsAbilityOfSkillType(uint32 skillType) const; + + bool IsAffectingArea() const; + bool IsTargetingArea() const; + bool NeedsExplicitUnitTarget() const; + bool NeedsToBeTriggeredByCaster(SpellInfo const* triggeringSpell) const; + + bool IsPassive() const; + bool IsAutocastable() const; + bool IsStackableWithRanks() const; + bool IsPassiveStackableWithRanks() const; + bool IsMultiSlotAura() const; + bool IsStackableOnOneSlotWithDifferentCasters() const; + bool IsCooldownStartedOnEvent() const; + bool IsDeathPersistent() const; + bool IsRequiringDeadTarget() const; + bool IsAllowingDeadTarget() const; + bool IsGroupBuff() const; + bool CanBeUsedInCombat() const; + bool IsPositive() const; + bool IsPositiveEffect(uint8 effIndex) const; + bool IsChanneled() const; + bool IsMoveAllowedChannel() const; + bool NeedsComboPoints() const; + bool IsNextMeleeSwingSpell() const; + bool IsBreakingStealth() const; + bool IsRangedWeaponSpell() const; + bool IsAutoRepeatRangedSpell() const; + bool HasInitialAggro() const; + + bool IsAffected(uint32 familyName, flag96 const& familyFlags) const; + + bool IsAffectedBySpellMods() const; + bool IsAffectedBySpellMod(SpellModifier const* mod) const; + + bool CanPierceImmuneAura(SpellInfo const* auraSpellInfo) const; + bool CanDispelAura(SpellInfo const* auraSpellInfo) const; + + bool IsSingleTarget() const; + bool IsAuraExclusiveBySpecificWith(SpellInfo const* spellInfo) const; + bool IsAuraExclusiveBySpecificPerCasterWith(SpellInfo const* spellInfo) const; + + SpellCastResult CheckShapeshift(uint32 form) const; + SpellCastResult CheckLocation(uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player = NULL) const; + SpellCastResult CheckTarget(Unit const* caster, WorldObject const* target, bool implicit = true) const; + SpellCastResult CheckExplicitTarget(Unit const* caster, WorldObject const* target, Item const* itemTarget = NULL) const; + SpellCastResult CheckVehicle(Unit const* caster) const; + bool CheckTargetCreatureType(Unit const* target) const; + + SpellSchoolMask GetSchoolMask() const; + uint32 GetAllEffectsMechanicMask() const; + uint32 GetEffectMechanicMask(uint8 effIndex) const; + uint32 GetSpellMechanicMaskByEffectMask(uint32 effectMask) const; + Mechanics GetEffectMechanic(uint8 effIndex) const; + bool HasAnyEffectMechanic() const; + uint32 GetDispelMask() const; + static uint32 GetDispelMask(DispelType type); + uint32 GetExplicitTargetMask() const; + + AuraStateType GetAuraState() const; + SpellSpecificType GetSpellSpecific() const; + + float GetMinRange(bool positive = false) const; + float GetMaxRange(bool positive = false, Unit* caster = NULL, Spell* spell = NULL) const; + + int32 GetDuration() const; + int32 GetMaxDuration() const; + + uint32 GetMaxTicks() const; + + uint32 CalcCastTime(Spell* spell = NULL) const; + uint32 GetRecoveryTime() const; + + int32 CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) const; + + bool IsRanked() const; + uint8 GetRank() const; + SpellInfo const* GetFirstRankSpell() const; + SpellInfo const* GetLastRankSpell() const; + SpellInfo const* GetNextRankSpell() const; + SpellInfo const* GetPrevRankSpell() const; + SpellInfo const* GetAuraRankForLevel(uint8 level) const; + bool IsRankOf(SpellInfo const* spellInfo) const; + bool IsDifferentRankOf(SpellInfo const* spellInfo) const; + bool IsHighRankOf(SpellInfo const* spellInfo) const; + + // spell diminishing returns + DiminishingGroup GetDiminishingReturnsGroupForSpell(bool triggered) const; + DiminishingReturnsType GetDiminishingReturnsGroupType(bool triggered) const; + DiminishingLevels GetDiminishingReturnsMaxLevel(bool triggered) const; + int32 GetDiminishingReturnsLimitDuration(bool triggered) const; + + // spell immunities + void ApplyAllSpellImmunitiesTo(Unit* target, uint8 effIndex, bool apply) const; + bool CanSpellProvideImmunityAgainstAura(SpellInfo const* auraSpellInfo) const; + bool CanSpellCastOverrideAuraEffect(SpellInfo const* auraSpellInfo, uint8 auraEffIndex) const; + + private: + // loading helpers + void _InitializeExplicitTargetMask(); + bool _IsPositiveEffect(uint8 effIndex, bool deep) const; + bool _IsPositiveSpell() const; + static bool _IsPositiveTarget(uint32 targetA, uint32 targetB); + void _LoadSpellSpecific(); + void _LoadAuraState(); + void _LoadSpellDiminishInfo(); + void _LoadImmunityInfo(); + + // unloading helpers + void _UnloadImplicitTargetConditionLists(); + + SpellSpecificType _spellSpecific; + AuraStateType _auraState; + + SpellDiminishInfo _diminishInfoNonTriggered; + SpellDiminishInfo _diminishInfoTriggered; + + ImmunityInfo _immunityInfo[MAX_SPELL_EFFECTS]; }; #endif // _SPELLINFO_H diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 94d34d78bd6..d034c744cc1 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -51,302 +51,6 @@ bool IsPartOfSkillLine(uint32 skillId, uint32 spellId) return false; } -DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellInfo const* spellproto, bool triggered) -{ - if (spellproto->IsPositive()) - return DIMINISHING_NONE; - - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (spellproto->Effects[i].ApplyAuraName == SPELL_AURA_MOD_TAUNT) - return DIMINISHING_TAUNT; - } - - // Explicit Diminishing Groups - switch (spellproto->SpellFamilyName) - { - case SPELLFAMILY_GENERIC: - { - // Pet charge effects (Infernal Awakening, Demon Charge) - if (spellproto->SpellVisual[0] == 2816 && spellproto->SpellIconID == 15) - return DIMINISHING_CONTROLLED_STUN; - // Frost Tomb - else if (spellproto->Id == 48400) - return DIMINISHING_NONE; - // Gnaw - else if (spellproto->Id == 47481) - return DIMINISHING_CONTROLLED_STUN; - // ToC Icehowl Arctic Breath - else if (spellproto->SpellVisual[0] == 14153) - return DIMINISHING_NONE; - // Black Plague - else if (spellproto->Id == 64155) - return DIMINISHING_NONE; - // Screams of the Dead (King Ymiron) - else if (spellproto->Id == 51750) - return DIMINISHING_NONE; - break; - } - // Event spells - case SPELLFAMILY_UNK1: - return DIMINISHING_NONE; - case SPELLFAMILY_MAGE: - { - // Frostbite - if (spellproto->SpellFamilyFlags[1] & 0x80000000) - return DIMINISHING_ROOT; - // Shattered Barrier - else if (spellproto->SpellVisual[0] == 12297) - return DIMINISHING_ROOT; - // Deep Freeze - else if (spellproto->SpellIconID == 2939 && spellproto->SpellVisual[0] == 9963) - return DIMINISHING_CONTROLLED_STUN; - // Frost Nova / Freeze (Water Elemental) - else if (spellproto->SpellIconID == 193) - return DIMINISHING_CONTROLLED_ROOT; - // Dragon's Breath - else if (spellproto->SpellFamilyFlags[0] & 0x800000) - return DIMINISHING_DRAGONS_BREATH; - break; - } - case SPELLFAMILY_WARRIOR: - { - // Hamstring - limit duration to 10s in PvP - if (spellproto->SpellFamilyFlags[0] & 0x2) - return DIMINISHING_LIMITONLY; - // Charge Stun (own diminishing) - else if (spellproto->SpellFamilyFlags[0] & 0x01000000) - return DIMINISHING_CHARGE; - break; - } - case SPELLFAMILY_WARLOCK: - { - // Curses/etc - if ((spellproto->SpellFamilyFlags[0] & 0x80000000) || (spellproto->SpellFamilyFlags[1] & 0x200)) - return DIMINISHING_LIMITONLY; - // Seduction - else if (spellproto->SpellFamilyFlags[1] & 0x10000000) - return DIMINISHING_FEAR; - break; - } - case SPELLFAMILY_DRUID: - { - // Pounce - if (spellproto->SpellFamilyFlags[0] & 0x20000) - return DIMINISHING_OPENING_STUN; - // Cyclone - else if (spellproto->SpellFamilyFlags[1] & 0x20) - return DIMINISHING_CYCLONE; - // Entangling Roots - // Nature's Grasp - else if (spellproto->SpellFamilyFlags[0] & 0x00000200) - return DIMINISHING_CONTROLLED_ROOT; - // Faerie Fire - else if (spellproto->SpellFamilyFlags[0] & 0x400) - return DIMINISHING_LIMITONLY; - break; - } - case SPELLFAMILY_ROGUE: - { - // Gouge - if (spellproto->SpellFamilyFlags[0] & 0x8) - return DIMINISHING_DISORIENT; - // Blind - else if (spellproto->SpellFamilyFlags[0] & 0x1000000) - return DIMINISHING_FEAR; - // Cheap Shot - else if (spellproto->SpellFamilyFlags[0] & 0x400) - return DIMINISHING_OPENING_STUN; - // Crippling poison - Limit to 10 seconds in PvP (No SpellFamilyFlags) - else if (spellproto->SpellIconID == 163) - return DIMINISHING_LIMITONLY; - break; - } - case SPELLFAMILY_HUNTER: - { - // Hunter's Mark - if ((spellproto->SpellFamilyFlags[0] & 0x400) && spellproto->SpellIconID == 538) - return DIMINISHING_LIMITONLY; - // Scatter Shot (own diminishing) - else if ((spellproto->SpellFamilyFlags[0] & 0x40000) && spellproto->SpellIconID == 132) - return DIMINISHING_SCATTER_SHOT; - // Entrapment (own diminishing) - else if (spellproto->SpellVisual[0] == 7484 && spellproto->SpellIconID == 20) - return DIMINISHING_ENTRAPMENT; - // Wyvern Sting mechanic is MECHANIC_SLEEP but the diminishing is DIMINISHING_DISORIENT - else if ((spellproto->SpellFamilyFlags[1] & 0x1000) && spellproto->SpellIconID == 1721) - return DIMINISHING_DISORIENT; - // Freezing Arrow - else if (spellproto->SpellFamilyFlags[0] & 0x8) - return DIMINISHING_DISORIENT; - break; - } - case SPELLFAMILY_PALADIN: - { - // Judgement of Justice - limit duration to 10s in PvP - if (spellproto->SpellFamilyFlags[0] & 0x100000) - return DIMINISHING_LIMITONLY; - // Turn Evil - else if ((spellproto->SpellFamilyFlags[1] & 0x804000) && spellproto->SpellIconID == 309) - return DIMINISHING_FEAR; - break; - } - case SPELLFAMILY_SHAMAN: - { - // Storm, Earth and Fire - Earthgrab - if (spellproto->SpellFamilyFlags[2] & 0x4000) - return DIMINISHING_NONE; - break; - } - case SPELLFAMILY_DEATHKNIGHT: - { - // Hungering Cold (no flags) - if (spellproto->SpellIconID == 2797) - return DIMINISHING_DISORIENT; - // Mark of Blood - else if ((spellproto->SpellFamilyFlags[0] & 0x10000000) && spellproto->SpellIconID == 2285) - return DIMINISHING_LIMITONLY; - break; - } - default: - break; - } - - // Lastly - Set diminishing depending on mechanic - uint32 mechanic = spellproto->GetAllEffectsMechanicMask(); - if (mechanic & (1 << MECHANIC_CHARM)) - return DIMINISHING_MIND_CONTROL; - if (mechanic & (1 << MECHANIC_SILENCE)) - return DIMINISHING_SILENCE; - if (mechanic & (1 << MECHANIC_SLEEP)) - return DIMINISHING_SLEEP; - if (mechanic & ((1 << MECHANIC_SAPPED) | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_SHACKLE))) - return DIMINISHING_DISORIENT; - // Mechanic Knockout, except Blast Wave - if (mechanic & (1 << MECHANIC_KNOCKOUT) && spellproto->SpellIconID != 292) - return DIMINISHING_DISORIENT; - if (mechanic & (1 << MECHANIC_DISARM)) - return DIMINISHING_DISARM; - if (mechanic & (1 << MECHANIC_FEAR)) - return DIMINISHING_FEAR; - if (mechanic & (1 << MECHANIC_STUN)) - return triggered ? DIMINISHING_STUN : DIMINISHING_CONTROLLED_STUN; - if (mechanic & (1 << MECHANIC_BANISH)) - return DIMINISHING_BANISH; - if (mechanic & (1 << MECHANIC_ROOT)) - return triggered ? DIMINISHING_ROOT : DIMINISHING_CONTROLLED_ROOT; - if (mechanic & (1 << MECHANIC_HORROR)) - return DIMINISHING_HORROR; - - return DIMINISHING_NONE; -} - -DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group) -{ - switch (group) - { - case DIMINISHING_TAUNT: - case DIMINISHING_CONTROLLED_STUN: - case DIMINISHING_STUN: - case DIMINISHING_OPENING_STUN: - case DIMINISHING_CYCLONE: - case DIMINISHING_CHARGE: - return DRTYPE_ALL; - case DIMINISHING_LIMITONLY: - case DIMINISHING_NONE: - return DRTYPE_NONE; - default: - return DRTYPE_PLAYER; - } -} - -DiminishingLevels GetDiminishingReturnsMaxLevel(DiminishingGroup group) -{ - switch (group) - { - case DIMINISHING_TAUNT: - return DIMINISHING_LEVEL_TAUNT_IMMUNE; - default: - return DIMINISHING_LEVEL_IMMUNE; - } -} - -int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellInfo const* spellproto) -{ - if (!IsDiminishingReturnsGroupDurationLimited(group)) - return 0; - - // Explicit diminishing duration - switch (spellproto->SpellFamilyName) - { - case SPELLFAMILY_DRUID: - { - // Faerie Fire - limit to 40 seconds in PvP (3.1) - if (spellproto->SpellFamilyFlags[0] & 0x400) - return 40 * IN_MILLISECONDS; - break; - } - case SPELLFAMILY_HUNTER: - { - // Wyvern Sting - if (spellproto->SpellFamilyFlags[1] & 0x1000) - return 6 * IN_MILLISECONDS; - // Hunter's Mark - if (spellproto->SpellFamilyFlags[0] & 0x400) - return 120 * IN_MILLISECONDS; - break; - } - case SPELLFAMILY_PALADIN: - { - // Repentance - limit to 6 seconds in PvP - if (spellproto->SpellFamilyFlags[0] & 0x4) - return 6 * IN_MILLISECONDS; - break; - } - case SPELLFAMILY_WARLOCK: - { - // Banish - limit to 6 seconds in PvP - if (spellproto->SpellFamilyFlags[1] & 0x8000000) - return 6 * IN_MILLISECONDS; - // Curse of Tongues - limit to 12 seconds in PvP - else if (spellproto->SpellFamilyFlags[2] & 0x800) - return 12 * IN_MILLISECONDS; - // Curse of Elements - limit to 120 seconds in PvP - else if (spellproto->SpellFamilyFlags[1] & 0x200) - return 120 * IN_MILLISECONDS; - break; - } - default: - break; - } - - return 10 * IN_MILLISECONDS; -} - -bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group) -{ - switch (group) - { - case DIMINISHING_BANISH: - case DIMINISHING_CONTROLLED_STUN: - case DIMINISHING_CONTROLLED_ROOT: - case DIMINISHING_CYCLONE: - case DIMINISHING_DISORIENT: - case DIMINISHING_ENTRAPMENT: - case DIMINISHING_FEAR: - case DIMINISHING_HORROR: - case DIMINISHING_MIND_CONTROL: - case DIMINISHING_OPENING_STUN: - case DIMINISHING_ROOT: - case DIMINISHING_STUN: - case DIMINISHING_SLEEP: - case DIMINISHING_LIMITONLY: - return true; - default: - return false; - } -} - SpellMgr::SpellMgr() { } SpellMgr::~SpellMgr() @@ -811,7 +515,8 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE return true; // do triggered cast checks - if (!(procEntry.AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC)) + // Do not consider autoattacks as triggered spells + if (!(procEntry.AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC) && !(eventInfo.GetTypeMask() & AUTO_ATTACK_PROC_FLAG_MASK)) { if (Spell const* spell = eventInfo.GetProcSpell()) { @@ -830,20 +535,13 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE return false; // check spell family name/flags (if set) for spells - if (eventInfo.GetTypeMask() & (PERIODIC_PROC_FLAG_MASK | SPELL_PROC_FLAG_MASK | PROC_FLAG_DONE_TRAP_ACTIVATION)) + if (eventInfo.GetTypeMask() & (PERIODIC_PROC_FLAG_MASK | SPELL_PROC_FLAG_MASK)) { - SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo(); - - if (procEntry.SpellFamilyName && eventSpellInfo && (procEntry.SpellFamilyName != eventSpellInfo->SpellFamilyName)) - return false; - - if (procEntry.SpellFamilyMask && eventSpellInfo && !(procEntry.SpellFamilyMask & eventSpellInfo->SpellFamilyFlags)) - return false; - } + if (SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo()) + if (!eventSpellInfo->IsAffected(procEntry.SpellFamilyName, procEntry.SpellFamilyMask)) + return false; - // check spell type mask (if set) - if (eventInfo.GetTypeMask() & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)) - { + // check spell type mask (if set) if (procEntry.SpellTypeMask && !(eventInfo.GetSpellTypeMask() & procEntry.SpellTypeMask)) return false; } @@ -1707,71 +1405,6 @@ void SpellMgr::LoadSpellGroupStackRules() TC_LOG_INFO("server.loading", ">> Loaded %u spell group stack rules in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } -// Used for prepare can/can't triggr aura -static bool InitTriggerAuraData(); -// Define can trigger auras -static bool isTriggerAura[TOTAL_AURAS]; -// Triggered always, even from triggered spells -static bool isAlwaysTriggeredAura[TOTAL_AURAS]; -// Prepare lists -static bool procPrepared = InitTriggerAuraData(); - -// List of auras that CAN be trigger but may not exist in spell_proc_event -// in most case need for drop charges -// in some types of aura need do additional check -// for example SPELL_AURA_MECHANIC_IMMUNITY - need check for mechanic -bool InitTriggerAuraData() -{ - for (uint16 i = 0; i < TOTAL_AURAS; ++i) - { - isTriggerAura[i] = false; - isAlwaysTriggeredAura[i] = false; - } - isTriggerAura[SPELL_AURA_DUMMY] = true; // Most dummy auras should require scripting. Remove? - isTriggerAura[SPELL_AURA_MOD_CONFUSE] = true; // "Any direct damaging attack will revive targets" - isTriggerAura[SPELL_AURA_MOD_THREAT] = true; // Only one spell: 28762 part of Mage T3 8p bonus - isTriggerAura[SPELL_AURA_MOD_STUN] = true; // Aura does not have charges but needs to be removed on trigger - isTriggerAura[SPELL_AURA_MOD_DAMAGE_DONE] = true; - isTriggerAura[SPELL_AURA_MOD_DAMAGE_TAKEN] = true; - isTriggerAura[SPELL_AURA_MOD_RESISTANCE] = true; - isTriggerAura[SPELL_AURA_MOD_STEALTH] = true; - isTriggerAura[SPELL_AURA_MOD_FEAR] = true; // Aura does not have charges but needs to be removed on trigger - isTriggerAura[SPELL_AURA_MOD_ROOT] = true; - isTriggerAura[SPELL_AURA_TRANSFORM] = true; - isTriggerAura[SPELL_AURA_REFLECT_SPELLS] = true; - isTriggerAura[SPELL_AURA_DAMAGE_IMMUNITY] = true; - isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL] = true; - isTriggerAura[SPELL_AURA_PROC_TRIGGER_DAMAGE] = true; - isTriggerAura[SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK] = true; - isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT] = true; - isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL] = true; - isTriggerAura[SPELL_AURA_REFLECT_SPELLS_SCHOOL] = true; - isTriggerAura[SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN] = true; - isTriggerAura[SPELL_AURA_MOD_ATTACK_POWER] = true; - isTriggerAura[SPELL_AURA_ADD_CASTER_HIT_TRIGGER] = true; - isTriggerAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true; - isTriggerAura[SPELL_AURA_MOD_MELEE_HASTE] = true; - isTriggerAura[SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE] = true; - isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE] = true; - isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE] = true; - isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE] = true; - isTriggerAura[SPELL_AURA_MOD_SPELL_CRIT_CHANCE] = true; - isTriggerAura[SPELL_AURA_ADD_FLAT_MODIFIER] = true; - isTriggerAura[SPELL_AURA_ADD_PCT_MODIFIER] = true; - isTriggerAura[SPELL_AURA_ABILITY_IGNORE_AURASTATE] = true; - - isAlwaysTriggeredAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true; - isAlwaysTriggeredAura[SPELL_AURA_MOD_FEAR] = true; - isAlwaysTriggeredAura[SPELL_AURA_MOD_ROOT] = true; - isAlwaysTriggeredAura[SPELL_AURA_MOD_STUN] = true; - isAlwaysTriggeredAura[SPELL_AURA_TRANSFORM] = true; - isAlwaysTriggeredAura[SPELL_AURA_SPELL_MAGNET] = true; - isAlwaysTriggeredAura[SPELL_AURA_SCHOOL_ABSORB] = true; - isAlwaysTriggeredAura[SPELL_AURA_MOD_STEALTH] = true; - - return true; -} - void SpellMgr::LoadSpellProcs() { uint32 oldMSTime = getMSTime(); @@ -1900,6 +1533,73 @@ void SpellMgr::LoadSpellProcs() TC_LOG_INFO("server.loading", ">> Loaded %u spell proc conditions and data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + // Define can trigger auras + bool isTriggerAura[TOTAL_AURAS]; + // Triggered always, even from triggered spells + bool isAlwaysTriggeredAura[TOTAL_AURAS]; + // SpellTypeMask to add to the proc + uint32 spellTypeMask[TOTAL_AURAS]; + + // List of auras that CAN trigger but may not exist in spell_proc + // in most cases needed to drop charges + + // some aura types need additional checks (eg SPELL_AURA_MECHANIC_IMMUNITY needs mechanic check) + // see AuraEffect::CheckEffectProc + for (uint16 i = 0; i < TOTAL_AURAS; ++i) + { + isTriggerAura[i] = false; + isAlwaysTriggeredAura[i] = false; + spellTypeMask[i] = PROC_SPELL_TYPE_MASK_ALL; + } + + isTriggerAura[SPELL_AURA_DUMMY] = true; // Most dummy auras should require scripting, but there are some exceptions (ie 12311) + isTriggerAura[SPELL_AURA_MOD_CONFUSE] = true; // "Any direct damaging attack will revive targets" + isTriggerAura[SPELL_AURA_MOD_THREAT] = true; // Only one spell: 28762 part of Mage T3 8p bonus + isTriggerAura[SPELL_AURA_MOD_STUN] = true; // Aura does not have charges but needs to be removed on trigger + isTriggerAura[SPELL_AURA_MOD_DAMAGE_DONE] = true; + isTriggerAura[SPELL_AURA_MOD_DAMAGE_TAKEN] = true; + isTriggerAura[SPELL_AURA_MOD_RESISTANCE] = true; + isTriggerAura[SPELL_AURA_MOD_STEALTH] = true; + isTriggerAura[SPELL_AURA_MOD_FEAR] = true; // Aura does not have charges but needs to be removed on trigger + isTriggerAura[SPELL_AURA_MOD_ROOT] = true; + isTriggerAura[SPELL_AURA_TRANSFORM] = true; + isTriggerAura[SPELL_AURA_REFLECT_SPELLS] = true; + isTriggerAura[SPELL_AURA_DAMAGE_IMMUNITY] = true; + isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL] = true; + isTriggerAura[SPELL_AURA_PROC_TRIGGER_DAMAGE] = true; + isTriggerAura[SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK] = true; + isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT] = true; + isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL] = true; + isTriggerAura[SPELL_AURA_REFLECT_SPELLS_SCHOOL] = true; + isTriggerAura[SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN] = true; + isTriggerAura[SPELL_AURA_MOD_ATTACK_POWER] = true; + isTriggerAura[SPELL_AURA_ADD_CASTER_HIT_TRIGGER] = true; + isTriggerAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true; + isTriggerAura[SPELL_AURA_MOD_MELEE_HASTE] = true; + isTriggerAura[SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE] = true; + isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE] = true; + isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE] = true; + isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE] = true; + isTriggerAura[SPELL_AURA_MOD_SPELL_CRIT_CHANCE] = true; + isTriggerAura[SPELL_AURA_ADD_FLAT_MODIFIER] = true; + isTriggerAura[SPELL_AURA_ADD_PCT_MODIFIER] = true; + isTriggerAura[SPELL_AURA_ABILITY_IGNORE_AURASTATE] = true; + + isAlwaysTriggeredAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_STEALTH] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_CONFUSE] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_FEAR] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_ROOT] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_STUN] = true; + isAlwaysTriggeredAura[SPELL_AURA_TRANSFORM] = true; + + spellTypeMask[SPELL_AURA_MOD_STEALTH] = PROC_SPELL_TYPE_DAMAGE | PROC_SPELL_TYPE_NO_DMG_HEAL; + spellTypeMask[SPELL_AURA_MOD_CONFUSE] = PROC_SPELL_TYPE_DAMAGE; + spellTypeMask[SPELL_AURA_MOD_FEAR] = PROC_SPELL_TYPE_DAMAGE; + spellTypeMask[SPELL_AURA_MOD_ROOT] = PROC_SPELL_TYPE_DAMAGE; + spellTypeMask[SPELL_AURA_MOD_STUN] = PROC_SPELL_TYPE_DAMAGE; + spellTypeMask[SPELL_AURA_TRANSFORM] = PROC_SPELL_TYPE_DAMAGE; + // This generates default procs to retain compatibility with previous proc system TC_LOG_INFO("server.loading", "Generating spell proc data from SpellMap..."); count = 0; @@ -1910,10 +1610,16 @@ void SpellMgr::LoadSpellProcs() if (!spellInfo) continue; + // Data already present in DB, overwrites default proc if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end()) continue; - bool found = false, addTriggerFlag = false; + // Nothing to do if no flags set + if (!spellInfo->ProcFlags) + continue; + + bool addTriggerFlag = false; + uint32 procSpellTypeMask = PROC_SPELL_TYPE_NONE; for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { if (!spellInfo->Effects[i].IsEffect()) @@ -1926,28 +1632,42 @@ void SpellMgr::LoadSpellProcs() if (!isTriggerAura[auraName]) continue; - found = true; - - if (!addTriggerFlag && isAlwaysTriggeredAura[auraName]) + procSpellTypeMask |= spellTypeMask[auraName]; + if (isAlwaysTriggeredAura[auraName]) addTriggerFlag = true; + + // many proc auras with taken procFlag mask don't have attribute "can proc with triggered" + // they should proc nevertheless (example mage armor spells with judgement) + if (!addTriggerFlag && (spellInfo->ProcFlags & TAKEN_HIT_PROC_FLAG_MASK) != 0) + { + switch (auraName) + { + case SPELL_AURA_PROC_TRIGGER_SPELL: + case SPELL_AURA_PROC_TRIGGER_DAMAGE: + addTriggerFlag = true; + break; + default: + break; + } + } break; } - if (!found) - continue; - - if (!spellInfo->ProcFlags) + if (!procSpellTypeMask) continue; SpellProcEntry procEntry; procEntry.SchoolMask = 0; - procEntry.SpellFamilyName = spellInfo->SpellFamilyName; procEntry.ProcFlags = spellInfo->ProcFlags; + procEntry.SpellFamilyName = 0; for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (spellInfo->Effects[i].IsEffect() && isTriggerAura[spellInfo->Effects[i].ApplyAuraName]) procEntry.SpellFamilyMask |= spellInfo->Effects[i].SpellClassMask; - procEntry.SpellTypeMask = PROC_SPELL_TYPE_MASK_ALL; + if (procEntry.SpellFamilyMask) + procEntry.SpellFamilyName = spellInfo->SpellFamilyName; + + procEntry.SpellTypeMask = procSpellTypeMask; procEntry.SpellPhaseMask = PROC_SPELL_PHASE_HIT; procEntry.HitMask = PROC_HIT_NONE; // uses default proc @see SpellMgr::CanSpellTriggerProcOnEvent @@ -2895,6 +2615,8 @@ void SpellMgr::LoadSpellInfoCorrections() case 2895: // Wrath of Air Totem rank 1 (Aura) case 68933: // Wrath of Air Totem rank 2 (Aura) case 29200: // Purify Helboar Meat + case 10872: // Abolish Disease Effect + case 3137: // Abolish Poison Effect spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CASTER); spellInfo->Effects[EFFECT_0].TargetB = SpellImplicitTargetInfo(); break; @@ -2922,6 +2644,15 @@ void SpellMgr::LoadSpellInfoCorrections() // because of bug in dbc spellInfo->ProcChance = 0; break; + case 51528: // Maelstrom Weapon (Rank 1) + case 51529: // Maelstrom Weapon (Rank 2) + case 51530: // Maelstrom Weapon (Rank 3) + case 51531: // Maelstrom Weapon (Rank 4) + case 51532: // Maelstrom Weapon (Rank 5) + // due to discrepancies between ranks + spellInfo->EquippedItemSubClassMask = 0x0000FC33; + spellInfo->AttributesEx3 |= SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED; + break; case 20335: // Heart of the Crusader case 20336: case 20337: @@ -3004,6 +2735,12 @@ void SpellMgr::LoadSpellInfoCorrections() case 53385: // Divine Storm (Damage) spellInfo->MaxAffectedTargets = 4; break; + case 56342: // Lock and Load (Rank 1) + // @workaround: Delete dummy effect from rank 1, + // effect apply aura has TargetA == TargetB == 0 but core still applies it to caster + // core bug? + spellInfo->Effects[EFFECT_2].Effect = 0; + break; case 53480: // Roar of Sacrifice // missing spell effect 2 data, taken from 4.3.4 spellInfo->Effects[EFFECT_1].Effect = SPELL_EFFECT_APPLY_AURA; @@ -3041,15 +2778,14 @@ void SpellMgr::LoadSpellInfoCorrections() case 44544: // Fingers of Frost spellInfo->Effects[EFFECT_0].SpellClassMask = flag96(685904631, 1151048, 0); break; - case 53257: // Cobra Strikes - spellInfo->ProcCharges = 2; - spellInfo->StackAmount = 0; - break; case 49224: // Magic Suppression - DK case 49610: // Magic Suppression - DK case 49611: // Magic Suppression - DK spellInfo->ProcCharges = 0; break; + case 52212: // Death and Decay + spellInfo->AttributesEx6 |= SPELL_ATTR6_CAN_TARGET_INVISIBLE; + break; case 37408: // Oscillation Field spellInfo->AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS; break; @@ -3129,6 +2865,19 @@ void SpellMgr::LoadSpellInfoCorrections() case 27915: // Anchor to Skulls case 27931: // Anchor to Skulls case 27937: // Anchor to Skulls + case 16177: // Ancestral Fortitude (Rank 1) + case 16236: // Ancestral Fortitude (Rank 2) + case 16237: // Ancestral Fortitude (Rank 3) + case 47930: // Grace + case 45145: // Snake Trap Effect (Rank 1) + case 13812: // Explosive Trap Effect (Rank 1) + case 14314: // Explosive Trap Effect (Rank 2) + case 14315: // Explosive Trap Effect (Rank 3) + case 27026: // Explosive Trap Effect (Rank 4) + case 49064: // Explosive Trap Effect (Rank 5) + case 49065: // Explosive Trap Effect (Rank 6) + case 43446: // Explosive Trap Effect (Hexlord Malacrass) + case 68979: // Unleashed Souls spellInfo->RangeEntry = sSpellRangeStore.LookupEntry(13); break; // target allys instead of enemies, target A is src_caster, spells with effect like that have ally target @@ -3236,9 +2985,6 @@ void SpellMgr::LoadSpellInfoCorrections() case 71839: // Drain Life - Bryntroll Heroic spellInfo->AttributesEx2 |= SPELL_ATTR2_CANT_CRIT; break; - case 34471: // The Beast Within - spellInfo->AttributesEx5 |= SPELL_ATTR5_USABLE_WHILE_CONFUSED | SPELL_ATTR5_USABLE_WHILE_FEARED | SPELL_ATTR5_USABLE_WHILE_STUNNED; - break; case 56606: // Ride Jokkum case 61791: // Ride Vehicle (Yogg-Saron) /// @todo: remove this when basepoints of all Ride Vehicle auras are calculated correctly @@ -3277,8 +3023,23 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->InterruptFlags &= ~AURA_INTERRUPT_FLAG_CAST; break; case 42767: // Sic'em + case 43092: // Stop the Ascension!: Halfdan's Soul Destruction spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_NEARBY_ENTRY); break; + case 14621: // Polymorph (Six Demon Bag) + spellInfo->RangeEntry = sSpellRangeStore.LookupEntry(4); // Medium Range + break; + case 35101: // Concussive Barrage + spellInfo->RangeEntry = sSpellRangeStore.LookupEntry(155); // Hunter Range (Long) + break; + case 55741: // Desecration (Rank 1) + case 68766: // Desecration (Rank 2) + spellInfo->RangeEntry = sSpellRangeStore.LookupEntry(2); // Melee Range + break; + case 46946: // Safeguard (Rank 1) + case 46947: // Safeguard (Rank 2) + spellInfo->RangeEntry = sSpellRangeStore.LookupEntry(34); // Twenty-Five yards + break; // VIOLET HOLD SPELLS // case 54258: // Water Globule (Ichoron) @@ -3748,3 +3509,33 @@ void SpellMgr::LoadSpellInfoSpellSpecificAndAuraState() TC_LOG_INFO("server.loading", ">> Loaded SpellInfo SpellSpecific and AuraState in %u ms", GetMSTimeDiffToNow(oldMSTime)); } + +void SpellMgr::LoadSpellInfoDiminishing() +{ + uint32 oldMSTime = getMSTime(); + + for (SpellInfo* spellInfo : mSpellInfoMap) + { + if (!spellInfo) + continue; + + spellInfo->_LoadSpellDiminishInfo(); + } + + TC_LOG_INFO("server.loading", ">> Loaded SpellInfo diminishing infos in %u ms", GetMSTimeDiffToNow(oldMSTime)); +} + +void SpellMgr::LoadSpellInfoImmunities() +{ + uint32 oldMSTime = getMSTime(); + + for (SpellInfo* spellInfo : mSpellInfoMap) + { + if (!spellInfo) + continue; + + spellInfo->_LoadImmunityInfo(); + } + + TC_LOG_INFO("server.loading", ">> Loaded SpellInfo immunity infos in %u ms", GetMSTimeDiffToNow(oldMSTime)); +} diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index 52ea6f25d1f..1304ac8cacd 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -160,13 +160,13 @@ enum ProcFlags | PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS | PROC_FLAG_TAKEN_SPELL_RANGED_DMG_CLASS, SPELL_PROC_FLAG_MASK = PROC_FLAG_DONE_SPELL_MELEE_DMG_CLASS | PROC_FLAG_TAKEN_SPELL_MELEE_DMG_CLASS + | PROC_FLAG_DONE_RANGED_AUTO_ATTACK | PROC_FLAG_TAKEN_RANGED_AUTO_ATTACK | PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS | PROC_FLAG_TAKEN_SPELL_RANGED_DMG_CLASS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG | PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS - | PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, - - SPELL_CAST_PROC_FLAG_MASK = SPELL_PROC_FLAG_MASK | PROC_FLAG_DONE_TRAP_ACTIVATION | RANGED_PROC_FLAG_MASK, + | PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG + | PROC_FLAG_DONE_TRAP_ACTIVATION, PERIODIC_PROC_FLAG_MASK = PROC_FLAG_DONE_PERIODIC | PROC_FLAG_TAKEN_PERIODIC, @@ -174,7 +174,8 @@ enum ProcFlags | PROC_FLAG_DONE_SPELL_MELEE_DMG_CLASS | PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG | PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG - | PROC_FLAG_DONE_PERIODIC | PROC_FLAG_DONE_MAINHAND_ATTACK | PROC_FLAG_DONE_OFFHAND_ATTACK, + | PROC_FLAG_DONE_PERIODIC | PROC_FLAG_DONE_TRAP_ACTIVATION + | PROC_FLAG_DONE_MAINHAND_ATTACK | PROC_FLAG_DONE_OFFHAND_ATTACK, TAKEN_HIT_PROC_FLAG_MASK = PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK | PROC_FLAG_TAKEN_RANGED_AUTO_ATTACK | PROC_FLAG_TAKEN_SPELL_MELEE_DMG_CLASS | PROC_FLAG_TAKEN_SPELL_RANGED_DMG_CLASS @@ -546,13 +547,6 @@ inline bool IsProfessionOrRidingSkill(uint32 skill) bool IsPartOfSkillLine(uint32 skillId, uint32 spellId); -// spell diminishing returns -TC_GAME_API DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellInfo const* spellproto, bool triggered); -TC_GAME_API DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group); -TC_GAME_API DiminishingLevels GetDiminishingReturnsMaxLevel(DiminishingGroup group); -TC_GAME_API int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellInfo const* spellproto); -TC_GAME_API bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group); - class TC_GAME_API SpellMgr { // Constructors @@ -685,6 +679,8 @@ class TC_GAME_API SpellMgr void LoadSpellInfoCustomAttributes(); void LoadSpellInfoCorrections(); void LoadSpellInfoSpellSpecificAndAuraState(); + void LoadSpellInfoDiminishing(); + void LoadSpellInfoImmunities(); private: SpellDifficultySearcherMap mSpellDifficultySearcherMap; diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp index 67aaa582776..950456394d8 100644 --- a/src/server/game/Spells/SpellScript.cpp +++ b/src/server/game/Spells/SpellScript.cpp @@ -301,35 +301,35 @@ void SpellScript::DestinationTargetSelectHandler::Call(SpellScript* spellScript, bool SpellScript::_Validate(SpellInfo const* entry) { - for (std::list<EffectHandler>::iterator itr = OnEffectLaunch.begin(); itr != OnEffectLaunch.end(); ++itr) + for (auto itr = OnEffectLaunch.begin(); itr != OnEffectLaunch.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectLaunch` of SpellScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectHandler>::iterator itr = OnEffectLaunchTarget.begin(); itr != OnEffectLaunchTarget.end(); ++itr) + for (auto itr = OnEffectLaunchTarget.begin(); itr != OnEffectLaunchTarget.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectLaunchTarget` of SpellScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectHandler>::iterator itr = OnEffectHit.begin(); itr != OnEffectHit.end(); ++itr) + for (auto itr = OnEffectHit.begin(); itr != OnEffectHit.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectHit` of SpellScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectHandler>::iterator itr = OnEffectHitTarget.begin(); itr != OnEffectHitTarget.end(); ++itr) + for (auto itr = OnEffectHitTarget.begin(); itr != OnEffectHitTarget.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectHitTarget` of SpellScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectHandler>::iterator itr = OnEffectSuccessfulDispel.begin(); itr != OnEffectSuccessfulDispel.end(); ++itr) + for (auto itr = OnEffectSuccessfulDispel.begin(); itr != OnEffectSuccessfulDispel.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectSuccessfulDispel` of SpellScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<ObjectAreaTargetSelectHandler>::iterator itr = OnObjectAreaTargetSelect.begin(); itr != OnObjectAreaTargetSelect.end(); ++itr) + for (auto itr = OnObjectAreaTargetSelect.begin(); itr != OnObjectAreaTargetSelect.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnObjectAreaTargetSelect` of SpellScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<ObjectTargetSelectHandler>::iterator itr = OnObjectTargetSelect.begin(); itr != OnObjectTargetSelect.end(); ++itr) + for (auto itr = OnObjectTargetSelect.begin(); itr != OnObjectTargetSelect.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnObjectTargetSelect` of SpellScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<DestinationTargetSelectHandler>::iterator itr = OnDestinationTargetSelect.begin(); itr != OnDestinationTargetSelect.end(); ++itr) + for (auto itr = OnDestinationTargetSelect.begin(); itr != OnDestinationTargetSelect.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnDestinationTargetSelect` of SpellScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); @@ -624,9 +624,9 @@ SpellInfo const* SpellScript::GetTriggeringSpell() return m_spell->m_triggeredByAuraSpell; } -void SpellScript::FinishCast(SpellCastResult result) +void SpellScript::FinishCast(SpellCastResult result, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) { - m_spell->SendCastResult(result); + m_spell->SendCastResult(result, param1, param2); m_spell->finish(result == SPELL_CAST_OK); } @@ -648,99 +648,99 @@ SpellValue const* SpellScript::GetSpellValue() bool AuraScript::_Validate(SpellInfo const* entry) { - for (std::list<CheckAreaTargetHandler>::iterator itr = DoCheckAreaTarget.begin(); itr != DoCheckAreaTarget.end(); ++itr) + for (auto itr = DoCheckAreaTarget.begin(); itr != DoCheckAreaTarget.end(); ++itr) if (!entry->HasAreaAuraEffect() && !entry->HasEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA) && !entry->HasEffect(SPELL_EFFECT_APPLY_AURA)) TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `DoCheckAreaTarget` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); - for (std::list<AuraDispelHandler>::iterator itr = OnDispel.begin(); itr != OnDispel.end(); ++itr) + for (auto itr = OnDispel.begin(); itr != OnDispel.end(); ++itr) if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `OnDispel` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); - for (std::list<AuraDispelHandler>::iterator itr = AfterDispel.begin(); itr != AfterDispel.end(); ++itr) + for (auto itr = AfterDispel.begin(); itr != AfterDispel.end(); ++itr) if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `AfterDispel` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); - for (std::list<EffectApplyHandler>::iterator itr = OnEffectApply.begin(); itr != OnEffectApply.end(); ++itr) + for (auto itr = OnEffectApply.begin(); itr != OnEffectApply.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectApply` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectApplyHandler>::iterator itr = OnEffectRemove.begin(); itr != OnEffectRemove.end(); ++itr) + for (auto itr = OnEffectRemove.begin(); itr != OnEffectRemove.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectRemove` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectApplyHandler>::iterator itr = AfterEffectApply.begin(); itr != AfterEffectApply.end(); ++itr) + for (auto itr = AfterEffectApply.begin(); itr != AfterEffectApply.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `AfterEffectApply` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectApplyHandler>::iterator itr = AfterEffectRemove.begin(); itr != AfterEffectRemove.end(); ++itr) + for (auto itr = AfterEffectRemove.begin(); itr != AfterEffectRemove.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `AfterEffectRemove` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectPeriodicHandler>::iterator itr = OnEffectPeriodic.begin(); itr != OnEffectPeriodic.end(); ++itr) + for (auto itr = OnEffectPeriodic.begin(); itr != OnEffectPeriodic.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectPeriodic` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectUpdatePeriodicHandler>::iterator itr = OnEffectUpdatePeriodic.begin(); itr != OnEffectUpdatePeriodic.end(); ++itr) + for (auto itr = OnEffectUpdatePeriodic.begin(); itr != OnEffectUpdatePeriodic.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectUpdatePeriodic` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectCalcAmountHandler>::iterator itr = DoEffectCalcAmount.begin(); itr != DoEffectCalcAmount.end(); ++itr) + for (auto itr = DoEffectCalcAmount.begin(); itr != DoEffectCalcAmount.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `DoEffectCalcAmount` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectCalcPeriodicHandler>::iterator itr = DoEffectCalcPeriodic.begin(); itr != DoEffectCalcPeriodic.end(); ++itr) + for (auto itr = DoEffectCalcPeriodic.begin(); itr != DoEffectCalcPeriodic.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `DoEffectCalcPeriodic` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectCalcSpellModHandler>::iterator itr = DoEffectCalcSpellMod.begin(); itr != DoEffectCalcSpellMod.end(); ++itr) + for (auto itr = DoEffectCalcSpellMod.begin(); itr != DoEffectCalcSpellMod.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `DoEffectCalcSpellMod` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectAbsorbHandler>::iterator itr = OnEffectAbsorb.begin(); itr != OnEffectAbsorb.end(); ++itr) + for (auto itr = OnEffectAbsorb.begin(); itr != OnEffectAbsorb.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectAbsorb` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectAbsorbHandler>::iterator itr = AfterEffectAbsorb.begin(); itr != AfterEffectAbsorb.end(); ++itr) + for (auto itr = AfterEffectAbsorb.begin(); itr != AfterEffectAbsorb.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `AfterEffectAbsorb` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectManaShieldHandler>::iterator itr = OnEffectManaShield.begin(); itr != OnEffectManaShield.end(); ++itr) + for (auto itr = OnEffectManaShield.begin(); itr != OnEffectManaShield.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectManaShield` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectManaShieldHandler>::iterator itr = AfterEffectManaShield.begin(); itr != AfterEffectManaShield.end(); ++itr) + for (auto itr = AfterEffectManaShield.begin(); itr != AfterEffectManaShield.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `AfterEffectManaShield` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectSplitHandler>::iterator itr = OnEffectSplit.begin(); itr != OnEffectSplit.end(); ++itr) + for (auto itr = OnEffectSplit.begin(); itr != OnEffectSplit.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectSplit` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<CheckProcHandler>::iterator itr = DoCheckProc.begin(); itr != DoCheckProc.end(); ++itr) + for (auto itr = DoCheckProc.begin(); itr != DoCheckProc.end(); ++itr) if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `DoCheckProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); - for (std::list<CheckEffectProcHandler>::iterator itr = DoCheckEffectProc.begin(); itr != DoCheckEffectProc.end(); ++itr) + for (auto itr = DoCheckEffectProc.begin(); itr != DoCheckEffectProc.end(); ++itr) if (!itr->GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `DoCheckEffectProc` of AuraScript won't be executed", entry->Id, itr->ToString().c_str(), m_scriptName->c_str()); - for (std::list<AuraProcHandler>::iterator itr = DoPrepareProc.begin(); itr != DoPrepareProc.end(); ++itr) + for (auto itr = DoPrepareProc.begin(); itr != DoPrepareProc.end(); ++itr) if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `DoPrepareProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); - for (std::list<AuraProcHandler>::iterator itr = OnProc.begin(); itr != OnProc.end(); ++itr) + for (auto itr = OnProc.begin(); itr != OnProc.end(); ++itr) if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `OnProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); - for (std::list<AuraProcHandler>::iterator itr = AfterProc.begin(); itr != AfterProc.end(); ++itr) + for (auto itr = AfterProc.begin(); itr != AfterProc.end(); ++itr) if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `AfterProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); - for (std::list<EffectProcHandler>::iterator itr = OnEffectProc.begin(); itr != OnEffectProc.end(); ++itr) + for (auto itr = OnEffectProc.begin(); itr != OnEffectProc.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectProc` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); - for (std::list<EffectProcHandler>::iterator itr = AfterEffectProc.begin(); itr != AfterEffectProc.end(); ++itr) + for (auto itr = AfterEffectProc.begin(); itr != AfterEffectProc.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `AfterEffectProc` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index 1ec10f03820..af30b6a7879 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -63,9 +63,9 @@ class TC_GAME_API _SpellScript public: _SpellScript() : m_currentScriptState(SPELL_SCRIPT_STATE_NONE), m_scriptName(NULL), m_scriptSpellId(0) {} virtual ~_SpellScript() { } - virtual void _Register(); - virtual void _Unload(); - virtual void _Init(std::string const* scriptname, uint32 spellId); + void _Register(); + void _Unload(); + void _Init(std::string const* scriptname, uint32 spellId); std::string const* _GetScriptName() const; protected: @@ -441,7 +441,7 @@ class TC_GAME_API SpellScript : public _SpellScript SpellInfo const* GetTriggeringSpell(); // finishes spellcast prematurely with selected error message - void FinishCast(SpellCastResult result); + void FinishCast(SpellCastResult result, uint32* param1 = nullptr, uint32* param2 = nullptr); void SetCustomCastResultMessage(SpellCustomErrors result); }; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 443d0c63efa..7bbf73d028d 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1449,8 +1449,11 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading SpellInfo custom attributes..."); sSpellMgr->LoadSpellInfoCustomAttributes(); - TC_LOG_INFO("server.loading", "Loading SpellInfo SpellSpecific and AuraState..."); - sSpellMgr->LoadSpellInfoSpellSpecificAndAuraState(); + TC_LOG_INFO("server.loading", "Loading SpellInfo diminishing infos..."); + sSpellMgr->LoadSpellInfoDiminishing(); + + TC_LOG_INFO("server.loading", "Loading SpellInfo immunity infos..."); + sSpellMgr->LoadSpellInfoImmunities(); TC_LOG_INFO("server.loading", "Loading GameObject models..."); LoadGameObjectModelList(m_dataPath); @@ -1511,6 +1514,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Spell Learn Skills..."); sSpellMgr->LoadSpellLearnSkills(); // must be after LoadSpellRanks + TC_LOG_INFO("server.loading", "Loading SpellInfo SpellSpecific and AuraState..."); + sSpellMgr->LoadSpellInfoSpellSpecificAndAuraState(); // must be after LoadSpellRanks + TC_LOG_INFO("server.loading", "Loading Spell Learn Spells..."); sSpellMgr->LoadSpellLearnSpells(); diff --git a/src/server/scripts/Commands/cs_cast.cpp b/src/server/scripts/Commands/cs_cast.cpp index 44c606a360f..45e6c65cc6b 100644 --- a/src/server/scripts/Commands/cs_cast.cpp +++ b/src/server/scripts/Commands/cs_cast.cpp @@ -99,8 +99,7 @@ public: return false; } - bool triggered = (triggeredStr != NULL); - + TriggerCastFlags triggered = (triggeredStr != NULL) ? TRIGGERED_FULL_DEBUG_MASK : TRIGGERED_NONE; handler->GetSession()->GetPlayer()->CastSpell(target, spellId, triggered); return true; @@ -132,8 +131,7 @@ public: return false; } - bool triggered = (triggeredStr != NULL); - + TriggerCastFlags triggered = (triggeredStr != NULL) ? TRIGGERED_FULL_DEBUG_MASK : TRIGGERED_NONE; caster->CastSpell(handler->GetSession()->GetPlayer(), spellId, triggered); return true; @@ -167,8 +165,7 @@ public: return false; } - bool triggered = (triggeredStr != NULL); - + TriggerCastFlags triggered = (triggeredStr != NULL) ? TRIGGERED_FULL_DEBUG_MASK : TRIGGERED_NONE; float x, y, z; handler->GetSession()->GetPlayer()->GetClosePoint(x, y, z, dist); @@ -230,8 +227,7 @@ public: return false; } - bool triggered = (triggeredStr != NULL); - + TriggerCastFlags triggered = (triggeredStr != NULL) ? TRIGGERED_FULL_DEBUG_MASK : TRIGGERED_NONE; caster->CastSpell(caster->GetVictim(), spellId, triggered); return true; @@ -274,8 +270,7 @@ public: return false; } - bool triggered = (triggeredStr != NULL); - + TriggerCastFlags triggered = (triggeredStr != NULL) ? TRIGGERED_FULL_DEBUG_MASK : TRIGGERED_NONE; caster->CastSpell(x, y, z, spellId, triggered); return true; diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 16217fbaea6..1e4daaea2d2 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -2268,19 +2268,20 @@ public: // melee damage by specific school if (!spellStr) { - uint32 absorb = 0; - uint32 resist = 0; + Player* attacker = handler->GetSession()->GetPlayer(); + DamageInfo dmgInfo(attacker, target, damage, nullptr, schoolmask, SPELL_DIRECT_DAMAGE, BASE_ATTACK); + attacker->CalcAbsorbResist(dmgInfo); - handler->GetSession()->GetPlayer()->CalcAbsorbResist(target, schoolmask, SPELL_DIRECT_DAMAGE, damage, &absorb, &resist); - - if (damage <= absorb + resist) + if (!dmgInfo.GetDamage()) return true; - damage -= absorb + resist; + damage = dmgInfo.GetDamage(); - handler->GetSession()->GetPlayer()->DealDamageMods(target, damage, &absorb); - handler->GetSession()->GetPlayer()->DealDamage(target, damage, NULL, DIRECT_DAMAGE, schoolmask, NULL, false); - handler->GetSession()->GetPlayer()->SendAttackStateUpdate (HITINFO_AFFECTS_VICTIM, target, 1, schoolmask, damage, absorb, resist, VICTIMSTATE_HIT, 0); + uint32 absorb = dmgInfo.GetAbsorb(); + uint32 resist = dmgInfo.GetResist(); + attacker->DealDamageMods(target, damage, &absorb); + attacker->DealDamage(target, damage, nullptr, DIRECT_DAMAGE, schoolmask, nullptr, false); + attacker->SendAttackStateUpdate(HITINFO_AFFECTS_VICTIM, target, 0, schoolmask, damage, absorb, resist, VICTIMSTATE_HIT, 0); return true; } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h index 446cc48d2f7..2c0f16c4ff2 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h @@ -48,7 +48,8 @@ enum DataTypes DATA_HALL_RUNE_4 = 19, DATA_HALL_RUNE_5 = 20, DATA_HALL_RUNE_6 = 21, - DATA_HALL_RUNE_7 = 22 + DATA_HALL_RUNE_7 = 22, + DATA_SCARSHIELD_INFILTRATOR = 23 }; enum CreaturesIds @@ -71,7 +72,8 @@ enum CreaturesIds NPC_BLACKHAND_SUMMONER = 9818, NPC_BLACKHAND_VETERAN = 9819, NPC_BLACKHAND_INCARCERATOR = 10316, - NPC_LORD_VICTOR_NEFARIUS = 10162 + NPC_LORD_VICTOR_NEFARIUS = 10162, + NPC_SCARSHIELD_INFILTRATOR = 10299 }; enum AdditionalData diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp index bbe8fda37eb..89617a9f4ef 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp @@ -109,6 +109,9 @@ public: if (GetBossState(DATA_GYTH) == DONE) creature->DisappearAndDie(); break; + case NPC_SCARSHIELD_INFILTRATOR: + ScarshieldInfiltrator = creature->GetGUID(); + break; } } @@ -318,6 +321,8 @@ public: return TheBeast; case DATA_GENERAL_DRAKKISATH: return GeneralDrakkisath; + case DATA_SCARSHIELD_INFILTRATOR: + return ScarshieldInfiltrator; case GO_EMBERSEER_IN: return go_emberseerin; case GO_DOORS: @@ -496,6 +501,7 @@ public: ObjectGuid LordVictorNefarius; ObjectGuid TheBeast; ObjectGuid GeneralDrakkisath; + ObjectGuid ScarshieldInfiltrator; ObjectGuid go_emberseerin; ObjectGuid go_doors; ObjectGuid go_emberseerout; @@ -565,9 +571,33 @@ public: } }; +class at_nearby_scarshield_infiltrator : public AreaTriggerScript +{ +public: + at_nearby_scarshield_infiltrator() : AreaTriggerScript("at_nearby_scarshield_infiltrator") { } + + bool OnTrigger(Player* player, const AreaTriggerEntry* /*at*/) override + { + if (player->IsAlive()) + if (InstanceScript* instance = player->GetInstanceScript()) + if (Creature* infiltrator = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_SCARSHIELD_INFILTRATOR))) + { + if (player->getLevel() >= 57) + infiltrator->AI()->SetData(1, 1); + else if (infiltrator->GetEntry() == NPC_SCARSHIELD_INFILTRATOR) + infiltrator->AI()->Talk(0, player); + + return true; + } + + return false; + } +}; + void AddSC_instance_blackrock_spire() { new instance_blackrock_spire(); new at_dragonspire_hall(); new at_blackrock_stadium(); + new at_nearby_scarshield_infiltrator(); } diff --git a/src/server/scripts/EasternKingdoms/ShadowfangKeep/shadowfang_keep.cpp b/src/server/scripts/EasternKingdoms/ShadowfangKeep/shadowfang_keep.cpp index d0430ebb3e5..67bda699643 100644 --- a/src/server/scripts/EasternKingdoms/ShadowfangKeep/shadowfang_keep.cpp +++ b/src/server/scripts/EasternKingdoms/ShadowfangKeep/shadowfang_keep.cpp @@ -54,7 +54,6 @@ enum Yells enum Spells { SPELL_UNLOCK = 6421, - SPELL_DARK_OFFERING = 7154 }; @@ -205,6 +204,123 @@ public: }; +enum ArugalSpells +{ + SPELL_TELE_UPPER = 7587, + SPELL_TELE_SPAWN = 7586, + SPELL_TELE_STAIRS = 7136, + NUM_TELEPORT_SPELLS = 3, + SPELL_ARUGAL_CURSE = 7621, + SPELL_THUNDERSHOCK = 7803, + SPELL_VOIDBOLT = 7588 +}; + +enum ArugalTexts +{ + SAY_AGGRO = 1, // You, too, shall serve! + SAY_TRANSFORM = 2, // Release your rage! + SAY_SLAY = 3 // Another falls! +}; + +enum ArugalEvents +{ + EVENT_VOID_BOLT = 1, + EVENT_TELEPORT, + EVENT_THUNDERSHOCK, + EVENT_CURSE +}; + +class boss_archmage_arugal : public CreatureScript +{ + public: + boss_archmage_arugal() : CreatureScript("boss_archmage_arugal") { } + + struct boss_archmage_arugalAI : public BossAI + { + boss_archmage_arugalAI(Creature* creature) : BossAI(creature, BOSS_ARUGAL) { } + + uint32 teleportSpells[NUM_TELEPORT_SPELLS] = + { + SPELL_TELE_SPAWN, + SPELL_TELE_UPPER, + SPELL_TELE_STAIRS + }; + + void KilledUnit(Unit* who) override + { + if (who->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void SpellHitTarget(Unit* /*target*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_ARUGAL_CURSE) + Talk(SAY_TRANSFORM); + } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + Talk(SAY_AGGRO); + events.ScheduleEvent(EVENT_CURSE, Seconds(7)); + events.ScheduleEvent(EVENT_TELEPORT, Seconds(15)); + events.ScheduleEvent(EVENT_VOID_BOLT, Seconds(1)); + events.ScheduleEvent(EVENT_THUNDERSHOCK, Seconds(10)); + } + + void AttackStart(Unit* who) override + { + AttackStartCaster(who, 100.0f); // void bolt range is 100.f + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_CURSE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 30.0f, true)) + DoCast(target, SPELL_ARUGAL_CURSE); + events.Repeat(Seconds(15)); + break; + case EVENT_TELEPORT: + { + // ensure we never cast the same teleport twice in a row + uint8 spellIndex = urand(1, NUM_TELEPORT_SPELLS-1); + std::swap(teleportSpells[0], teleportSpells[spellIndex]); + DoCast(teleportSpells[0]); + events.Repeat(Seconds(20)); + break; + } + case EVENT_THUNDERSHOCK: + DoCastAOE(SPELL_THUNDERSHOCK); + events.Repeat(Seconds(30)); + break; + case EVENT_VOID_BOLT: + DoCastVictim(SPELL_VOIDBOLT); + events.Repeat(Seconds(5)); + break; + } + } + DoMeleeAttackIfReady(); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_archmage_arugalAI>(creature); + } +}; + class spell_shadowfang_keep_haunting_spirits : public SpellScriptLoader { public: @@ -248,5 +364,6 @@ void AddSC_shadowfang_keep() { new npc_shadowfang_prisoner(); new npc_arugal_voidwalker(); + new boss_archmage_arugal(); new spell_shadowfang_keep_haunting_spirits(); } diff --git a/src/server/scripts/EasternKingdoms/ShadowfangKeep/shadowfang_keep.h b/src/server/scripts/EasternKingdoms/ShadowfangKeep/shadowfang_keep.h index 88edc3f1ee1..7e508191f69 100644 --- a/src/server/scripts/EasternKingdoms/ShadowfangKeep/shadowfang_keep.h +++ b/src/server/scripts/EasternKingdoms/ShadowfangKeep/shadowfang_keep.h @@ -26,8 +26,8 @@ enum DataTypes TYPE_FREE_NPC = 1, TYPE_RETHILGORE = 2, TYPE_FENRUS = 3, - TYPE_NANDOS = 4 + TYPE_NANDOS = 4, + BOSS_ARUGAL = 5 }; #endif - diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp index adcb4f9fc9a..d9c929794cc 100644 --- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp +++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp @@ -175,7 +175,7 @@ void AddSC_arathi_highlands(); void AddSC_blasted_lands(); void AddSC_burning_steppes(); void AddSC_duskwood(); -void AddSC_eastern_plaguelands(); +//void AddSC_eastern_plaguelands(); void AddSC_ghostlands(); void AddSC_hinterlands(); void AddSC_isle_of_queldanas(); @@ -352,7 +352,7 @@ void AddEasternKingdomsScripts() AddSC_blasted_lands(); AddSC_burning_steppes(); AddSC_duskwood(); - AddSC_eastern_plaguelands(); + //AddSC_eastern_plaguelands(); AddSC_ghostlands(); AddSC_hinterlands(); AddSC_isle_of_queldanas(); diff --git a/src/server/scripts/EasternKingdoms/zone_eastern_plaguelands.cpp b/src/server/scripts/EasternKingdoms/zone_eastern_plaguelands.cpp deleted file mode 100644 index c35c8629cef..00000000000 --- a/src/server/scripts/EasternKingdoms/zone_eastern_plaguelands.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* ScriptData -SDName: Eastern_Plaguelands -SD%Complete: 100 -SDComment: Quest support: 5211. Special vendor Augustus the Touched -SDCategory: Eastern Plaguelands -EndScriptData */ - -/* ContentData -npc_ghoul_flayer -npc_augustus_the_touched -npc_darrowshire_spirit -EndContentData */ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "ScriptedGossip.h" -#include "Player.h" -#include "WorldSession.h" - -class npc_ghoul_flayer : public CreatureScript -{ -public: - npc_ghoul_flayer() : CreatureScript("npc_ghoul_flayer") { } - - struct npc_ghoul_flayerAI : public ScriptedAI - { - npc_ghoul_flayerAI(Creature* creature) : ScriptedAI(creature) { } - - void Reset() override { } - - void EnterCombat(Unit* /*who*/) override { } - - void JustDied(Unit* killer) override - { - if (killer->GetTypeId() == TYPEID_PLAYER) - me->SummonCreature(11064, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_ghoul_flayerAI(creature); - } -}; - -/*###### -## npc_augustus_the_touched -######*/ - -class npc_augustus_the_touched : public CreatureScript -{ -public: - npc_augustus_the_touched() : CreatureScript("npc_augustus_the_touched") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(creature->GetGUID()); - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (creature->IsVendor() && player->GetQuestRewardStatus(6164)) - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - - SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); - return true; - } -}; - -/*###### -## npc_darrowshire_spirit -######*/ - -enum DarrowshireSpirit -{ - SPELL_SPIRIT_SPAWNIN = 17321 -}; - -class npc_darrowshire_spirit : public CreatureScript -{ -public: - npc_darrowshire_spirit() : CreatureScript("npc_darrowshire_spirit") { } - - bool OnGossipHello(Player* player, Creature* creature) override - { - SendGossipMenuFor(player, 3873, creature->GetGUID()); - player->TalkedToCreature(creature->GetEntry(), creature->GetGUID()); - creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - return true; - } - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_darrowshire_spiritAI(creature); - } - - struct npc_darrowshire_spiritAI : public ScriptedAI - { - npc_darrowshire_spiritAI(Creature* creature) : ScriptedAI(creature) { } - - void Reset() override - { - DoCast(me, SPELL_SPIRIT_SPAWNIN); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - } - - void EnterCombat(Unit* /*who*/) override { } - }; -}; - -void AddSC_eastern_plaguelands() -{ - new npc_ghoul_flayer(); - new npc_augustus_the_touched(); - new npc_darrowshire_spirit(); -} diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/the_black_morass.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/the_black_morass.cpp index 137da1002af..34a456ae502 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/the_black_morass.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/the_black_morass.cpp @@ -16,19 +16,6 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* -Name: The_Black_Morass -%Complete: 30 -Comment: Misc NPC's and mobs for instance. Most here far from complete. -Category: Caverns of Time, The Black Morass -*/ - -/* ContentData -npc_medivh_bm -npc_time_rift -npc_saat -EndContentData */ - #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "ScriptedGossip.h" @@ -365,57 +352,8 @@ public: }; -enum Saat -{ - SPELL_CHRONO_BEACON = 34975, - ITEM_CHRONO_BEACON = 24289 -}; - -#define GOSSIP_ITEM_OBTAIN "[PH] Obtain Chrono-Beacon" - -class npc_saat : public CreatureScript -{ -public: - npc_saat() : CreatureScript("npc_saat") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF+1) - { - CloseGossipMenuFor(player); - creature->CastSpell(player, SPELL_CHRONO_BEACON, false); - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (player->GetQuestStatus(QUEST_OPENING_PORTAL) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(ITEM_CHRONO_BEACON)) - { - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_OBTAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - SendGossipMenuFor(player, 10000, creature->GetGUID()); - return true; - } - else if (player->GetQuestRewardStatus(QUEST_OPENING_PORTAL) && !player->HasItemCount(ITEM_CHRONO_BEACON)) - { - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_OBTAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - SendGossipMenuFor(player, 10001, creature->GetGUID()); - return true; - } - - SendGossipMenuFor(player, 10002, creature->GetGUID()); - return true; - } - -}; - void AddSC_the_black_morass() { new npc_medivh_bm(); new npc_time_rift(); - new npc_saat(); } diff --git a/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp b/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp index b7f88200077..7a7c104bdba 100644 --- a/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp +++ b/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp @@ -16,18 +16,6 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Stonetalon_Mountains -SD%Complete: 95 -SDComment: Quest support: 6627, 6523 -SDCategory: Stonetalon Mountains -EndScriptData */ - -/* ContentData -npc_braug_dimspirit -npc_kaya_flathoof -EndContentData */ - #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "ScriptedGossip.h" @@ -35,61 +23,6 @@ EndContentData */ #include "Player.h" /*###### -## npc_braug_dimspirit -######*/ - -#define GOSSIP_HBD1 "Ysera" -#define GOSSIP_HBD2 "Neltharion" -#define GOSSIP_HBD3 "Nozdormu" -#define GOSSIP_HBD4 "Alexstrasza" -#define GOSSIP_HBD5 "Malygos" - -class npc_braug_dimspirit : public CreatureScript -{ -public: - npc_braug_dimspirit() : CreatureScript("npc_braug_dimspirit") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF+1) - { - CloseGossipMenuFor(player); - creature->CastSpell(player, 6766, false); - - } - if (action == GOSSIP_ACTION_INFO_DEF+2) - { - CloseGossipMenuFor(player); - player->AreaExploredOrEventHappens(6627); - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (player->GetQuestStatus(6627) == QUEST_STATUS_INCOMPLETE) - { - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HBD1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HBD2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HBD3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HBD4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HBD5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - - SendGossipMenuFor(player, 5820, creature->GetGUID()); - } - else - SendGossipMenuFor(player, 5819, creature->GetGUID()); - - return true; - } - -}; - -/*###### ## npc_kaya_flathoof ######*/ @@ -174,6 +107,5 @@ public: void AddSC_stonetalon_mountains() { - new npc_braug_dimspirit(); new npc_kaya_flathoof(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h index 685d0f51edd..2eda9509bb8 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h @@ -33,7 +33,8 @@ enum DataTypes DATA_WATCHER_NARJIL, DATA_WATCHER_GASHRA, DATA_WATCHER_SILTHIK, - DATA_ANUBARAK_WALL + DATA_ANUBARAK_WALL, + DATA_ANUBARAK_WALL_2 }; enum CreatureIds diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp index 06a91d58705..2860698a8b2 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp @@ -121,12 +121,16 @@ public: _petCount = 0; } + bool CanAIAttack(Unit const* /*who*/) const override { return true; } // do not check boundary here + void EnterCombat(Unit* who) override { BossAI::EnterCombat(who); if (GameObject* door = instance->GetGameObject(DATA_ANUBARAK_WALL)) door->SetGoState(GO_STATE_ACTIVE); // open door for now + if (GameObject* door2 = instance->GetGameObject(DATA_ANUBARAK_WALL_2)) + door2->SetGoState(GO_STATE_ACTIVE); Talk(SAY_AGGRO); instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_GOTTA_GO_START_EVENT); @@ -179,6 +183,8 @@ public: case EVENT_CLOSE_DOOR: if (GameObject* door = instance->GetGameObject(DATA_ANUBARAK_WALL)) door->SetGoState(GO_STATE_READY); + if (GameObject* door2 = instance->GetGameObject(DATA_ANUBARAK_WALL_2)) + door2->SetGoState(GO_STATE_READY); break; case EVENT_POUND: DoCastVictim(SPELL_POUND); diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp index 7338774c21e..c41ec1c488d 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp @@ -789,7 +789,7 @@ struct npc_hadronox_foeAI : public ScriptedAI me->GetMotionMaster()->MovePoint(MOVE_HADRONOX, hadronoxStep[2]); break; } - me->GetMotionMaster()->MoveChase(hadronox); + AttackStart(hadronox); } break; } diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp index ea912da4c3d..0d52a09bbdc 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp @@ -138,10 +138,10 @@ class boss_krik_thir : public CreatureScript for (uint8 i = 1; i <= 3; ++i) { - std::list<TempSummon*> summons; - me->SummonCreatureGroup(i, &summons); - for (TempSummon* summon : summons) - summon->AI()->SetData(DATA_PET_GROUP, i); + std::list<TempSummon*> adds; + me->SummonCreatureGroup(i, &adds); + for (TempSummon* add : adds) + add->AI()->SetData(DATA_PET_GROUP, i); } } @@ -416,10 +416,7 @@ class npc_watcher_gashra : public CreatureScript struct npc_watcher_gashraAI : public npc_gatewatcher_petAI { - npc_watcher_gashraAI(Creature* creature) : npc_gatewatcher_petAI(creature, true) - { - me->SetReactState(REACT_PASSIVE); - } + npc_watcher_gashraAI(Creature* creature) : npc_gatewatcher_petAI(creature, true) { } void Reset() override { @@ -928,11 +925,15 @@ class spell_gatewatcher_subboss_trigger : public SpellScriptLoader void HandleTargets(std::list<WorldObject*>& targetList) { // Remove any Watchers that are already in combat - for (std::list<WorldObject*>::iterator it = targetList.begin(); it != targetList.end(); ++it) + auto it = targetList.begin(); + while (it != targetList.end()) { if (Creature* creature = (*it)->ToCreature()) if (creature->IsAlive() && !creature->IsInCombat()) + { + ++it; continue; + } it = targetList.erase(it); } diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp index 8af4f6cecc4..7f0ce5c369e 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp @@ -41,7 +41,8 @@ ObjectData const creatureData[] = ObjectData const gameobjectData[] = { - { GO_ANUBARAK_DOOR_3, DATA_ANUBARAK_WALL }, + { GO_ANUBARAK_DOOR_1, DATA_ANUBARAK_WALL }, + { GO_ANUBARAK_DOOR_3, DATA_ANUBARAK_WALL_2 }, { 0, 0 } // END }; @@ -77,6 +78,17 @@ class instance_azjol_nerub : public InstanceMapScript if (Creature* gatewatcher = GetCreature(DATA_KRIKTHIR)) gatewatcher->AI()->DoAction(-ACTION_GATEWATCHER_GREET); } + + bool CheckRequiredBosses(uint32 bossId, Player const* player) const override + { + if (_SkipCheckRequiredBosses(player)) + return true; + + if (bossId > DATA_KRIKTHIR && GetBossState(DATA_KRIKTHIR) != DONE) + return false; + + return true; + } }; InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp index 1041d250cc7..976ef3e34db 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp @@ -16,9 +16,6 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -// Known bugs: -// Gormok - Snobolled (creature at back) - #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "trial_of_the_crusader.h" @@ -71,14 +68,16 @@ enum BossSpells //Gormok SPELL_IMPALE = 66331, SPELL_STAGGERING_STOMP = 67648, - SPELL_RISING_ANGER = 66636, //Snobold + SPELL_RISING_ANGER = 66636, SPELL_SNOBOLLED = 66406, SPELL_BATTER = 66408, SPELL_FIRE_BOMB = 66313, SPELL_FIRE_BOMB_1 = 66317, SPELL_FIRE_BOMB_DOT = 66318, SPELL_HEAD_CRACK = 66407, + SPELL_JUMP_TO_HAND = 66342, + SPELL_RIDE_PLAYER = 66245, //Acidmaw & Dreadscale Generic SPELL_SWEEP = 66794, @@ -117,38 +116,41 @@ enum BossSpells enum MyActions { ACTION_ENABLE_FIRE_BOMB = 1, - ACTION_DISABLE_FIRE_BOMB = 2 + ACTION_DISABLE_FIRE_BOMB = 2, + ACTION_ACTIVE_SNOBOLD = 3 }; enum Events { // Gormok EVENT_IMPALE = 1, - EVENT_STAGGERING_STOMP = 2, - EVENT_THROW = 3, + EVENT_STAGGERING_STOMP, + EVENT_THROW, // Snobold - EVENT_FIRE_BOMB = 4, - EVENT_BATTER = 5, - EVENT_HEAD_CRACK = 6, + EVENT_FIRE_BOMB, + EVENT_BATTER, + EVENT_HEAD_CRACK, + EVENT_SNOBOLLED, + EVENT_CHECK_MOUNT, // Acidmaw & Dreadscale - EVENT_BITE = 7, - EVENT_SPEW = 8, - EVENT_SLIME_POOL = 9, - EVENT_SPIT = 10, - EVENT_SPRAY = 11, - EVENT_SWEEP = 12, - EVENT_SUBMERGE = 13, - EVENT_EMERGE = 14, - EVENT_SUMMON_ACIDMAW = 15, + EVENT_BITE, + EVENT_SPEW, + EVENT_SLIME_POOL, + EVENT_SPIT, + EVENT_SPRAY, + EVENT_SWEEP, + EVENT_SUBMERGE, + EVENT_EMERGE, + EVENT_SUMMON_ACIDMAW, // Icehowl - EVENT_FEROCIOUS_BUTT = 16, - EVENT_MASSIVE_CRASH = 17, - EVENT_WHIRL = 18, - EVENT_ARCTIC_BREATH = 19, - EVENT_TRAMPLE = 20 + EVENT_FEROCIOUS_BUTT, + EVENT_MASSIVE_CRASH, + EVENT_WHIRL, + EVENT_ARCTIC_BREATH, + EVENT_TRAMPLE }; enum Phases @@ -158,6 +160,13 @@ enum Phases PHASE_SUBMERGED = 3 }; +enum GormokMisc +{ + DATA_NEW_TARGET = 1, + GORMOK_HAND_SEAT = 4, + PLAYER_VEHICLE_ID = 444, +}; + class boss_gormok : public CreatureScript { public: @@ -169,9 +178,9 @@ class boss_gormok : public CreatureScript void Reset() override { - events.ScheduleEvent(EVENT_IMPALE, urand(8*IN_MILLISECONDS, 10*IN_MILLISECONDS)); - events.ScheduleEvent(EVENT_STAGGERING_STOMP, 15*IN_MILLISECONDS); - events.ScheduleEvent(EVENT_THROW, urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS)); + events.ScheduleEvent(EVENT_IMPALE, Seconds(8), Seconds(10)); + events.ScheduleEvent(EVENT_STAGGERING_STOMP, Seconds(15)); + events.ScheduleEvent(EVENT_THROW, Seconds(15), Seconds(30)); summons.DespawnAll(); } @@ -216,18 +225,7 @@ class boss_gormok : public CreatureScript void EnterCombat(Unit* /*who*/) override { _EnterCombat(); - me->SetInCombatWithZone(); instance->SetData(TYPE_NORTHREND_BEASTS, GORMOK_IN_PROGRESS); - - for (uint8 i = 0; i < MAX_SNOBOLDS; i++) - { - if (Creature* pSnobold = DoSpawnCreature(NPC_SNOBOLD_VASSAL, 0, 0, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0)) - { - pSnobold->EnterVehicle(me, i); - pSnobold->SetInCombatWithZone(); - pSnobold->AI()->DoAction(ACTION_ENABLE_FIRE_BOMB); - } - } } void DamageTaken(Unit* /*who*/, uint32& damage) override @@ -235,8 +233,14 @@ class boss_gormok : public CreatureScript // despawn the remaining passengers on death if (damage >= me->GetHealth()) for (uint8 i = 0; i < MAX_SNOBOLDS; ++i) - if (Unit* pSnobold = me->GetVehicleKit()->GetPassenger(i)) - pSnobold->ToCreature()->DespawnOrUnsummon(); + if (Unit* snobold = me->GetVehicleKit()->GetPassenger(i)) + snobold->ToCreature()->DespawnOrUnsummon(); + } + + void PassengerBoarded(Unit* who, int8 seatId, bool apply) override + { + if (apply && seatId == GORMOK_HAND_SEAT) + who->CastSpell(me, SPELL_RISING_ANGER, true); } void UpdateAI(uint32 diff) override @@ -255,30 +259,28 @@ class boss_gormok : public CreatureScript { case EVENT_IMPALE: DoCastVictim(SPELL_IMPALE); - events.ScheduleEvent(EVENT_IMPALE, urand(8*IN_MILLISECONDS, 10*IN_MILLISECONDS)); - return; + events.Repeat(Seconds(8), Seconds(10)); + break; case EVENT_STAGGERING_STOMP: DoCastVictim(SPELL_STAGGERING_STOMP); - events.ScheduleEvent(EVENT_STAGGERING_STOMP, 15*IN_MILLISECONDS); - return; + events.Repeat(Seconds(15)); + break; case EVENT_THROW: for (uint8 i = 0; i < MAX_SNOBOLDS; ++i) { - if (Unit* pSnobold = me->GetVehicleKit()->GetPassenger(i)) + if (Unit* snobold = me->GetVehicleKit()->GetPassenger(i)) { - pSnobold->ExitVehicle(); - pSnobold->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - pSnobold->ToCreature()->SetReactState(REACT_AGGRESSIVE); - pSnobold->ToCreature()->AI()->DoAction(ACTION_DISABLE_FIRE_BOMB); - pSnobold->CastSpell(me, SPELL_RISING_ANGER, true); - Talk(EMOTE_SNOBOLLED); + snobold->ExitVehicle(); + snobold->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + snobold->GetAI()->DoAction(ACTION_DISABLE_FIRE_BOMB); + snobold->CastSpell(me, SPELL_JUMP_TO_HAND, true); break; } } - events.ScheduleEvent(EVENT_THROW, urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS)); - return; + events.Repeat(Seconds(15), Seconds(30)); + break; default: - return; + break; } } @@ -292,6 +294,23 @@ class boss_gormok : public CreatureScript } }; +class SnobolledTargetSelector : public std::unary_function<Unit*, bool> +{ +public: + SnobolledTargetSelector(Unit const* /*unit*/) { } + + bool operator()(Unit* unit) const + { + if (unit->GetTypeId() != TYPEID_PLAYER) + return false; + + if (unit->HasAura(SPELL_RIDE_PLAYER) || unit->HasAura(SPELL_SNOBOLLED)) + return false; + + return true; + } +}; + class npc_snobold_vassal : public CreatureScript { public: @@ -299,59 +318,24 @@ class npc_snobold_vassal : public CreatureScript struct npc_snobold_vassalAI : public ScriptedAI { - npc_snobold_vassalAI(Creature* creature) : ScriptedAI(creature) + npc_snobold_vassalAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _isActive(false) { - _targetDied = false; - _instance = creature->GetInstanceScript(); _instance->SetData(DATA_SNOBOLD_COUNT, INCREASE); + SetCombatMovement(false); } void Reset() override { - _events.ScheduleEvent(EVENT_BATTER, 5*IN_MILLISECONDS); - _events.ScheduleEvent(EVENT_HEAD_CRACK, 25*IN_MILLISECONDS); - - _targetGUID.Clear(); - _targetDied = false; - - //Workaround for Snobold me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - } - - void EnterCombat(Unit* who) override - { - _targetGUID = who->GetGUID(); - me->TauntApply(who); - DoCast(who, SPELL_SNOBOLLED); - } - - void DamageTaken(Unit* pDoneBy, uint32 &uiDamage) override - { - if (pDoneBy->GetGUID() == _targetGUID) - uiDamage = 0; - } - - void MovementInform(uint32 type, uint32 pointId) override - { - if (type != POINT_MOTION_TYPE) - return; - - switch (pointId) - { - case 0: - if (_targetDied) - me->DespawnOrUnsummon(); - break; - default: - break; - } + me->SetInCombatWithZone(); + _events.ScheduleEvent(EVENT_CHECK_MOUNT, Seconds(1)); + _events.ScheduleEvent(EVENT_FIRE_BOMB, Seconds(5), Seconds(30)); } void JustDied(Unit* /*killer*/) override { if (Unit* target = ObjectAccessor::GetPlayer(*me, _targetGUID)) - if (target->IsAlive()) - target->RemoveAurasDueToSpell(SPELL_SNOBOLLED); + target->RemoveAurasDueToSpell(SPELL_SNOBOLLED); _instance->SetData(DATA_SNOBOLD_COUNT, DECREASE); } @@ -360,50 +344,69 @@ class npc_snobold_vassal : public CreatureScript switch (action) { case ACTION_ENABLE_FIRE_BOMB: - _events.ScheduleEvent(EVENT_FIRE_BOMB, urand(5*IN_MILLISECONDS, 30*IN_MILLISECONDS)); + _events.ScheduleEvent(EVENT_FIRE_BOMB, Seconds(5), Seconds(30)); break; case ACTION_DISABLE_FIRE_BOMB: _events.CancelEvent(EVENT_FIRE_BOMB); break; + case ACTION_ACTIVE_SNOBOLD: + _isActive = true; + break; default: break; } } - void UpdateAI(uint32 diff) override + void SetGUID(ObjectGuid guid, int32 id) override { - if (!UpdateVictim() || _targetDied) + if (id == DATA_NEW_TARGET) + if (Unit* target = ObjectAccessor::GetPlayer(*me, guid)) + { + _targetGUID = guid; + AttackStart(target); + _events.ScheduleEvent(EVENT_BATTER, Seconds(5)); + _events.ScheduleEvent(EVENT_HEAD_CRACK, Seconds(25)); + _events.ScheduleEvent(EVENT_SNOBOLLED, Milliseconds(500)); + } + } + + void AttackStart(Unit* who) override + { + //Snobold only melee attack players that is your vehicle + if (!_isActive || who->GetGUID() != _targetGUID) return; - if (Unit* target = ObjectAccessor::GetPlayer(*me, _targetGUID)) + ScriptedAI::AttackStart(who); + } + + void MountOnBoss() + { + Unit* gormok = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_GORMOK)); + if (gormok && gormok->IsAlive()) { - if (!target->IsAlive()) - { - Unit* gormok = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_GORMOK)); - if (gormok && gormok->IsAlive()) - { - SetCombatMovement(false); - _targetDied = true; + me->AttackStop(); + _targetGUID.Clear(); + _isActive = false; + _events.CancelEvent(EVENT_BATTER); + _events.CancelEvent(EVENT_HEAD_CRACK); - // looping through Gormoks seats - for (uint8 i = 0; i < MAX_SNOBOLDS; i++) - { - if (!gormok->GetVehicleKit()->GetPassenger(i)) - { - me->EnterVehicle(gormok, i); - DoAction(ACTION_ENABLE_FIRE_BOMB); - break; - } - } - } - else if (Unit* target2 = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) + for (uint8 i = 0; i < MAX_SNOBOLDS; i++) + { + if (!gormok->GetVehicleKit()->GetPassenger(i)) { - _targetGUID = target2->GetGUID(); - me->GetMotionMaster()->MoveJump(*target2, 15.0f, 15.0f); + me->EnterVehicle(gormok, i); + DoAction(ACTION_ENABLE_FIRE_BOMB); + break; } } } + //Without Boss, snobolds should jump in another players + else if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, SnobolledTargetSelector(me))) + me->CastSpell(target, SPELL_RIDE_PLAYER, true); + } + void UpdateAI(uint32 diff) override + { _events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) @@ -416,35 +419,46 @@ class npc_snobold_vassal : public CreatureScript case EVENT_FIRE_BOMB: if (me->GetVehicleBase()) if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, -me->GetVehicleBase()->GetCombatReach(), true)) - me->CastSpell(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), SPELL_FIRE_BOMB, true); - _events.ScheduleEvent(EVENT_FIRE_BOMB, 20*IN_MILLISECONDS); - return; + me->CastSpell(target, SPELL_FIRE_BOMB); + _events.Repeat(Seconds(20)); + break; case EVENT_HEAD_CRACK: - // commented out while SPELL_SNOBOLLED gets fixed - //if (Unit* target = ObjectAccessor::GetPlayer(*me, m_uiTargetGUID)) - DoCastVictim(SPELL_HEAD_CRACK); - _events.ScheduleEvent(EVENT_HEAD_CRACK, 30*IN_MILLISECONDS); - return; + DoCast(me->GetVehicleBase(), SPELL_HEAD_CRACK); + _events.Repeat(Seconds(30)); + break; case EVENT_BATTER: - // commented out while SPELL_SNOBOLLED gets fixed - //if (Unit* target = ObjectAccessor::GetPlayer(*me, m_uiTargetGUID)) - DoCastVictim(SPELL_BATTER); - _events.ScheduleEvent(EVENT_BATTER, 10*IN_MILLISECONDS); - return; + DoCast(me->GetVehicleBase(), SPELL_BATTER); + _events.Repeat(Seconds(10)); + break; + case EVENT_SNOBOLLED: + DoCastAOE(SPELL_SNOBOLLED); + break; + case EVENT_CHECK_MOUNT: + if (!me->GetVehicleBase()) + MountOnBoss(); + _events.Repeat(Seconds(1)); + break; default: - return; + break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - // do melee attack only when not on Gormoks back - if (!me->GetVehicleBase()) + if (!UpdateVictim()) + return; + + // do melee attack only if is in player back. + if (_isActive) DoMeleeAttackIfReady(); } + private: EventMap _events; InstanceScript* _instance; ObjectGuid _targetGUID; - bool _targetDied; + bool _isActive; }; CreatureAI* GetAI(Creature* creature) const override @@ -1145,6 +1159,138 @@ class boss_icehowl : public CreatureScript } }; +class spell_gormok_jump_to_hand : public SpellScriptLoader +{ +public: + spell_gormok_jump_to_hand() : SpellScriptLoader("spell_gormok_jump_to_hand") { } + + class spell_gormok_jump_to_hand_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gormok_jump_to_hand_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_RIDE_PLAYER)) + return false; + return true; + } + + bool Load() override + { + if (GetCaster() && GetCaster()->GetEntry() == NPC_SNOBOLD_VASSAL) + return true; + return false; + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* caster = GetCaster()) + { + if (CreatureAI* gormokAI = GetTarget()->ToCreature()->AI()) + { + if (Unit* target = gormokAI->SelectTarget(SELECT_TARGET_RANDOM, 0, SnobolledTargetSelector(GetTarget()))) + { + gormokAI->Talk(EMOTE_SNOBOLLED); + caster->GetAI()->DoAction(ACTION_ACTIVE_SNOBOLD); + caster->CastSpell(target, SPELL_RIDE_PLAYER, true); + } + } + } + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_gormok_jump_to_hand_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_CONTROL_VEHICLE, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_gormok_jump_to_hand_AuraScript(); + } +}; + +class spell_gormok_ride_player : public SpellScriptLoader +{ +public: + spell_gormok_ride_player() : SpellScriptLoader("spell_gormok_ride_player") { } + + class spell_gormok_ride_player_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gormok_ride_player_AuraScript); + + bool Load() override + { + if (GetCaster() && GetCaster()->GetEntry() == NPC_SNOBOLD_VASSAL) + return true; + return false; + } + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + if (target->GetTypeId() != TYPEID_PLAYER || !target->IsInWorld()) + return; + + if (!target->CreateVehicleKit(PLAYER_VEHICLE_ID, 0)) + return; + + if (Unit *caster = GetCaster()) + caster->GetAI()->SetGUID(target->GetGUID(), DATA_NEW_TARGET); + } + + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->RemoveVehicleKit(); + } + + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_gormok_ride_player_AuraScript::OnApply, EFFECT_0, SPELL_AURA_CONTROL_VEHICLE, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_gormok_ride_player_AuraScript::AfterRemove, EFFECT_0, SPELL_AURA_CONTROL_VEHICLE, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_gormok_ride_player_AuraScript(); + } +}; + +class spell_gormok_snobolled : public SpellScriptLoader +{ +public: + spell_gormok_snobolled() : SpellScriptLoader("spell_gormok_snobolled") { } + + class spell_gormok_snobolled_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gormok_snobolled_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_RIDE_PLAYER)) + return false; + return true; + } + + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + if (!GetTarget()->HasAura(SPELL_RIDE_PLAYER)) + Remove(); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_gormok_snobolled_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_gormok_snobolled_AuraScript(); + } +}; + class spell_jormungars_paralytic_toxin : public SpellScriptLoader { public: @@ -1293,6 +1439,9 @@ void AddSC_boss_northrend_beasts() new npc_snobold_vassal(); new npc_firebomb(); new spell_gormok_fire_bomb(); + new spell_gormok_jump_to_hand(); + new spell_gormok_ride_player(); + new spell_gormok_snobolled(); new boss_acidmaw(); new boss_dreadscale(); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp index 5f884ccee9e..85126b35cdb 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp @@ -56,21 +56,26 @@ enum Spells SPELL_MANA_BARRIER = 70842, SPELL_SHADOW_BOLT = 71254, SPELL_DEATH_AND_DECAY = 71001, - SPELL_DOMINATE_MIND_H = 71289, + SPELL_DOMINATE_MIND = 71289, + SPELL_DOMINATE_MIND_SCALE = 71290, SPELL_FROSTBOLT = 71420, SPELL_FROSTBOLT_VOLLEY = 72905, SPELL_TOUCH_OF_INSIGNIFICANCE = 71204, SPELL_SUMMON_SHADE = 71363, - SPELL_SHADOW_CHANNELING = 43897, // Prefight, during intro + SPELL_SHADOW_CHANNELING = 43897, SPELL_DARK_TRANSFORMATION_T = 70895, SPELL_DARK_EMPOWERMENT_T = 70896, SPELL_DARK_MARTYRDOM_T = 70897, + SPELL_SUMMON_SPIRITS = 72478, // Achievement SPELL_FULL_HOUSE = 72827, // does not exist in dbc but still can be used for criteria check // Both Adds SPELL_TELEPORT_VISUAL = 41236, + SPELL_CLEAR_ALL_DEBUFFS = 34098, + SPELL_FULL_HEAL = 17683, + SPELL_PERMANENT_FEIGN_DEATH = 70628, // Fanatics SPELL_DARK_TRANSFORMATION = 70900, @@ -86,7 +91,7 @@ enum Spells SPELL_DEATHCHILL_BOLT = 70594, SPELL_DEATHCHILL_BLAST = 70906, SPELL_CURSE_OF_TORPOR = 71237, - SPELL_SHORUD_OF_THE_OCCULT = 70768, + SPELL_SHROUD_OF_THE_OCCULT = 70768, SPELL_ADHERENT_S_DETERMINATION = 71234, SPELL_DARK_MARTYRDOM_ADHERENT = 70903, @@ -108,44 +113,6 @@ enum Spells enum EventTypes { - // Lady Deathwhisper - EVENT_INTRO_2 = 1, - EVENT_INTRO_3 = 2, - EVENT_INTRO_4 = 3, - EVENT_INTRO_5 = 4, - EVENT_INTRO_6 = 5, - EVENT_INTRO_7 = 6, - EVENT_BERSERK = 7, - EVENT_DEATH_AND_DECAY = 8, - EVENT_DOMINATE_MIND_H = 9, - - // Phase 1 only - EVENT_P1_SUMMON_WAVE = 10, - EVENT_P1_SHADOW_BOLT = 11, - EVENT_P1_EMPOWER_CULTIST = 12, - EVENT_P1_REANIMATE_CULTIST = 13, - - // Phase 2 only - EVENT_P2_SUMMON_WAVE = 14, - EVENT_P2_FROSTBOLT = 15, - EVENT_P2_FROSTBOLT_VOLLEY = 16, - EVENT_P2_TOUCH_OF_INSIGNIFICANCE = 17, - EVENT_P2_SUMMON_SHADE = 18, - - // Shared adds events - EVENT_CULTIST_DARK_MARTYRDOM = 19, - - // Cult Fanatic - EVENT_FANATIC_NECROTIC_STRIKE = 20, - EVENT_FANATIC_SHADOW_CLEAVE = 21, - EVENT_FANATIC_VAMPIRIC_MIGHT = 22, - - // Cult Adherent - EVENT_ADHERENT_FROST_FEVER = 23, - EVENT_ADHERENT_DEATHCHILL = 24, - EVENT_ADHERENT_CURSE_OF_TORPOR = 25, - EVENT_ADHERENT_SHORUD_OF_THE_OCCULT = 26, - // Darnavan EVENT_DARNAVAN_BLADESTORM = 27, EVENT_DARNAVAN_CHARGE = 28, @@ -163,6 +130,13 @@ enum Phases PHASE_TWO = 3 }; +enum Groups +{ + GROUP_INTRO = 0, + GROUP_ONE = 1, + GROUP_TWO = 2 +}; + enum DeprogrammingData { NPC_DARNAVAN_10 = 38472, @@ -185,8 +159,6 @@ enum Actions uint32 const SummonEntries[2] = {NPC_CULT_FANATIC, NPC_CULT_ADHERENT}; -#define GUID_CULTIST 1 - Position const SummonPositions[7] = { {-578.7066f, 2154.167f, 51.01529f, 1.692969f}, // 1 Left Door 1 (Cult Fanatic) @@ -229,43 +201,67 @@ class boss_lady_deathwhisper : public CreatureScript void Initialize() { _waveCounter = 0; - _nextVengefulShadeTargetGUID.Clear(); + _nextVengefulShadeTargetGUID.clear(); + _cultistQueue.clear(); _darnavanGUID.Clear(); + _phase = PHASE_ALL; + scheduler.SetValidator([this] + { + return !(me->HasUnitState(UNIT_STATE_CASTING) && _phase != PHASE_INTRO); + }); } void Reset() override { - _Reset(); - me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA)); - events.SetPhase(PHASE_ONE); Initialize(); - DoCast(me, SPELL_SHADOW_CHANNELING); - me->RemoveAurasDueToSpell(SPELL_BERSERK); - me->RemoveAurasDueToSpell(SPELL_MANA_BARRIER); + _phase = PHASE_ONE; + DoCastSelf(SPELL_SHADOW_CHANNELING); + me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA)); me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false); me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, false); } void DoAction(int32 action) override { - switch (action) + if (action != ACTION_START_INTRO) + return; + + if (!_introDone) { - case ACTION_START_INTRO: - if (!_introDone) + _introDone = true; + Talk(SAY_INTRO_1); + _phase = PHASE_INTRO; + scheduler.Schedule(Seconds(10), GROUP_INTRO, [this](TaskContext context) + { + switch (context.GetRepeatCounter()) { - _introDone = true; - Talk(SAY_INTRO_1); - events.SetPhase(PHASE_INTRO); - events.ScheduleEvent(EVENT_INTRO_2, 11000, 0, PHASE_INTRO); - events.ScheduleEvent(EVENT_INTRO_3, 21000, 0, PHASE_INTRO); - events.ScheduleEvent(EVENT_INTRO_4, 31500, 0, PHASE_INTRO); - events.ScheduleEvent(EVENT_INTRO_5, 39500, 0, PHASE_INTRO); - events.ScheduleEvent(EVENT_INTRO_6, 48500, 0, PHASE_INTRO); - events.ScheduleEvent(EVENT_INTRO_7, 58000, 0, PHASE_INTRO); + case 0: + Talk(SAY_INTRO_2); + context.Repeat(Seconds(21)); + break; + case 1: + Talk(SAY_INTRO_3); + context.Repeat(Seconds(11)); + break; + case 2: + Talk(SAY_INTRO_4); + context.Repeat(Seconds(9)); + break; + case 3: + Talk(SAY_INTRO_5); + context.Repeat(Seconds(21)); + break; + case 4: + Talk(SAY_INTRO_6); + context.Repeat(Seconds(10)); + break; + case 5: + Talk(SAY_INTRO_7); + return; + default: + break; } - break; - default: - break; + }); } } @@ -274,7 +270,7 @@ class boss_lady_deathwhisper : public CreatureScript if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) return; - if (victim && me->Attack(victim, true) && !events.IsInPhase(PHASE_ONE)) + if (victim && me->Attack(victim, true) && _phase != PHASE_ONE) me->GetMotionMaster()->MoveChase(victim); } @@ -282,31 +278,61 @@ class boss_lady_deathwhisper : public CreatureScript { if (!instance->CheckRequiredBosses(DATA_LADY_DEATHWHISPER, who->ToPlayer())) { - EnterEvadeMode(); + EnterEvadeMode(EVADE_REASON_SEQUENCE_BREAK); instance->DoCastSpellOnPlayers(LIGHT_S_HAMMER_TELEPORT); return; } + me->SetCombatPulseDelay(5); me->setActive(true); DoZoneInCombat(); - - events.Reset(); - events.SetPhase(PHASE_ONE); + _phase = PHASE_ONE; + scheduler.CancelGroup(GROUP_INTRO); // phase-independent events - events.ScheduleEvent(EVENT_BERSERK, 600000); - events.ScheduleEvent(EVENT_DEATH_AND_DECAY, 10000); + scheduler + .Schedule(Minutes(10), [this](TaskContext /*context*/) + { + DoCastSelf(SPELL_BERSERK); + Talk(SAY_BERSERK); + }) + .Schedule(Seconds(17), [this](TaskContext death_and_decay) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) + DoCast(target, SPELL_DEATH_AND_DECAY); + death_and_decay.Repeat(Seconds(22), Seconds(30)); + }); + if (GetDifficulty() != RAID_DIFFICULTY_10MAN_NORMAL) + scheduler.Schedule(Seconds(27), [this](TaskContext dominate_mind) + { + Talk(SAY_DOMINATE_MIND); + for (uint8 i = 0; i < _dominateMindCount; i++) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_DOMINATE_MIND)) + DoCast(target, SPELL_DOMINATE_MIND); + dominate_mind.Repeat(Seconds(40), Seconds(45)); + }); // phase one only - events.ScheduleEvent(EVENT_P1_SUMMON_WAVE, 5000, 0, PHASE_ONE); - events.ScheduleEvent(EVENT_P1_SHADOW_BOLT, urand(5500, 6000), 0, PHASE_ONE); - events.ScheduleEvent(EVENT_P1_EMPOWER_CULTIST, urand(20000, 30000), 0, PHASE_ONE); - if (GetDifficulty() != RAID_DIFFICULTY_10MAN_NORMAL) - events.ScheduleEvent(EVENT_DOMINATE_MIND_H, 27000); + scheduler + .Schedule(Seconds(5), GROUP_ONE, [this](TaskContext wave) + { + SummonWaveP1(); + wave.Repeat(Seconds(IsHeroic() ? 45 : 60)); + }) + .Schedule(Seconds(2), GROUP_ONE, [this](TaskContext shadow_bolt) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) + DoCast(target, SPELL_SHADOW_BOLT); + shadow_bolt.Repeat(Milliseconds(2450), Milliseconds(3600)); + }) + .Schedule(Seconds(15), GROUP_ONE, [this](TaskContext context) + { + DoImproveCultist(); + context.Repeat(Seconds(25)); + }); Talk(SAY_AGGRO); DoStartNoMovement(who); me->RemoveAurasDueToSpell(SPELL_SHADOW_CHANNELING); - DoCast(me, SPELL_MANA_BARRIER, true); - + DoCastSelf(SPELL_MANA_BARRIER, true); instance->SetBossState(DATA_LADY_DEATHWHISPER, IN_PROGRESS); } @@ -338,7 +364,7 @@ class boss_lady_deathwhisper : public CreatureScript { if (Group* group = owner->GetGroup()) { - for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) if (Player* member = itr->GetSource()) member->KilledMonsterCredit(NPC_DARNAVAN_CREDIT); } @@ -351,17 +377,14 @@ class boss_lady_deathwhisper : public CreatureScript _JustDied(); } - void JustReachedHome() override + void EnterEvadeMode(EvadeReason /*why*/) override { - _JustReachedHome(); - instance->SetBossState(DATA_LADY_DEATHWHISPER, FAIL); - + scheduler.CancelAll(); summons.DespawnAll(); if (Creature* darnavan = ObjectAccessor::GetCreature(*me, _darnavanGUID)) - { darnavan->DespawnOrUnsummon(); - _darnavanGUID.Clear(); - } + + _DespawnAtEvade(); } void KilledUnit(Unit* victim) override @@ -373,151 +396,98 @@ class boss_lady_deathwhisper : public CreatureScript void DamageTaken(Unit* /*damageDealer*/, uint32& damage) override { // phase transition - if (events.IsInPhase(PHASE_ONE) && damage > me->GetPower(POWER_MANA)) + if (_phase == PHASE_ONE && damage > me->GetPower(POWER_MANA)) { + _phase = PHASE_TWO; Talk(SAY_PHASE_2); Talk(EMOTE_PHASE_2); DoStartMovement(me->GetVictim()); + DoResetThreat(); damage -= me->GetPower(POWER_MANA); me->SetPower(POWER_MANA, 0); me->RemoveAurasDueToSpell(SPELL_MANA_BARRIER); - events.SetPhase(PHASE_TWO); - events.ScheduleEvent(EVENT_P2_FROSTBOLT, urand(10000, 12000), 0, PHASE_TWO); - events.ScheduleEvent(EVENT_P2_FROSTBOLT_VOLLEY, urand(19000, 21000), 0, PHASE_TWO); - events.ScheduleEvent(EVENT_P2_TOUCH_OF_INSIGNIFICANCE, urand(6000, 9000), 0, PHASE_TWO); - events.ScheduleEvent(EVENT_P2_SUMMON_SHADE, urand(12000, 15000), 0, PHASE_TWO); + scheduler.CancelGroup(GROUP_ONE); + + scheduler + .Schedule(Seconds(12), GROUP_TWO, [this](TaskContext frostbolt) + { + DoCastVictim(SPELL_FROSTBOLT); + frostbolt.Repeat(); + }) + .Schedule(Seconds(20), GROUP_TWO, [this](TaskContext frostboldVolley) + { + DoCastAOE(SPELL_FROSTBOLT_VOLLEY); + frostboldVolley.Repeat(); + }) + .Schedule(Seconds(6), Seconds(9), GROUP_TWO, [this](TaskContext touch) + { + if (me->GetVictim()) + me->AddAura(SPELL_TOUCH_OF_INSIGNIFICANCE, me->EnsureVictim()); + touch.Repeat(); + }) + .Schedule(Seconds(12), GROUP_TWO, [this](TaskContext summonShade) + { + me->CastCustomSpell(SPELL_SUMMON_SPIRITS, SPELLVALUE_MAX_TARGETS, Is25ManRaid() ? 2 : 1); + summonShade.Repeat(); + }); + // on heroic mode Lady Deathwhisper is immune to taunt effects in phase 2 and continues summoning adds if (IsHeroic()) { me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); - events.ScheduleEvent(EVENT_P2_SUMMON_WAVE, 45000, 0, PHASE_TWO); + scheduler.Schedule(Seconds(), GROUP_TWO, [this](TaskContext context) + { + SummonWaveP2(); + context.Repeat(Seconds(45)); + }); } } } - void JustSummoned(Creature* summon) override + void SpellHitTarget(Unit* target, const SpellInfo* spell) override { - if (summon->GetEntry() == NPC_DARNAVAN) - _darnavanGUID = summon->GetGUID(); - else - summons.Summon(summon); + if (spell->Id == SPELL_SUMMON_SPIRITS) + _nextVengefulShadeTargetGUID.push_back(target->GetGUID()); + } - Unit* target = NULL; - if (summon->GetEntry() == NPC_VENGEFUL_SHADE) + void JustSummoned(Creature* summon) override + { + switch (summon->GetEntry()) { - target = ObjectAccessor::GetUnit(*me, _nextVengefulShadeTargetGUID); // Vengeful Shade - _nextVengefulShadeTargetGUID.Clear(); + case NPC_DARNAVAN_10: + case NPC_DARNAVAN_25: + _darnavanGUID = summon->GetGUID(); + summon->AI()->AttackStart(SelectTarget(SELECT_TARGET_RANDOM)); + return; + case NPC_VENGEFUL_SHADE: + if (_nextVengefulShadeTargetGUID.empty()) + break; + summon->AI()->SetGUID(_nextVengefulShadeTargetGUID.front()); + _nextVengefulShadeTargetGUID.pop_front(); + break; + case NPC_CULT_ADHERENT: + case NPC_CULT_FANATIC: + _cultistQueue.push_back(summon->GetGUID()); + summon->AI()->AttackStart(SelectTarget(SELECT_TARGET_RANDOM)); + break; + default: + break; } - else - target = SelectTarget(SELECT_TARGET_RANDOM); // Wave adds - - summon->AI()->AttackStart(target); // CAN be NULL - if (summon->GetEntry() == NPC_REANIMATED_FANATIC) - summon->CastSpell(summon, SPELL_FANATIC_S_DETERMINATION, true); - else if (summon->GetEntry() == NPC_REANIMATED_ADHERENT) - summon->CastSpell(summon, SPELL_ADHERENT_S_DETERMINATION, true); + summons.Summon(summon); } void UpdateAI(uint32 diff) override { - if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO)) + if (!UpdateVictim() && _phase != PHASE_INTRO) return; - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING) && !events.IsInPhase(PHASE_INTRO)) - return; - - while (uint32 eventId = events.ExecuteEvent()) + scheduler.Update(diff, [this] { - switch (eventId) - { - case EVENT_INTRO_2: - Talk(SAY_INTRO_2); - break; - case EVENT_INTRO_3: - Talk(SAY_INTRO_3); - break; - case EVENT_INTRO_4: - Talk(SAY_INTRO_4); - break; - case EVENT_INTRO_5: - Talk(SAY_INTRO_5); - break; - case EVENT_INTRO_6: - Talk(SAY_INTRO_6); - break; - case EVENT_INTRO_7: - Talk(SAY_INTRO_7); - break; - case EVENT_DEATH_AND_DECAY: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) - DoCast(target, SPELL_DEATH_AND_DECAY); - events.ScheduleEvent(EVENT_DEATH_AND_DECAY, urand(22000, 30000)); - break; - case EVENT_DOMINATE_MIND_H: - Talk(SAY_DOMINATE_MIND); - for (uint8 i = 0; i < _dominateMindCount; i++) - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_DOMINATE_MIND_H)) - DoCast(target, SPELL_DOMINATE_MIND_H); - events.ScheduleEvent(EVENT_DOMINATE_MIND_H, urand(40000, 45000)); - break; - case EVENT_P1_SUMMON_WAVE: - SummonWaveP1(); - events.ScheduleEvent(EVENT_P1_SUMMON_WAVE, IsHeroic() ? 45000 : 60000, 0, PHASE_ONE); - break; - case EVENT_P1_SHADOW_BOLT: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) - DoCast(target, SPELL_SHADOW_BOLT); - events.ScheduleEvent(EVENT_P1_SHADOW_BOLT, urand(5000, 8000), 0, PHASE_ONE); - break; - case EVENT_P1_REANIMATE_CULTIST: - ReanimateCultist(); - break; - case EVENT_P1_EMPOWER_CULTIST: - EmpowerCultist(); - events.ScheduleEvent(EVENT_P1_EMPOWER_CULTIST, urand(18000, 25000)); - break; - case EVENT_P2_FROSTBOLT: - DoCastVictim(SPELL_FROSTBOLT); - events.ScheduleEvent(EVENT_P2_FROSTBOLT, urand(10000, 11000), 0, PHASE_TWO); - break; - case EVENT_P2_FROSTBOLT_VOLLEY: - DoCastAOE(SPELL_FROSTBOLT_VOLLEY); - events.ScheduleEvent(EVENT_P2_FROSTBOLT_VOLLEY, urand(13000, 15000), 0, PHASE_TWO); - break; - case EVENT_P2_TOUCH_OF_INSIGNIFICANCE: - DoCastVictim(SPELL_TOUCH_OF_INSIGNIFICANCE); - events.ScheduleEvent(EVENT_P2_TOUCH_OF_INSIGNIFICANCE, urand(9000, 13000), 0, PHASE_TWO); - break; - case EVENT_P2_SUMMON_SHADE: - if (Unit* shadeTarget = SelectTarget(SELECT_TARGET_RANDOM, 1)) - { - _nextVengefulShadeTargetGUID = shadeTarget->GetGUID(); - DoCast(shadeTarget, SPELL_SUMMON_SHADE); - } - events.ScheduleEvent(EVENT_P2_SUMMON_SHADE, urand(18000, 23000), 0, PHASE_TWO); - break; - case EVENT_P2_SUMMON_WAVE: - SummonWaveP2(); - events.ScheduleEvent(EVENT_P2_SUMMON_WAVE, 45000, 0, PHASE_TWO); - break; - case EVENT_BERSERK: - DoCast(me, SPELL_BERSERK); - Talk(SAY_BERSERK); - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING) && !events.IsInPhase(PHASE_INTRO)) - return; - } - - // We should not melee attack when barrier is up - if (me->HasAura(SPELL_MANA_BARRIER)) - return; - - DoMeleeAttackIfReady(); + // We should not melee attack when barrier is up + if (!me->HasAura(SPELL_MANA_BARRIER)) + DoMeleeAttackIfReady(); + }); } // summoning function for first phase @@ -568,72 +538,40 @@ class boss_lady_deathwhisper : public CreatureScript summon->CastSpell(summon, SPELL_TELEPORT_VISUAL); } - void SetGUID(ObjectGuid guid, int32 id/* = 0*/) override + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override { - if (id != GUID_CULTIST) - return; - - _reanimationQueue.push_back(guid); - events.ScheduleEvent(EVENT_P1_REANIMATE_CULTIST, 3000, 0, PHASE_ONE); + if (summon->GetEntry() == NPC_CULT_ADHERENT || summon->GetEntry() == NPC_CULT_FANATIC) + _cultistQueue.remove(summon->GetGUID()); } - void ReanimateCultist() + void DoImproveCultist() { - if (_reanimationQueue.empty()) + if (_cultistQueue.empty()) return; - ObjectGuid cultistGUID = _reanimationQueue.front(); - Creature* cultist = ObjectAccessor::GetCreature(*me, cultistGUID); - _reanimationQueue.pop_front(); + _cultistGUID = Trinity::Containers::SelectRandomContainerElement(_cultistQueue); + _cultistQueue.remove(_cultistGUID); + Creature* cultist = ObjectAccessor::GetCreature(*me, _cultistGUID); if (!cultist) return; - Talk(SAY_ANIMATE_DEAD); - DoCast(cultist, SPELL_DARK_MARTYRDOM_T); - } - - void SpellHitTarget(Unit* target, SpellInfo const* spell) override - { - if (spell->Id == SPELL_DARK_MARTYRDOM_T) + if (RAND(0,1)) + me->CastSpell(cultist, SPELL_DARK_MARTYRDOM_T); + else { - Position pos = target->GetPosition(); - if (target->GetEntry() == NPC_CULT_FANATIC) - me->SummonCreature(NPC_REANIMATED_FANATIC, pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); - else - me->SummonCreature(NPC_REANIMATED_ADHERENT, pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); - - if (TempSummon* summon = target->ToTempSummon()) - summon->UnSummon(); + me->CastSpell(cultist, cultist->GetEntry() == NPC_CULT_FANATIC ? SPELL_DARK_TRANSFORMATION_T : SPELL_DARK_EMPOWERMENT_T, true); + Talk(uint8(cultist->GetEntry() == NPC_CULT_FANATIC ? SAY_DARK_TRANSFORMATION : SAY_DARK_EMPOWERMENT)); } } - void EmpowerCultist() - { - if (summons.empty()) - return; - - std::list<Creature*> temp; - for (SummonList::iterator itr = summons.begin(); itr != summons.end(); ++itr) - if (Creature* cre = ObjectAccessor::GetCreature(*me, *itr)) - if (cre->IsAlive() && (cre->GetEntry() == NPC_CULT_FANATIC || cre->GetEntry() == NPC_CULT_ADHERENT)) - temp.push_back(cre); - - // noone to empower - if (temp.empty()) - return; - - // select random cultist - Creature* cultist = Trinity::Containers::SelectRandomContainerElement(temp); - DoCast(cultist, cultist->GetEntry() == NPC_CULT_FANATIC ? SPELL_DARK_TRANSFORMATION_T : SPELL_DARK_EMPOWERMENT_T, true); - Talk(uint8(cultist->GetEntry() == NPC_CULT_FANATIC ? SAY_DARK_TRANSFORMATION : SAY_DARK_EMPOWERMENT)); - } - private: - ObjectGuid _nextVengefulShadeTargetGUID; ObjectGuid _darnavanGUID; - GuidDeque _reanimationQueue; + ObjectGuid _cultistGUID; + GuidList _cultistQueue; + GuidList _nextVengefulShadeTargetGUID; uint32 _waveCounter; uint8 const _dominateMindCount; + uint8 _phase; bool _introDone; }; @@ -643,8 +581,6 @@ class boss_lady_deathwhisper : public CreatureScript } }; -typedef boss_lady_deathwhisper::boss_lady_deathwhisperAI DeathwisperAI; - class npc_cult_fanatic : public CreatureScript { public: @@ -652,71 +588,91 @@ class npc_cult_fanatic : public CreatureScript struct npc_cult_fanaticAI : public ScriptedAI { - npc_cult_fanaticAI(Creature* creature) : ScriptedAI(creature) { } + npc_cult_fanaticAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } void Reset() override { - Events.Reset(); - Events.ScheduleEvent(EVENT_FANATIC_NECROTIC_STRIKE, urand(10000, 12000)); - Events.ScheduleEvent(EVENT_FANATIC_SHADOW_CLEAVE, urand(14000, 16000)); - Events.ScheduleEvent(EVENT_FANATIC_VAMPIRIC_MIGHT, urand(20000, 27000)); - if (me->GetEntry() == NPC_CULT_FANATIC) - Events.ScheduleEvent(EVENT_CULTIST_DARK_MARTYRDOM, urand(18000, 32000)); + _scheduler.CancelAll(); + _scheduler + .SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }) + .Schedule(Seconds(17), [this](TaskContext vampiric_might) + { + DoCastSelf(SPELL_VAMPIRIC_MIGHT); + vampiric_might.Repeat(Seconds(25)); + }) + .Schedule(Seconds(12), [this](TaskContext shadow_cleave) + { + DoCastVictim(SPELL_SHADOW_CLEAVE); + shadow_cleave.Repeat(Seconds(14)); + }) + .Schedule(Seconds(10), [this](TaskContext necrotic_strike) + { + DoCastVictim(SPELL_NECROTIC_STRIKE); + necrotic_strike.Repeat(Seconds(17)); + }); } void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override { - if (spell->Id == SPELL_DARK_TRANSFORMATION) - me->UpdateEntry(NPC_DEFORMED_FANATIC); - else if (spell->Id == SPELL_DARK_TRANSFORMATION_T) + switch (spell->Id) { - Events.CancelEvent(EVENT_CULTIST_DARK_MARTYRDOM); - me->InterruptNonMeleeSpells(true); - DoCast(me, SPELL_DARK_TRANSFORMATION); + case SPELL_DARK_TRANSFORMATION_T: + me->InterruptNonMeleeSpells(true); + DoCastSelf(SPELL_DARK_TRANSFORMATION); + break; + case SPELL_DARK_TRANSFORMATION: + DoCastSelf(SPELL_FULL_HEAL); + me->UpdateEntry(NPC_DEFORMED_FANATIC); + break; + case SPELL_DARK_MARTYRDOM_T: + me->SetReactState(REACT_PASSIVE); + me->InterruptNonMeleeSpells(true); + me->AttackStop(); + DoCastSelf(SPELL_DARK_MARTYRDOM_FANATIC); + break; + case SPELL_DARK_MARTYRDOM_FANATIC: + _scheduler + .Schedule(Seconds(2), [this](TaskContext /*context*/) + { + me->UpdateEntry(NPC_REANIMATED_FANATIC); + DoCastSelf(SPELL_PERMANENT_FEIGN_DEATH); + DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS); + DoCastSelf(SPELL_FULL_HEAL, true); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED | UNIT_FLAG_UNK_29 | UNIT_FLAG_NOT_SELECTABLE); + }) + .Schedule(Seconds(6), [this](TaskContext /*context*/) + { + me->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED | UNIT_FLAG_UNK_29 | UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + DoZoneInCombat(me); + + if (Creature* ladyDeathwhisper = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_LADY_DEATHWHISPER))) + ladyDeathwhisper->AI()->Talk(SAY_ANIMATE_DEAD); + }); + break; + default: + break; } } void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) - return; - - Events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) + if (!UpdateVictim() && !me->HasAura(SPELL_PERMANENT_FEIGN_DEATH)) return; - while (uint32 eventId = Events.ExecuteEvent()) + _scheduler.Update(diff, [this] { - switch (eventId) - { - case EVENT_FANATIC_NECROTIC_STRIKE: - DoCastVictim(SPELL_NECROTIC_STRIKE); - Events.ScheduleEvent(EVENT_FANATIC_NECROTIC_STRIKE, urand(11000, 13000)); - break; - case EVENT_FANATIC_SHADOW_CLEAVE: - DoCastVictim(SPELL_SHADOW_CLEAVE); - Events.ScheduleEvent(EVENT_FANATIC_SHADOW_CLEAVE, urand(9500, 11000)); - break; - case EVENT_FANATIC_VAMPIRIC_MIGHT: - DoCast(me, SPELL_VAMPIRIC_MIGHT); - Events.ScheduleEvent(EVENT_FANATIC_VAMPIRIC_MIGHT, urand(20000, 27000)); - break; - case EVENT_CULTIST_DARK_MARTYRDOM: - DoCast(me, SPELL_DARK_MARTYRDOM_FANATIC); - Events.ScheduleEvent(EVENT_CULTIST_DARK_MARTYRDOM, urand(16000, 21000)); - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoMeleeAttackIfReady(); + DoMeleeAttackIfReady(); + }); } protected: - EventMap Events; + TaskScheduler _scheduler; + InstanceScript* _instance; }; CreatureAI* GetAI(Creature* creature) const override @@ -732,80 +688,88 @@ class npc_cult_adherent : public CreatureScript struct npc_cult_adherentAI : public ScriptedAI { - npc_cult_adherentAI(Creature* creature) : ScriptedAI(creature) { } + npc_cult_adherentAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } void Reset() override { - Events.Reset(); - Events.ScheduleEvent(EVENT_ADHERENT_FROST_FEVER, urand(10000, 12000)); - Events.ScheduleEvent(EVENT_ADHERENT_DEATHCHILL, urand(14000, 16000)); - Events.ScheduleEvent(EVENT_ADHERENT_CURSE_OF_TORPOR, urand(14000, 16000)); - Events.ScheduleEvent(EVENT_ADHERENT_SHORUD_OF_THE_OCCULT, urand(32000, 39000)); - if (me->GetEntry() == NPC_CULT_ADHERENT) - Events.ScheduleEvent(EVENT_CULTIST_DARK_MARTYRDOM, urand(18000, 32000)); + _scheduler.CancelAll(); + _scheduler + .SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }) + .Schedule(Seconds(5), [this](TaskContext deathchill) + { + if (me->GetEntry() == NPC_EMPOWERED_ADHERENT) + DoCastVictim(SPELL_DEATHCHILL_BLAST); + else + DoCastVictim(SPELL_DEATHCHILL_BOLT); + deathchill.Repeat(Milliseconds(2500)); + }) + .Schedule(Seconds(15), [this](TaskContext shroud_of_the_occult) + { + DoCastSelf(SPELL_SHROUD_OF_THE_OCCULT); + shroud_of_the_occult.Repeat(Seconds(10)); + }) + .Schedule(Seconds(15), [this](TaskContext curse_of_torpor) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1)) + DoCast(target, SPELL_CURSE_OF_TORPOR); + curse_of_torpor.Repeat(Seconds(18)); + }); } void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override { - if (spell->Id == SPELL_DARK_EMPOWERMENT) - me->UpdateEntry(NPC_EMPOWERED_ADHERENT); - else if (spell->Id == SPELL_DARK_EMPOWERMENT_T) + switch (spell->Id) { - Events.CancelEvent(EVENT_CULTIST_DARK_MARTYRDOM); - me->InterruptNonMeleeSpells(true); - DoCast(me, SPELL_DARK_EMPOWERMENT); + case SPELL_DARK_EMPOWERMENT_T: + me->UpdateEntry(NPC_EMPOWERED_ADHERENT); + break; + case SPELL_DARK_MARTYRDOM_T: + me->SetReactState(REACT_PASSIVE); + me->InterruptNonMeleeSpells(true); + me->AttackStop(); + DoCastSelf(SPELL_DARK_MARTYRDOM_ADHERENT); + break; + case SPELL_DARK_MARTYRDOM_ADHERENT: + _scheduler + .Schedule(Seconds(2), [this](TaskContext /*context*/) + { + me->UpdateEntry(NPC_REANIMATED_ADHERENT); + DoCastSelf(SPELL_PERMANENT_FEIGN_DEATH); + DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS); + DoCastSelf(SPELL_FULL_HEAL, true); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED | UNIT_FLAG_UNK_29 | UNIT_FLAG_NOT_SELECTABLE); + }) + .Schedule(Seconds(6), [this](TaskContext /*context*/) + { + me->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED | UNIT_FLAG_UNK_29 | UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + DoCastSelf(SPELL_SHROUD_OF_THE_OCCULT); + DoZoneInCombat(me); + + if (Creature* ladyDeathwhisper = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_LADY_DEATHWHISPER))) + ladyDeathwhisper->AI()->Talk(SAY_ANIMATE_DEAD); + }); + break; + default: + break; } } void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) + if (!UpdateVictim() && !me->HasAura(SPELL_PERMANENT_FEIGN_DEATH)) return; - Events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - while (uint32 eventId = Events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_ADHERENT_FROST_FEVER: - DoCastVictim(SPELL_FROST_FEVER); - Events.ScheduleEvent(EVENT_ADHERENT_FROST_FEVER, urand(9000, 13000)); - break; - case EVENT_ADHERENT_DEATHCHILL: - if (me->GetEntry() == NPC_EMPOWERED_ADHERENT) - DoCastVictim(SPELL_DEATHCHILL_BLAST); - else - DoCastVictim(SPELL_DEATHCHILL_BOLT); - Events.ScheduleEvent(EVENT_ADHERENT_DEATHCHILL, urand(9000, 13000)); - break; - case EVENT_ADHERENT_CURSE_OF_TORPOR: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1)) - DoCast(target, SPELL_CURSE_OF_TORPOR); - Events.ScheduleEvent(EVENT_ADHERENT_CURSE_OF_TORPOR, urand(9000, 13000)); - break; - case EVENT_ADHERENT_SHORUD_OF_THE_OCCULT: - DoCast(me, SPELL_SHORUD_OF_THE_OCCULT); - Events.ScheduleEvent(EVENT_ADHERENT_SHORUD_OF_THE_OCCULT, urand(27000, 32000)); - break; - case EVENT_CULTIST_DARK_MARTYRDOM: - DoCast(me, SPELL_DARK_MARTYRDOM_ADHERENT); - Events.ScheduleEvent(EVENT_CULTIST_DARK_MARTYRDOM, urand(16000, 21000)); - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoMeleeAttackIfReady(); + _scheduler.Update(diff); } protected: - EventMap Events; + TaskScheduler _scheduler; + InstanceScript* _instance; }; CreatureAI* GetAI(Creature* creature) const override @@ -821,15 +785,28 @@ class npc_vengeful_shade : public CreatureScript struct npc_vengeful_shadeAI : public ScriptedAI { - npc_vengeful_shadeAI(Creature* creature) : ScriptedAI(creature) - { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - } + npc_vengeful_shadeAI(Creature* creature) : ScriptedAI(creature) { } void Reset() override { + me->SetReactState(REACT_PASSIVE); me->AddAura(SPELL_VENGEFUL_BLAST_PASSIVE, me); + + _scheduler + .Schedule(Seconds(2), [this](TaskContext /*context*/) + { + me->SetReactState(REACT_AGGRESSIVE); + me->AI()->AttackStart(ObjectAccessor::GetUnit(*me, _targetGUID)); + }) + .Schedule(Seconds(7), [this](TaskContext /*context*/) + { + me->KillSelf(); + }); + } + + void SetGUID(ObjectGuid guid, int32 /*type*/) override + { + _targetGUID = guid; } void SpellHitTarget(Unit* /*target*/, SpellInfo const* spell) override @@ -846,6 +823,18 @@ class npc_vengeful_shade : public CreatureScript break; } } + + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff, [this] + { + DoMeleeAttackIfReady(); + }); + } + + private: + TaskScheduler _scheduler; + ObjectGuid _targetGUID; }; CreatureAI* GetAI(Creature* creature) const override @@ -875,10 +864,10 @@ class npc_darnavan : public CreatureScript void Reset() override { _events.Reset(); - _events.ScheduleEvent(EVENT_DARNAVAN_BLADESTORM, 10000); - _events.ScheduleEvent(EVENT_DARNAVAN_INTIMIDATING_SHOUT, urand(20000, 25000)); - _events.ScheduleEvent(EVENT_DARNAVAN_MORTAL_STRIKE, urand(25000, 30000)); - _events.ScheduleEvent(EVENT_DARNAVAN_SUNDER_ARMOR, urand(5000, 8000)); + _events.ScheduleEvent(EVENT_DARNAVAN_BLADESTORM, Seconds(10)); + _events.ScheduleEvent(EVENT_DARNAVAN_INTIMIDATING_SHOUT, Seconds(20), Seconds(25)); + _events.ScheduleEvent(EVENT_DARNAVAN_MORTAL_STRIKE, Seconds(25), Seconds(30)); + _events.ScheduleEvent(EVENT_DARNAVAN_SUNDER_ARMOR, Seconds(5), Seconds(8)); Initialize(); } @@ -889,7 +878,7 @@ class npc_darnavan : public CreatureScript { if (Group* group = owner->GetGroup()) { - for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) if (Player* member = itr->GetSource()) member->FailQuest(QUEST_DEPROGRAMMING); } @@ -925,7 +914,7 @@ class npc_darnavan : public CreatureScript { DoCastVictim(SPELL_SHATTERING_THROW); _canShatter = false; - _events.ScheduleEvent(EVENT_DARNAVAN_SHATTERING_THROW, 30000); + _events.ScheduleEvent(EVENT_DARNAVAN_SHATTERING_THROW, Seconds(30)); return; } @@ -933,7 +922,7 @@ class npc_darnavan : public CreatureScript { DoCastVictim(SPELL_CHARGE); _canCharge = false; - _events.ScheduleEvent(EVENT_DARNAVAN_CHARGE, 20000); + _events.ScheduleEvent(EVENT_DARNAVAN_CHARGE, Seconds(20)); return; } @@ -943,25 +932,25 @@ class npc_darnavan : public CreatureScript { case EVENT_DARNAVAN_BLADESTORM: DoCast(SPELL_BLADESTORM); - _events.ScheduleEvent(EVENT_DARNAVAN_BLADESTORM, urand(90000, 100000)); + _events.ScheduleEvent(EVENT_DARNAVAN_BLADESTORM, Seconds(90), Seconds(100)); break; case EVENT_DARNAVAN_CHARGE: _canCharge = true; break; case EVENT_DARNAVAN_INTIMIDATING_SHOUT: DoCast(SPELL_INTIMIDATING_SHOUT); - _events.ScheduleEvent(EVENT_DARNAVAN_INTIMIDATING_SHOUT, urand(90000, 120000)); + _events.ScheduleEvent(EVENT_DARNAVAN_INTIMIDATING_SHOUT, Seconds(90), Minutes(2)); break; case EVENT_DARNAVAN_MORTAL_STRIKE: DoCastVictim(SPELL_MORTAL_STRIKE); - _events.ScheduleEvent(EVENT_DARNAVAN_MORTAL_STRIKE, urand(15000, 30000)); + _events.ScheduleEvent(EVENT_DARNAVAN_MORTAL_STRIKE, Seconds(15), Seconds(30)); break; case EVENT_DARNAVAN_SHATTERING_THROW: _canShatter = true; break; case EVENT_DARNAVAN_SUNDER_ARMOR: DoCastVictim(SPELL_SUNDER_ARMOR); - _events.ScheduleEvent(EVENT_DARNAVAN_SUNDER_ARMOR, urand(3000, 7000)); + _events.ScheduleEvent(EVENT_DARNAVAN_SUNDER_ARMOR, Seconds(3), Seconds(7)); break; } } @@ -1013,50 +1002,86 @@ class spell_deathwhisper_mana_barrier : public SpellScriptLoader } }; -class spell_cultist_dark_martyrdom : public SpellScriptLoader +class at_lady_deathwhisper_entrance : public AreaTriggerScript { public: - spell_cultist_dark_martyrdom() : SpellScriptLoader("spell_cultist_dark_martyrdom") { } + at_lady_deathwhisper_entrance() : AreaTriggerScript("at_lady_deathwhisper_entrance") { } - class spell_cultist_dark_martyrdom_SpellScript : public SpellScript + bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { - PrepareSpellScript(spell_cultist_dark_martyrdom_SpellScript); + if (InstanceScript* instance = player->GetInstanceScript()) + if (instance->GetBossState(DATA_LADY_DEATHWHISPER) != DONE) + if (Creature* ladyDeathwhisper = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_LADY_DEATHWHISPER))) + ladyDeathwhisper->AI()->DoAction(ACTION_START_INTRO); + + return true; + } +}; + +class spell_deathwhisper_dominated_mind : public SpellScriptLoader +{ + public: + spell_deathwhisper_dominated_mind() : SpellScriptLoader("spell_deathwhisper_dominated_mind") { } - void HandleEffect(SpellEffIndex /*effIndex*/) + class spell_deathwhisper_dominated_mind_AuraScript : public AuraScript + { + PrepareAuraScript(spell_deathwhisper_dominated_mind_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override { - if (GetCaster()->IsSummon()) - if (Unit* owner = GetCaster()->ToTempSummon()->GetSummoner()) - owner->GetAI()->SetGUID(GetCaster()->GetGUID(), GUID_CULTIST); + if (!sSpellMgr->GetSpellInfo(SPELL_DOMINATE_MIND_SCALE)) + return false; + return true; + } - GetCaster()->KillSelf(); - GetCaster()->SetDisplayId(uint32(GetCaster()->GetEntry() == NPC_CULT_FANATIC ? 38009 : 38010)); + void HandleApply(AuraEffect const* /*eff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->CastSpell(target, SPELL_DOMINATE_MIND_SCALE, true); } void Register() override { - OnEffectHitTarget += SpellEffectFn(spell_cultist_dark_martyrdom_SpellScript::HandleEffect, EFFECT_2, SPELL_EFFECT_FORCE_DESELECT); + AfterEffectApply += AuraEffectApplyFn(spell_deathwhisper_dominated_mind_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_AOE_CHARM, AURA_EFFECT_HANDLE_REAL); } }; - SpellScript* GetSpellScript() const override + AuraScript* GetAuraScript() const override { - return new spell_cultist_dark_martyrdom_SpellScript(); + return new spell_deathwhisper_dominated_mind_AuraScript(); } }; -class at_lady_deathwhisper_entrance : public AreaTriggerScript +class spell_deathwhisper_summon_spirits : public SpellScriptLoader { public: - at_lady_deathwhisper_entrance() : AreaTriggerScript("at_lady_deathwhisper_entrance") { } + spell_deathwhisper_summon_spirits() : SpellScriptLoader("spell_deathwhisper_summon_spirits") { } - bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override + class spell_deathwhisper_summon_spirits_SpellScript : public SpellScript { - if (InstanceScript* instance = player->GetInstanceScript()) - if (instance->GetBossState(DATA_LADY_DEATHWHISPER) != DONE) - if (Creature* ladyDeathwhisper = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_LADY_DEATHWHISPER))) - ladyDeathwhisper->AI()->DoAction(ACTION_START_INTRO); + PrepareSpellScript(spell_deathwhisper_summon_spirits_SpellScript); - return true; + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_SHADE)) + return false; + return true; + } + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_SHADE, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_deathwhisper_summon_spirits_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_deathwhisper_summon_spirits_SpellScript(); } }; @@ -1068,6 +1093,7 @@ void AddSC_boss_lady_deathwhisper() new npc_vengeful_shade(); new npc_darnavan(); new spell_deathwhisper_mana_barrier(); - new spell_cultist_dark_martyrdom(); + new spell_deathwhisper_dominated_mind(); + new spell_deathwhisper_summon_spirits(); new at_lady_deathwhisper_entrance(); } diff --git a/src/server/scripts/Northrend/zone_dragonblight.cpp b/src/server/scripts/Northrend/zone_dragonblight.cpp index c22cd2d9ad7..cb5a7462000 100644 --- a/src/server/scripts/Northrend/zone_dragonblight.cpp +++ b/src/server/scripts/Northrend/zone_dragonblight.cpp @@ -701,6 +701,31 @@ class npc_torturer_lecraft : public CreatureScript } }; +enum MessengerTorvus +{ + NPC_MESSENGER_TORVUS = 26649, + QUEST_MESSAGE_FROM_THE_WEST = 12033, + + TALK_0 = 0 +}; + +class at_nearby_messenger_torvus : public AreaTriggerScript +{ +public: + at_nearby_messenger_torvus() : AreaTriggerScript("at_nearby_messenger_torvus") { } + + bool OnTrigger(Player* player, const AreaTriggerEntry* /*at*/) override + { + if (player->IsAlive()) + if (Quest const* quest = sObjectMgr->GetQuestTemplate(QUEST_MESSAGE_FROM_THE_WEST)) + if (player->CanTakeQuest(quest, false)) + if (Creature* creature = player->FindNearestCreature(NPC_MESSENGER_TORVUS, 50.0f, true)) + creature->AI()->Talk(TALK_0, player); + + return true; + } +}; + void AddSC_dragonblight() { new npc_commander_eligor_dawnbringer(); @@ -708,4 +733,5 @@ void AddSC_dragonblight() new spell_q12096_q12092_bark(); new npc_wyrmrest_defender(); new npc_torturer_lecraft(); + new at_nearby_messenger_torvus(); } diff --git a/src/server/scripts/Northrend/zone_wintergrasp.cpp b/src/server/scripts/Northrend/zone_wintergrasp.cpp index a85d97d4c1d..e45b1f42fa1 100644 --- a/src/server/scripts/Northrend/zone_wintergrasp.cpp +++ b/src/server/scripts/Northrend/zone_wintergrasp.cpp @@ -247,39 +247,39 @@ class npc_wg_queue : public CreatureScript public: npc_wg_queue() : CreatureScript("npc_wg_queue") { } - struct npc_wg_queueAI : public ScriptedAI - { - npc_wg_queueAI(Creature* creature) : ScriptedAI(creature) + struct npc_wg_queueAI : public ScriptedAI { - FrostArmor_Timer = 0; - } + npc_wg_queueAI(Creature* creature) : ScriptedAI(creature) + { + FrostArmor_Timer = 0; + } - uint32 FrostArmor_Timer; + uint32 FrostArmor_Timer; - void Reset() override - { - FrostArmor_Timer = 0; - } + void Reset() override + { + FrostArmor_Timer = 0; + } - void EnterCombat(Unit* /*who*/) override { } + void EnterCombat(Unit* /*who*/) override { } - void UpdateAI(uint32 diff) override - { - if (FrostArmor_Timer <= diff) + void UpdateAI(uint32 diff) override { - DoCast(me, SPELL_FROST_ARMOR); - FrostArmor_Timer = 180000; + if (FrostArmor_Timer <= diff) + { + DoCast(me, SPELL_FROST_ARMOR); + FrostArmor_Timer = 180000; + } + else FrostArmor_Timer -= diff; + + DoMeleeAttackIfReady(); } - else FrostArmor_Timer -= diff; + }; - DoMeleeAttackIfReady(); + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_wg_queueAI(creature); } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_wg_queueAI(creature); - } bool OnGossipHello(Player* player, Creature* creature) override { @@ -474,26 +474,26 @@ class spell_wintergrasp_grab_passenger : public SpellScriptLoader class achievement_wg_didnt_stand_a_chance : public AchievementCriteriaScript { -public: - achievement_wg_didnt_stand_a_chance() : AchievementCriteriaScript("achievement_wg_didnt_stand_a_chance") { } - - bool OnCheck(Player* source, Unit* target) override - { - if (!target) - return false; + public: + achievement_wg_didnt_stand_a_chance() : AchievementCriteriaScript("achievement_wg_didnt_stand_a_chance") { } - if (Player* victim = target->ToPlayer()) + bool OnCheck(Player* source, Unit* target) override { - if (!victim->IsMounted()) + if (!target) return false; - if (Vehicle* vehicle = source->GetVehicle()) - if (vehicle->GetVehicleInfo()->m_ID == 244) // Wintergrasp Tower Cannon - return true; - } + if (Player* victim = target->ToPlayer()) + { + if (!victim->IsMounted()) + return false; - return false; - } + if (Vehicle* vehicle = source->GetVehicle()) + if (vehicle->GetVehicleInfo()->m_ID == 244) // Wintergrasp Tower Cannon + return true; + } + + return false; + } }; enum WgTeleport @@ -503,91 +503,91 @@ enum WgTeleport class spell_wintergrasp_defender_teleport : public SpellScriptLoader { -public: - spell_wintergrasp_defender_teleport() : SpellScriptLoader("spell_wintergrasp_defender_teleport") { } - - class spell_wintergrasp_defender_teleport_SpellScript : public SpellScript - { - PrepareSpellScript(spell_wintergrasp_defender_teleport_SpellScript); + public: + spell_wintergrasp_defender_teleport() : SpellScriptLoader("spell_wintergrasp_defender_teleport") { } - SpellCastResult CheckCast() + class spell_wintergrasp_defender_teleport_SpellScript : public SpellScript { - if (Battlefield* wg = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG)) - if (Player* target = GetExplTargetUnit()->ToPlayer()) - // check if we are in Wintergrasp at all, SotA uses same teleport spells - if ((target->GetZoneId() == 4197 && target->GetTeamId() != wg->GetDefenderTeam()) || target->HasAura(SPELL_WINTERGRASP_TELEPORT_TRIGGER)) - return SPELL_FAILED_BAD_TARGETS; - return SPELL_CAST_OK; - } + PrepareSpellScript(spell_wintergrasp_defender_teleport_SpellScript); + + SpellCastResult CheckCast() + { + if (Battlefield* wg = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG)) + if (Player* target = GetExplTargetUnit()->ToPlayer()) + // check if we are in Wintergrasp at all, SotA uses same teleport spells + if ((target->GetZoneId() == 4197 && target->GetTeamId() != wg->GetDefenderTeam()) || target->HasAura(SPELL_WINTERGRASP_TELEPORT_TRIGGER)) + return SPELL_FAILED_BAD_TARGETS; + return SPELL_CAST_OK; + } - void Register() override + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_wintergrasp_defender_teleport_SpellScript::CheckCast); + } + }; + + SpellScript* GetSpellScript() const override { - OnCheckCast += SpellCheckCastFn(spell_wintergrasp_defender_teleport_SpellScript::CheckCast); + return new spell_wintergrasp_defender_teleport_SpellScript(); } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_wintergrasp_defender_teleport_SpellScript(); - } }; class spell_wintergrasp_defender_teleport_trigger : public SpellScriptLoader { -public: - spell_wintergrasp_defender_teleport_trigger() : SpellScriptLoader("spell_wintergrasp_defender_teleport_trigger") { } - - class spell_wintergrasp_defender_teleport_trigger_SpellScript : public SpellScript - { - PrepareSpellScript(spell_wintergrasp_defender_teleport_trigger_SpellScript); + public: + spell_wintergrasp_defender_teleport_trigger() : SpellScriptLoader("spell_wintergrasp_defender_teleport_trigger") { } - void HandleDummy(SpellEffIndex /*effindex*/) + class spell_wintergrasp_defender_teleport_trigger_SpellScript : public SpellScript { - if (Unit* target = GetHitUnit()) + PrepareSpellScript(spell_wintergrasp_defender_teleport_trigger_SpellScript); + + void HandleDummy(SpellEffIndex /*effindex*/) { - WorldLocation loc = target->GetWorldLocation(); - SetExplTargetDest(loc); + if (Unit* target = GetHitUnit()) + { + WorldLocation loc = target->GetWorldLocation(); + SetExplTargetDest(loc); + } } - } - void Register() override + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_wintergrasp_defender_teleport_trigger_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override { - OnEffectHitTarget += SpellEffectFn(spell_wintergrasp_defender_teleport_trigger_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + return new spell_wintergrasp_defender_teleport_trigger_SpellScript(); } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_wintergrasp_defender_teleport_trigger_SpellScript(); - } }; class condition_is_wintergrasp_horde : public ConditionScript { -public: - condition_is_wintergrasp_horde() : ConditionScript("condition_is_wintergrasp_horde") { } + public: + condition_is_wintergrasp_horde() : ConditionScript("condition_is_wintergrasp_horde") { } - bool OnConditionCheck(Condition const* /* condition */, ConditionSourceInfo& /* sourceInfo */) - { - Battlefield* wintergrasp = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG); - if (wintergrasp->IsEnabled() && wintergrasp->GetDefenderTeam() == TEAM_HORDE) - return true; - return false; - } + bool OnConditionCheck(Condition const* /* condition */, ConditionSourceInfo& /* sourceInfo */) + { + Battlefield* wintergrasp = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG); + if (wintergrasp && wintergrasp->IsEnabled() && wintergrasp->GetDefenderTeam() == TEAM_HORDE) + return true; + return false; + } }; class condition_is_wintergrasp_alliance : public ConditionScript { -public: - condition_is_wintergrasp_alliance() : ConditionScript("condition_is_wintergrasp_alliance") { } + public: + condition_is_wintergrasp_alliance() : ConditionScript("condition_is_wintergrasp_alliance") { } - bool OnConditionCheck(Condition const* /* condition */, ConditionSourceInfo& /* sourceInfo */) - { - Battlefield* wintergrasp = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG); - if (wintergrasp->IsEnabled() && wintergrasp->GetDefenderTeam() == TEAM_ALLIANCE) - return true; - return false; - } + bool OnConditionCheck(Condition const* /* condition */, ConditionSourceInfo& /* sourceInfo */) + { + Battlefield* wintergrasp = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG); + if (wintergrasp && wintergrasp->IsEnabled() && wintergrasp->GetDefenderTeam() == TEAM_ALLIANCE) + return true; + return false; + } }; void AddSC_wintergrasp() diff --git a/src/server/scripts/Outland/BlackTemple/black_temple.h b/src/server/scripts/Outland/BlackTemple/black_temple.h index 0856639f4c2..9d2c3dacb3f 100644 --- a/src/server/scripts/Outland/BlackTemple/black_temple.h +++ b/src/server/scripts/Outland/BlackTemple/black_temple.h @@ -47,27 +47,29 @@ enum DataTypes DATA_BLOOD_ELF_COUNCIL_VOICE = 15, DATA_GO_ILLIDAN_GATE = 16, - DATA_GO_ILLIDAN_DOOR_R = 17, - DATA_GO_ILLIDAN_DOOR_L = 18 }; enum CreatureIds { + //Bosses NPC_HIGH_WARLORD_NAJENTUS = 22887, NPC_SUPREMUS = 22898, NPC_SHADE_OF_AKAMA = 22841, - NPC_AKAMA_SHADE = 23191, // This is the Akama that starts the Shade of Akama encounter. - NPC_AKAMA = 23089, // This is the Akama that starts the Illidan encounter. + NPC_TERON_GOREFIEND = 22871, + NPC_GURTOGG_BLOODBOIL = 22948, + NPC_RELIQUARY_OF_SOULS = 22856, + NPC_MOTHER_SHAHRAZ = 22947, + NPC_ILLIDARI_COUNCIL = 23426, + NPC_ILLIDAN_STORMRAGE = 22917, + //Misc NPC_GATHIOS_THE_SHATTERER = 22949, NPC_HIGH_NETHERMANCER_ZEREVOR = 22950, NPC_LADY_MALANDE = 22951, NPC_VERAS_DARKSHADOW = 22952, - NPC_ILLIDARI_COUNCIL = 23426, NPC_BLOOD_ELF_COUNCIL_VOICE = 23499, - - NPC_ILLIDAN_STORMRAGE = 22917, - + NPC_AKAMA = 23089, // This is the Akama that starts the Illidan encounter. + NPC_AKAMA_SHADE = 23191, // This is the Akama that starts the Shade of Akama encounter. NPC_SUPREMUS_VOLCANO = 23085 }; diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp index a2215862219..07578b4c9ae 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp @@ -539,8 +539,7 @@ public: void EnterCombat(Unit* /*who*/) override { me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); - me->setActive(true); - DoZoneInCombat(); + _EnterCombat(); } void AttackStart(Unit* who) override @@ -561,9 +560,6 @@ public: { me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - for (uint8 i = DATA_GO_ILLIDAN_DOOR_R; i < DATA_GO_ILLIDAN_DOOR_L + 1; ++i) - instance->HandleGameObject(instance->GetGuidData(i), true); - _JustDied(); } @@ -1412,23 +1408,13 @@ public: IllidanGUID = instance->GetGuidData(DATA_ILLIDAN_STORMRAGE); GateGUID = instance->GetGuidData(DATA_GO_ILLIDAN_GATE); - DoorGUID[0] = instance->GetGuidData(DATA_GO_ILLIDAN_DOOR_R); - DoorGUID[1] = instance->GetGuidData(DATA_GO_ILLIDAN_DOOR_L); if (JustCreated) // close all doors at create - { instance->HandleGameObject(GateGUID, false); - - for (uint8 i = 0; i < 2; ++i) - instance->HandleGameObject(DoorGUID[i], false); - } else // open all doors, raid wiped { instance->HandleGameObject(GateGUID, true); WalkCount = 1; // skip first wp - - for (uint8 i = 0; i < 2; ++i) - instance->HandleGameObject(DoorGUID[i], true); } KillAllElites(); @@ -1480,9 +1466,6 @@ public: void BeginTalk() { - instance->SetBossState(DATA_ILLIDAN_STORMRAGE, IN_PROGRESS); - for (uint8 i = 0; i < 2; ++i) - instance->HandleGameObject(DoorGUID[i], false); if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID)) { illidan->RemoveAurasDueToSpell(SPELL_KNEEL); @@ -1674,10 +1657,6 @@ public: { switch (WalkCount) { - case 6: - for (uint8 i = 0; i < 2; ++i) - instance->HandleGameObject(DoorGUID[i], true); - break; case 8: if (Phase == PHASE_WALK) EnterPhase(PHASE_TALK); @@ -1795,7 +1774,6 @@ public: ObjectGuid ChannelGUID; ObjectGuid SpiritGUID[2]; ObjectGuid GateGUID; - ObjectGuid DoorGUID[2]; uint32 ChannelCount; uint32 WalkCount; uint32 TalkCount; diff --git a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp index 347843ec7ff..d83e9f8aed9 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp @@ -46,7 +46,7 @@ enum Spells SPELL_FIXATE = 40607, SPELL_CHAIN_LIGHTNING = 39945, SPELL_DESTRUCTIVE_POISON = 40874, - SPELL_AKAMA_SOUL_EXPEL = 40902, + SPELL_AKAMA_SOUL_RETRIEVE = 40902, // Shade SPELL_THREAT = 41602, SPELL_SHADE_OF_AKAMA_TRIGGER = 40955, @@ -108,7 +108,7 @@ enum Events EVENT_CHAIN_LIGHTNING = 4, EVENT_DESTRUCTIVE_POISON = 5, EVENT_START_BROKEN_FREE = 6, - EVENT_START_SOUL_EXPEL = 7, + EVENT_START_SOUL_RETRIEVE = 7, EVENT_EVADE_CHECK = 8, EVENT_BROKEN_FREE_1 = 9, EVENT_BROKEN_FREE_2 = 10, @@ -246,11 +246,11 @@ public: events.ScheduleEvent(EVENT_START_CHANNELERS_AND_SPAWNERS, Seconds(1)); me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); events.ScheduleEvent(EVENT_EVADE_CHECK, Seconds(10)); - if (Creature* akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) + if (Creature* akama = instance->GetCreature(DATA_AKAMA_SHADE)) AttackStart(akama); } - if (spell->Id == SPELL_AKAMA_SOUL_EXPEL) + if (spell->Id == SPELL_AKAMA_SOUL_RETRIEVE) DoCastSelf(SPELL_AKAMA_SOUL_EXPEL_CHANNEL); } @@ -273,7 +273,7 @@ public: { DoCastSelf(SPELL_SHADE_OF_AKAMA_TRIGGER); - if (Creature* akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) + if (Creature* akama = instance->GetCreature(DATA_AKAMA_SHADE)) akama->AI()->DoAction(ACTION_SHADE_OF_AKAMA_DEAD); for (ObjectGuid const& spawnerGuid : _spawners) @@ -401,7 +401,7 @@ public: _isInCombat = true; me->SetWalk(false); me->RemoveAurasDueToSpell(SPELL_AKAMA_SOUL_CHANNEL); - if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) { shade->RemoveAurasDueToSpell(SPELL_AKAMA_SOUL_CHANNEL); AttackStart(shade); @@ -445,7 +445,7 @@ public: { me->SetWalk(false); me->SetFacingTo(0.08726646f, true); - _events.ScheduleEvent(EVENT_START_SOUL_EXPEL, Seconds(1)); + _events.ScheduleEvent(EVENT_START_SOUL_RETRIEVE, Seconds(1)); } } @@ -489,14 +489,14 @@ public: break; case EVENT_CHAIN_LIGHTNING: DoCastVictim(SPELL_CHAIN_LIGHTNING); - _events.Repeat(randtime(Seconds(8), Seconds(15))); + _events.Repeat(Seconds(8), Seconds(15)); break; case EVENT_DESTRUCTIVE_POISON: DoCastSelf(SPELL_DESTRUCTIVE_POISON); - _events.Repeat(randtime(Seconds(3), Seconds(7))); + _events.Repeat(Seconds(3), Seconds(7)); break; - case EVENT_START_SOUL_EXPEL: - DoCast(SPELL_AKAMA_SOUL_EXPEL); + case EVENT_START_SOUL_RETRIEVE: + DoCast(SPELL_AKAMA_SOUL_RETRIEVE); _events.ScheduleEvent(EVENT_START_BROKEN_FREE, Seconds(15)); break; case EVENT_START_BROKEN_FREE: @@ -541,7 +541,7 @@ public: { _summons.DespawnAll(); Talk(SAY_DEAD); - if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) if (shade->IsAlive()) shade->AI()->EnterEvadeMode(EVADE_REASON_OTHER); } @@ -587,7 +587,7 @@ public: { _scheduler.Schedule(Seconds(2), [this](TaskContext channel) { - if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) { if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) DoCastSelf(SPELL_SHADE_SOUL_CHANNEL); @@ -657,12 +657,12 @@ public: if (_leftSide) { _events.ScheduleEvent(EVENT_SPAWN_WAVE_B, Milliseconds(100)); - _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, randtime(Seconds(2), Seconds(5))); + _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, Seconds(2), Seconds(5)); } else { _events.ScheduleEvent(EVENT_SPAWN_WAVE_B, Seconds(10)); - _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, randtime(Seconds(2), Seconds(5))); + _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, Seconds(2), Seconds(5)); } break; case ACTION_STOP_SPAWNING: @@ -687,15 +687,15 @@ public: { case EVENT_SPAWN_WAVE_B: DoCastSelf(SPELL_ASHTONGUE_WAVE_B); - _events.Repeat(randtime(Seconds(50), Seconds(60))); + _events.Repeat(Seconds(50), Seconds(60)); break; case EVENT_SUMMON_ASHTONGUE_SORCERER: // left DoCastSelf(SPELL_SUMMON_ASHTONGUE_SORCERER); - _events.Repeat(randtime(Seconds(30), Seconds(35))); + _events.Repeat(Seconds(30), Seconds(35)); break; case EVENT_SUMMON_ASHTONGUE_DEFENDER: // right DoCastSelf(SPELL_SUMMON_ASHTONGUE_DEFENDER); - _events.Repeat(randtime(Seconds(30), Seconds(40))); + _events.Repeat(Seconds(30), Seconds(40)); break; default: break; @@ -736,16 +736,13 @@ public: void Reset() override { - if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) { if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) me->GetMotionMaster()->MovePoint(0, shade->GetPosition()); - else - { - if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) - AttackStart(akama); - } + else if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) + AttackStart(akama); } Initialize(); } @@ -777,7 +774,7 @@ public: _scheduler.Schedule(Seconds(1) + Milliseconds(500), [this](TaskContext sorcer_channel) { - if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) { if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) { @@ -789,7 +786,7 @@ public: { me->InterruptSpell(CURRENT_CHANNELED_SPELL); _switchToCombat = true; - if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) AttackStart(akama); } } @@ -837,7 +834,7 @@ public: void Reset() override { - if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) AttackStart(akama); } @@ -849,9 +846,9 @@ public: void EnterCombat(Unit* /*who*/) override { _events.ScheduleEvent(EVENT_HEROIC_STRIKE, Seconds(5)); - _events.ScheduleEvent(EVENT_SHIELD_BASH, randtime(Seconds(10), Seconds(16))); - _events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, randtime(Seconds(10), Seconds(16))); - _events.ScheduleEvent(EVENT_WINDFURY, randtime(Seconds(8), Seconds(12))); + _events.ScheduleEvent(EVENT_SHIELD_BASH, Seconds(10), Seconds(16)); + _events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, Seconds(10), Seconds(16)); + _events.ScheduleEvent(EVENT_WINDFURY, Seconds(8), Seconds(12)); } @@ -868,19 +865,19 @@ public: { case EVENT_DEBILITATING_STRIKE: DoCastVictim(SPELL_DEBILITATING_STRIKE); - _events.Repeat(randtime(Seconds(20), Seconds(25))); + _events.Repeat(Seconds(20), Seconds(25)); break; case EVENT_HEROIC_STRIKE: DoCastSelf(SPELL_HEROIC_STRIKE); - _events.Repeat(randtime(Seconds(5), Seconds(15))); + _events.Repeat(Seconds(5), Seconds(15)); break; case EVENT_SHIELD_BASH: DoCastVictim(SPELL_SHIELD_BASH); - _events.Repeat(randtime(Seconds(10), Seconds(20))); + _events.Repeat(Seconds(10), Seconds(20)); break; case EVENT_WINDFURY: DoCastVictim(SPELL_WINDFURY); - _events.Repeat(randtime(Seconds(6), Seconds(8))); + _events.Repeat(Seconds(6), Seconds(8)); break; default: break; @@ -915,7 +912,7 @@ public: void Reset() override { - if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) AttackStart(akama); } @@ -926,8 +923,8 @@ public: void EnterCombat(Unit* /*who*/) override { - _events.ScheduleEvent(EVENT_DEBILITATING_POISON, randtime(Milliseconds(500), Seconds(2))); - _events.ScheduleEvent(EVENT_EVISCERATE, randtime(Seconds(2), Seconds(5))); + _events.ScheduleEvent(EVENT_DEBILITATING_POISON, Milliseconds(500), Seconds(2)); + _events.ScheduleEvent(EVENT_EVISCERATE, Seconds(2), Seconds(5)); } void EnterEvadeMode(EvadeReason /*why*/) override { } @@ -945,11 +942,11 @@ public: { case EVENT_DEBILITATING_POISON: DoCastVictim(SPELL_DEBILITATING_POISON); - _events.Repeat(randtime(Seconds(15), Seconds(20))); + _events.Repeat(Seconds(15), Seconds(20)); break; case EVENT_EVISCERATE: DoCastVictim(SPELL_EVISCERATE); - _events.Repeat(randtime(Seconds(12), Seconds(20))); + _events.Repeat(Seconds(12), Seconds(20)); break; default: break; @@ -984,7 +981,7 @@ public: void Reset() override { - if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) AttackStart(akama); } @@ -1014,11 +1011,11 @@ public: { case EVENT_RAIN_OF_FIRE: DoCastVictim(SPELL_RAIN_OF_FIRE); - _events.Repeat(randtime(Seconds(15), Seconds(20))); + _events.Repeat(Seconds(15), Seconds(20)); break; case EVENT_LIGHTNING_BOLT: DoCastVictim(SPELL_LIGHTNING_BOLT); - _events.Repeat(randtime(Seconds(8), Seconds(15))); + _events.Repeat(Seconds(8), Seconds(15)); break; default: break; @@ -1062,7 +1059,7 @@ public: { Initialize(); - if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) AttackStart(akama); } @@ -1073,7 +1070,7 @@ public: void EnterCombat(Unit* /*who*/) override { - _events.ScheduleEvent(EVENT_SPIRIT_HEAL, randtime(Seconds(5), Seconds(6))); + _events.ScheduleEvent(EVENT_SPIRIT_HEAL, Seconds(5), Seconds(6)); } void DamageTaken(Unit* /*who*/, uint32& /*damage*/) override @@ -1083,7 +1080,7 @@ public: { DoCastSelf(SPELL_SPIRIT_MEND); _spiritMend = true; - _events.ScheduleEvent(EVENT_SPIRIT_MEND_RESET, randtime(Seconds(10),Seconds(15))); + _events.ScheduleEvent(EVENT_SPIRIT_MEND_RESET, Seconds(10),Seconds(15)); } if (!_chainHeal) @@ -1091,7 +1088,7 @@ public: { DoCastSelf(SPELL_CHAIN_HEAL); _chainHeal = true; - _events.ScheduleEvent(EVENT_CHAIN_HEAL_RESET, randtime(Seconds(10), Seconds(15))); + _events.ScheduleEvent(EVENT_CHAIN_HEAL_RESET, Seconds(10), Seconds(15)); } } @@ -1108,7 +1105,7 @@ public: { case EVENT_SPIRIT_HEAL: DoCastSelf(SPELL_SPIRITBINDER_SPIRIT_HEAL); - _events.Repeat(randtime(Seconds(13), Seconds(16))); + _events.Repeat(Seconds(13), Seconds(16)); break; case EVENT_SPIRIT_MEND_RESET: _spiritMend = false; @@ -1157,7 +1154,7 @@ public: if (motionType != POINT_MOTION_TYPE) return; - if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) me->SetFacingToObject(akama); } diff --git a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp index bac996918ac..2784792fe8d 100644 --- a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp +++ b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp @@ -22,17 +22,20 @@ DoorData const doorData[] = { { GO_NAJENTUS_GATE, DATA_HIGH_WARLORD_NAJENTUS, DOOR_TYPE_PASSAGE }, - { GO_NAJENTUS_GATE, DATA_SUPREMUS, DOOR_TYPE_ROOM }, + { GO_NAJENTUS_GATE, DATA_SUPREMUS, DOOR_TYPE_ROOM }, { GO_SUPREMUS_GATE, DATA_SUPREMUS, DOOR_TYPE_PASSAGE }, - { GO_SHADE_OF_AKAMA_DOOR, DATA_SHADE_OF_AKAMA, DOOR_TYPE_ROOM }, - { GO_TERON_DOOR_1, DATA_TERON_GOREFIEND, DOOR_TYPE_ROOM }, - { GO_TERON_DOOR_2, DATA_TERON_GOREFIEND, DOOR_TYPE_ROOM }, + { GO_SHADE_OF_AKAMA_DOOR, DATA_SHADE_OF_AKAMA, DOOR_TYPE_ROOM }, + { GO_TERON_DOOR_1, DATA_TERON_GOREFIEND, DOOR_TYPE_ROOM }, + { GO_TERON_DOOR_2, DATA_TERON_GOREFIEND, DOOR_TYPE_ROOM }, { GO_GURTOGG_DOOR, DATA_GURTOGG_BLOODBOIL, DOOR_TYPE_PASSAGE }, { GO_TEMPLE_DOOR, DATA_RELIQUARY_OF_SOULS, DOOR_TYPE_PASSAGE }, { GO_MOTHER_SHAHRAZ_DOOR, DATA_MOTHER_SHAHRAZ, DOOR_TYPE_PASSAGE }, - { GO_COUNCIL_DOOR_1, DATA_ILLIDARI_COUNCIL, DOOR_TYPE_ROOM }, - { GO_COUNCIL_DOOR_2, DATA_ILLIDARI_COUNCIL, DOOR_TYPE_ROOM }, - { 0, 0, DOOR_TYPE_ROOM } // END + { GO_COUNCIL_DOOR_1, DATA_ILLIDARI_COUNCIL, DOOR_TYPE_ROOM }, + { GO_COUNCIL_DOOR_2, DATA_ILLIDARI_COUNCIL, DOOR_TYPE_ROOM }, + //{ GO_ILLIDAN_GATE, DATA_GO_ILLIDAN_GATE, DOOR_TYPE_PASSAGE }, + { GO_ILLIDAN_DOOR_R, DATA_ILLIDAN_STORMRAGE, DOOR_TYPE_ROOM }, + { GO_ILLIDAN_DOOR_L, DATA_ILLIDAN_STORMRAGE, DOOR_TYPE_ROOM }, + { 0, 0, DOOR_TYPE_ROOM } // END }; BossBoundaryData const boundaries = @@ -49,6 +52,27 @@ BossBoundaryData const boundaries = { DATA_ILLIDAN_STORMRAGE, new EllipseBoundary(Position(694.8f, 309.0f), 70.0 , 85.0) } }; +ObjectData const creatureData[] = +{ + { NPC_HIGH_WARLORD_NAJENTUS, DATA_HIGH_WARLORD_NAJENTUS }, + { NPC_SUPREMUS, DATA_SUPREMUS }, + { NPC_SHADE_OF_AKAMA, DATA_SHADE_OF_AKAMA }, + { NPC_TERON_GOREFIEND, DATA_TERON_GOREFIEND }, + { NPC_GURTOGG_BLOODBOIL, DATA_GURTOGG_BLOODBOIL }, + { NPC_RELIQUARY_OF_SOULS, DATA_RELIQUARY_OF_SOULS }, + { NPC_MOTHER_SHAHRAZ, DATA_MOTHER_SHAHRAZ }, + { NPC_ILLIDARI_COUNCIL, DATA_ILLIDARI_COUNCIL }, + { NPC_ILLIDAN_STORMRAGE, DATA_ILLIDAN_STORMRAGE }, + { NPC_AKAMA_SHADE, DATA_AKAMA_SHADE }, + { NPC_AKAMA, DATA_AKAMA }, + { NPC_GATHIOS_THE_SHATTERER, DATA_GATHIOS_THE_SHATTERER }, + { NPC_HIGH_NETHERMANCER_ZEREVOR, DATA_HIGH_NETHERMANCER_ZEREVOR }, + { NPC_LADY_MALANDE, DATA_LADY_MALANDE }, + { NPC_VERAS_DARKSHADOW, DATA_VERAS_DARKSHADOW }, + { NPC_BLOOD_ELF_COUNCIL_VOICE, DATA_BLOOD_ELF_COUNCIL_VOICE }, + { 0, 0 } // end +}; + class instance_black_temple : public InstanceMapScript { public: @@ -61,165 +85,28 @@ class instance_black_temple : public InstanceMapScript SetHeaders(DataHeader); SetBossNumber(EncounterCount); LoadDoorData(doorData); + LoadObjectData(creatureData, nullptr); LoadBossBoundaries(boundaries); } - void OnCreatureCreate(Creature* creature) override - { - switch (creature->GetEntry()) - { - case NPC_HIGH_WARLORD_NAJENTUS: - NajentusGUID = creature->GetGUID(); - break; - case NPC_SUPREMUS: - SupremusGUID = creature->GetGUID(); - break; - case NPC_SHADE_OF_AKAMA: - ShadeOfAkamaGUID = creature->GetGUID(); - break; - case NPC_AKAMA_SHADE: - AkamaShadeGUID = creature->GetGUID(); - break; - case NPC_AKAMA: - AkamaGUID = creature->GetGUID(); - break; - case NPC_GATHIOS_THE_SHATTERER: - GathiosTheShattererGUID = creature->GetGUID(); - break; - case NPC_HIGH_NETHERMANCER_ZEREVOR: - HighNethermancerZerevorGUID = creature->GetGUID(); - break; - case NPC_LADY_MALANDE: - LadyMalandeGUID = creature->GetGUID(); - break; - case NPC_VERAS_DARKSHADOW: - VerasDarkshadowGUID = creature->GetGUID(); - break; - case NPC_ILLIDARI_COUNCIL: - IllidariCouncilGUID = creature->GetGUID(); - break; - case NPC_BLOOD_ELF_COUNCIL_VOICE: - BloodElfCouncilVoiceGUID = creature->GetGUID(); - break; - case NPC_ILLIDAN_STORMRAGE: - IllidanStormrageGUID = creature->GetGUID(); - break; - default: - break; - } - } - void OnGameObjectCreate(GameObject* go) override { - switch (go->GetEntry()) - { - case GO_NAJENTUS_GATE: - case GO_SUPREMUS_GATE: - case GO_SHADE_OF_AKAMA_DOOR: - case GO_TERON_DOOR_1: - case GO_TERON_DOOR_2: - case GO_GURTOGG_DOOR: - case GO_TEMPLE_DOOR: - case GO_MOTHER_SHAHRAZ_DOOR: - case GO_COUNCIL_DOOR_1: - case GO_COUNCIL_DOOR_2: - AddDoor(go, true); - break; - case GO_ILLIDAN_GATE: - IllidanGateGUID = go->GetGUID(); - break; - case GO_ILLIDAN_DOOR_R: - IllidanDoorGUIDs[0] = go->GetGUID(); - break; - case GO_ILLIDAN_DOOR_L: - IllidanDoorGUIDs[1] = go->GetGUID(); - break; - default: - break; - } - } + if (go->GetEntry() == GO_ILLIDAN_GATE) + IllidanGateGUID = go->GetGUID(); - void OnGameObjectRemove(GameObject* go) override - { - switch (go->GetEntry()) - { - case GO_NAJENTUS_GATE: - case GO_SUPREMUS_GATE: - case GO_SHADE_OF_AKAMA_DOOR: - case GO_TERON_DOOR_1: - case GO_TERON_DOOR_2: - case GO_GURTOGG_DOOR: - case GO_TEMPLE_DOOR: - case GO_MOTHER_SHAHRAZ_DOOR: - case GO_COUNCIL_DOOR_1: - case GO_COUNCIL_DOOR_2: - AddDoor(go, false); - break; - default: - break; - } + InstanceScript::OnGameObjectCreate(go); } ObjectGuid GetGuidData(uint32 type) const override { - switch (type) - { - case DATA_HIGH_WARLORD_NAJENTUS: - return NajentusGUID; - case DATA_SUPREMUS: - return SupremusGUID; - case DATA_SHADE_OF_AKAMA: - return ShadeOfAkamaGUID; - case DATA_AKAMA_SHADE: - return AkamaShadeGUID; - case DATA_AKAMA: - return AkamaGUID; - case DATA_GATHIOS_THE_SHATTERER: - return GathiosTheShattererGUID; - case DATA_HIGH_NETHERMANCER_ZEREVOR: - return HighNethermancerZerevorGUID; - case DATA_LADY_MALANDE: - return LadyMalandeGUID; - case DATA_VERAS_DARKSHADOW: - return VerasDarkshadowGUID; - case DATA_ILLIDARI_COUNCIL: - return IllidariCouncilGUID; - case DATA_BLOOD_ELF_COUNCIL_VOICE: - return BloodElfCouncilVoiceGUID; - case DATA_ILLIDAN_STORMRAGE: - return IllidanStormrageGUID; - case DATA_GO_ILLIDAN_GATE: - return IllidanGateGUID; - case DATA_GO_ILLIDAN_DOOR_R: - return IllidanDoorGUIDs[0]; - case DATA_GO_ILLIDAN_DOOR_L: - return IllidanDoorGUIDs[1]; - default: - break; - } + if (type == DATA_GO_ILLIDAN_GATE) + return IllidanGateGUID; - return ObjectGuid::Empty; + return InstanceScript::GetGuidData(type); } protected: - ObjectGuid NajentusGUID; - ObjectGuid SupremusGUID; - ObjectGuid ShadeOfAkamaGUID; - ObjectGuid AkamaShadeGUID; - ObjectGuid AkamaGUID; - - ObjectGuid GathiosTheShattererGUID; - ObjectGuid HighNethermancerZerevorGUID; - ObjectGuid LadyMalandeGUID; - ObjectGuid VerasDarkshadowGUID; - - ObjectGuid IllidariCouncilGUID; - ObjectGuid BloodElfCouncilVoiceGUID; - - ObjectGuid IllidanStormrageGUID; - ObjectGuid IllidanGateGUID; - ObjectGuid IllidanDoorGUIDs[2]; }; InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index 90bf8bb1948..3b364d557ed 100644 --- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp +++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp @@ -332,8 +332,14 @@ public: enum FelGuard { - SPELL_SUMMON_POO = 37688, - NPC_DERANGED_HELBOAR = 16863 + SPELL_SUMMON_POO = 37688, + SPELL_FAKE_BLOOD = 37692, + NPC_DERANGED_HELBOAR = 16863, + + EVENT_SEARCH_HELBOAR = 1, + EVENT_HELBOAR_FOUND = 2, + EVENT_SUMMON_POO = 3, + EVENT_FOLLOW_PLAYER = 4 }; class npc_fel_guard_hound : public CreatureScript @@ -350,8 +356,8 @@ public: void Initialize() { - checkTimer = 5000; //check for creature every 5 sec helboarGUID.Clear(); + _events.ScheduleEvent(EVENT_SEARCH_HELBOAR, Seconds(3)); } void Reset() override @@ -366,29 +372,54 @@ public: if (Creature* helboar = ObjectAccessor::GetCreature(*me, helboarGUID)) { - helboar->RemoveCorpse(); - DoCast(SPELL_SUMMON_POO); - - if (Player* owner = me->GetCharmerOrOwnerPlayerOrPlayerItself()) - me->GetMotionMaster()->MoveFollow(owner, 0.0f, 0.0f); + _events.CancelEvent(EVENT_SEARCH_HELBOAR); + me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK_UNARMED); + me->CastSpell(helboar, SPELL_FAKE_BLOOD); + _events.ScheduleEvent(EVENT_HELBOAR_FOUND, Seconds(2)); } } void UpdateAI(uint32 diff) override { - if (checkTimer <= diff) + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) { - if (Creature* helboar = me->FindNearestCreature(NPC_DERANGED_HELBOAR, 10.0f, false)) + switch (eventId) { - if (helboar->GetGUID() != helboarGUID && me->GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE && !me->FindCurrentSpellBySpellId(SPELL_SUMMON_POO)) - { - helboarGUID = helboar->GetGUID(); - me->GetMotionMaster()->MovePoint(1, helboar->GetPositionX(), helboar->GetPositionY(), helboar->GetPositionZ()); - } + case EVENT_SEARCH_HELBOAR: + if (Creature* helboar = me->FindNearestCreature(NPC_DERANGED_HELBOAR, 10.0f, false)) + { + if (helboar->GetGUID() != helboarGUID && me->GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE && !me->FindCurrentSpellBySpellId(SPELL_SUMMON_POO)) + { + helboarGUID = helboar->GetGUID(); + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(1, helboar->GetPositionX(), helboar->GetPositionY(), helboar->GetPositionZ()); + helboar->DespawnOrUnsummon(Seconds(10)); + } + } + _events.Repeat(Seconds(3)); + break; + case EVENT_HELBOAR_FOUND: + if (Creature* helboar = ObjectAccessor::GetCreature(*me, helboarGUID)) + { + me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK_UNARMED); + me->CastSpell(helboar, SPELL_FAKE_BLOOD); + _events.ScheduleEvent(EVENT_SUMMON_POO, Seconds(1)); + } + break; + case EVENT_SUMMON_POO: + DoCast(SPELL_SUMMON_POO); + _events.ScheduleEvent(EVENT_FOLLOW_PLAYER, Seconds(2)); + break; + case EVENT_FOLLOW_PLAYER: + me->SetWalk(false); + if (Player* owner = me->GetCharmerOrOwnerPlayerOrPlayerItself()) + me->GetMotionMaster()->MoveFollow(owner, 0.0f, 0.0f); + _events.ScheduleEvent(EVENT_SEARCH_HELBOAR, Seconds(3)); + break; } - checkTimer = 5000; } - else checkTimer -= diff; if (!UpdateVictim()) return; @@ -397,7 +428,7 @@ public: } private: - uint32 checkTimer; + EventMap _events; ObjectGuid helboarGUID; }; @@ -892,6 +923,122 @@ public: } }; +enum Aledis +{ + SAY_CHALLENGE = 0, + SAY_DEFEATED = 1, + EVENT_TALK = 1, + EVENT_ATTACK = 2, + EVENT_EVADE = 3, + EVENT_FIREBALL = 4, + EVENT_FROSTNOVA = 5, + SPELL_FIREBALL = 20823, + SPELL_FROSTNOVA = 11831 +}; + +class npc_magister_aledis : public CreatureScript +{ +public: + npc_magister_aledis() : CreatureScript("npc_magister_aledis") { } + + bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 /*action*/) override + { + CloseGossipMenuFor(player); + creature->StopMoving(); + ENSURE_AI(npc_magister_aledis::npc_magister_aledisAI, creature->AI())->StartFight(player); + return true; + } + + struct npc_magister_aledisAI : public ScriptedAI + { + npc_magister_aledisAI(Creature* creature) : ScriptedAI(creature) { } + + void StartFight(Player* player) + { + me->Dismount(); + me->SetFacingToObject(player, true); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + _playerGUID = player->GetGUID(); + _events.ScheduleEvent(EVENT_TALK, Seconds(2)); + } + + void Reset() override + { + me->RestoreFaction(); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + } + + void DamageTaken(Unit* /*attacker*/, uint32 &damage) override + { + if (damage > me->GetHealth() || me->HealthBelowPctDamaged(20, damage)) + { + damage = 0; + + _events.Reset(); + me->RestoreFaction(); + me->RemoveAllAuras(); + me->DeleteThreatList(); + me->CombatStop(true); + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + Talk(SAY_DEFEATED); + + _events.ScheduleEvent(EVENT_EVADE, Minutes(1)); + } + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_TALK: + Talk(SAY_CHALLENGE); + _events.ScheduleEvent(EVENT_ATTACK, Seconds(2)); + break; + case EVENT_ATTACK: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + me->setFaction(FACTION_HOSTILE); + me->CombatStart(ObjectAccessor::GetPlayer(*me, _playerGUID)); + _events.ScheduleEvent(EVENT_FIREBALL, 1); + _events.ScheduleEvent(EVENT_FROSTNOVA, Seconds(5)); + break; + case EVENT_FIREBALL: + DoCast(SPELL_FIREBALL); + _events.ScheduleEvent(EVENT_FIREBALL, Seconds(10)); + break; + case EVENT_FROSTNOVA: + DoCastAOE(SPELL_FROSTNOVA); + _events.ScheduleEvent(EVENT_FROSTNOVA, Seconds(20)); + break; + case EVENT_EVADE: + EnterEvadeMode(); + break; + } + } + + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); + } + + private: + EventMap _events; + ObjectGuid _playerGUID; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_magister_aledisAI(creature); + } +}; + void AddSC_hellfire_peninsula() { new npc_aeranas(); @@ -900,4 +1047,5 @@ void AddSC_hellfire_peninsula() new npc_fel_guard_hound(); new npc_colonel_jules(); new npc_barada(); + new npc_magister_aledis(); } diff --git a/src/server/scripts/Pet/pet_hunter.cpp b/src/server/scripts/Pet/pet_hunter.cpp index 5b4b26e599e..a155dbc36c4 100644 --- a/src/server/scripts/Pet/pet_hunter.cpp +++ b/src/server/scripts/Pet/pet_hunter.cpp @@ -215,7 +215,7 @@ class spell_pet_guard_dog : public SpellScriptLoader Unit* caster = eventInfo.GetActor(); caster->CastSpell((Unit*)nullptr, SPELL_PET_GUARD_DOG_HAPPINESS, true); - float addThreat = CalculatePct(eventInfo.GetSpellInfo()->Effects[EFFECT_0].CalcValue(caster), aurEff->GetAmount()); + float addThreat = CalculatePct(ASSERT_NOTNULL(eventInfo.GetSpellInfo())->Effects[EFFECT_0].CalcValue(caster), aurEff->GetAmount()); eventInfo.GetProcTarget()->AddThreat(caster, addThreat); } diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index a1543e1199c..7cce7e4655e 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -2801,17 +2801,25 @@ public: class player_ghoulAI : public PlayerAI { public: - player_ghoulAI(Player* player, ObjectGuid ghoulGUID) : PlayerAI(player), _ghoulGUID(ghoulGUID) { } + player_ghoulAI(Player* player, ObjectGuid ghoulGUID) : PlayerAI(player), _ghoulGUID(ghoulGUID), _ghoulCheckTimer(1000){ } - void UpdateAI(uint32 /*diff*/) override + void UpdateAI(uint32 diff) override { - Creature* ghoul = ObjectAccessor::GetCreature(*me, _ghoulGUID); - if (!ghoul || !ghoul->IsAlive()) - me->RemoveAura(SPELL_DK_RAISE_ALLY); + if (_ghoulCheckTimer <= diff) + { + _ghoulCheckTimer = 1000; + + Creature* ghoul = ObjectAccessor::GetCreature(*me, _ghoulGUID); + if (!ghoul || !ghoul->IsAlive()) + me->RemoveAura(SPELL_DK_RAISE_ALLY); + } + else + _ghoulCheckTimer -= diff; } private: ObjectGuid _ghoulGUID; + uint32 _ghoulCheckTimer; }; // 46619 - Raise Ally @@ -2833,7 +2841,7 @@ public: void SendText() { if (Unit* original = GetOriginalCaster()) - original->Whisper(TEXT_RISE_ALLY, GetCaster()->ToPlayer(), true); + original->Unit::Whisper(TEXT_RISE_ALLY, GetCaster()->ToPlayer(), true); } void HandleSummon(SpellEffIndex effIndex) diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 794ef470d40..f1eaf45770e 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -1100,9 +1100,17 @@ class spell_gen_creature_permanent_feign_death : public SpellScriptLoader target->ToCreature()->SetReactState(REACT_PASSIVE); } + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); + target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); + } + void Register() override { OnEffectApply += AuraEffectApplyFn(spell_gen_creature_permanent_feign_death_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + OnEffectRemove += AuraEffectRemoveFn(spell_gen_creature_permanent_feign_death_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); } }; diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index e95bfe4395c..e18dc838965 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -275,6 +275,70 @@ class spell_hun_chimera_shot : public SpellScriptLoader } }; +// -53256 - Cobra Strikes +class spell_hun_cobra_strikes : public SpellScriptLoader +{ + public: + spell_hun_cobra_strikes() : SpellScriptLoader("spell_hun_cobra_strikes") { } + + class spell_hun_cobra_strikes_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hun_cobra_strikes_AuraScript); + + bool Validate(SpellInfo const* spellInfo) override + { + if (!sSpellMgr->GetSpellInfo(spellInfo->Effects[EFFECT_0].TriggerSpell)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + + SpellInfo const* triggeredSpellInfo = sSpellMgr->AssertSpellInfo(GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell); + GetTarget()->CastCustomSpell(triggeredSpellInfo->Id, SPELLVALUE_AURA_STACK, triggeredSpellInfo->StackAmount, (Unit*)nullptr, true); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_hun_cobra_strikes_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hun_cobra_strikes_AuraScript(); + } +}; + +// 53257 - Cobra Strikes (triggered spell) +class spell_hun_cobra_strikes_triggered : public SpellScriptLoader +{ + public: + spell_hun_cobra_strikes_triggered() : SpellScriptLoader("spell_hun_cobra_strikes_triggered") { } + + class spell_hun_cobra_strikes_triggered_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hun_cobra_strikes_triggered_AuraScript); + + void HandleStackDrop(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + ModStackAmount(-1); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_hun_cobra_strikes_triggered_AuraScript::HandleStackDrop, EFFECT_0, SPELL_AURA_ADD_FLAT_MODIFIER); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hun_cobra_strikes_triggered_AuraScript(); + } +}; + // 781 - Disengage class spell_hun_disengage : public SpellScriptLoader { @@ -388,7 +452,7 @@ class spell_hun_glyph_of_mend_pet : public SpellScriptLoader void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) { PreventDefaultAction(); - eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), SPELL_HUNTER_GLYPH_OF_MEND_PET_HAPPINESS, true); + eventInfo.GetProcTarget()->CastSpell((Unit*)nullptr, SPELL_HUNTER_GLYPH_OF_MEND_PET_HAPPINESS, true); } void Register() override @@ -664,35 +728,6 @@ class spell_hun_lock_and_load : public SpellScriptLoader } }; -// 56342 - Lock and Load (Rank 1) -class spell_hun_lock_and_load_dummy : public SpellScriptLoader -{ - public: - spell_hun_lock_and_load_dummy() : SpellScriptLoader("spell_hun_lock_and_load_dummy") { } - - class spell_hun_lock_and_load_dummy_AuraScript : public AuraScript - { - PrepareAuraScript(spell_hun_lock_and_load_dummy_AuraScript); - - bool DummyCheck(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) - { - // Prevents Aura proc from this dummy effect - // Only rank 1 has this aura - return false; - } - - void Register() override - { - DoCheckEffectProc += AuraCheckEffectProcFn(spell_hun_lock_and_load_dummy_AuraScript::DummyCheck, EFFECT_2, SPELL_AURA_DUMMY); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_hun_lock_and_load_dummy_AuraScript(); - } -}; - // 53271 - Masters Call class spell_hun_masters_call : public SpellScriptLoader { @@ -710,30 +745,58 @@ class spell_hun_masters_call : public SpellScriptLoader return true; } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } + + SpellCastResult DoCheckCast() + { + Pet* pet = GetCaster()->ToPlayer()->GetPet(); + ASSERT(pet); // checked in Spell::CheckCast + + if (!pet->IsAlive()) + return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; + + // Do a mini Spell::CheckCasterAuras on the pet, no other way of doing this + SpellCastResult result = SPELL_CAST_OK; + uint32 const unitflag = pet->GetUInt32Value(UNIT_FIELD_FLAGS); + if (pet->GetCharmerGUID()) + result = SPELL_FAILED_CHARMED; + else if (unitflag & UNIT_FLAG_STUNNED) + result = SPELL_FAILED_STUNNED; + else if (unitflag & UNIT_FLAG_FLEEING) + result = SPELL_FAILED_FLEEING; + else if (unitflag & UNIT_FLAG_CONFUSED) + result = SPELL_FAILED_CONFUSED; + + if (result != SPELL_CAST_OK) + return result; + + Unit* target = GetExplTargetUnit(); + if (!target) + return SPELL_FAILED_BAD_TARGETS; + + if (!pet->IsWithinLOSInMap(target)) + return SPELL_FAILED_LINE_OF_SIGHT; + + return SPELL_CAST_OK; + } + void HandleDummy(SpellEffIndex /*effIndex*/) { - if (Unit* ally = GetHitUnit()) - if (Player* caster = GetCaster()->ToPlayer()) - if (Pet* target = caster->GetPet()) - { - TriggerCastFlags castMask = TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_CASTER_AURASTATE); - target->CastSpell(ally, GetEffectValue(), castMask); - target->CastSpell(ally, GetSpellInfo()->Effects[EFFECT_0].CalcValue(), castMask); - } + GetCaster()->ToPlayer()->GetPet()->CastSpell(GetHitUnit(), GetEffectValue(), true); } void HandleScriptEffect(SpellEffIndex /*effIndex*/) { - if (Unit* target = GetHitUnit()) - { - // Cannot be processed while pet is dead - TriggerCastFlags castMask = TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_CASTER_AURASTATE); - target->CastSpell(target, SPELL_HUNTER_MASTERS_CALL_TRIGGERED, castMask); - } + GetHitUnit()->CastSpell((Unit*)nullptr, SPELL_HUNTER_MASTERS_CALL_TRIGGERED, true); } void Register() override { + OnCheckCast += SpellCheckCastFn(spell_hun_masters_call_SpellScript::DoCheckCast); + OnEffectHitTarget += SpellEffectFn(spell_hun_masters_call_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); OnEffectHitTarget += SpellEffectFn(spell_hun_masters_call_SpellScript::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); } @@ -1505,6 +1568,8 @@ void AddSC_hunter_spell_scripts() new spell_hun_aspect_of_the_beast(); new spell_hun_ascpect_of_the_viper(); new spell_hun_chimera_shot(); + new spell_hun_cobra_strikes(); + new spell_hun_cobra_strikes_triggered(); new spell_hun_disengage(); new spell_hun_glyph_of_arcane_shot(); new spell_hun_glyph_of_mend_pet(); @@ -1514,7 +1579,6 @@ void AddSC_hunter_spell_scripts() new spell_hun_kill_command_pet(); new spell_hun_last_stand_pet(); new spell_hun_lock_and_load(); - new spell_hun_lock_and_load_dummy(); new spell_hun_masters_call(); new spell_hun_misdirection(); new spell_hun_misdirection_proc(); diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index f51b9e8e965..b45df66e0ac 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -1961,6 +1961,8 @@ class spell_item_shadows_fate : public SpellScriptLoader void HandleProc(ProcEventInfo& procInfo) { + PreventDefaultAction(); + Unit* caster = procInfo.GetActor(); Unit* target = GetCaster(); if (!caster || !target) @@ -4343,6 +4345,119 @@ class spell_item_taunt_flag_targeting : public SpellScriptLoader } }; +// 13180 - Gnomish Mind Control Cap +enum MindControlCap +{ + ROLL_CHANCE_DULLARD = 32, + ROLL_CHANCE_NO_BACKFIRE = 95, + SPELL_GNOMISH_MIND_CONTROL_CAP = 13181, + SPELL_DULLARD = 67809 +}; + +class spell_item_mind_control_cap : public SpellScriptLoader +{ + public: + spell_item_mind_control_cap() : SpellScriptLoader("spell_item_mind_control_cap") { } + + class spell_item_mind_control_cap_SpellScript : public SpellScript + { + PrepareSpellScript(spell_item_mind_control_cap_SpellScript); + + bool Load() override + { + if (!GetCastItem()) + return false; + return true; + } + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_GNOMISH_MIND_CONTROL_CAP) || !sSpellMgr->GetSpellInfo(SPELL_DULLARD)) + return false; + return true; + } + + void HandleDummy(SpellEffIndex /* effIndex */) + { + Unit* caster = GetCaster(); + if (Unit* target = GetHitUnit()) + { + if (roll_chance_i(ROLL_CHANCE_NO_BACKFIRE)) + caster->CastSpell(target, roll_chance_i(ROLL_CHANCE_DULLARD) ? SPELL_DULLARD : SPELL_GNOMISH_MIND_CONTROL_CAP, true, GetCastItem()); + else + target->CastSpell(caster, SPELL_GNOMISH_MIND_CONTROL_CAP, true); // backfire - 5% chance + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_mind_control_cap_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_item_mind_control_cap_SpellScript(); + } +}; + +// 8344 - Universal Remote (Gnomish Universal Remote) +enum UniversalRemote +{ + SPELL_CONTROL_MACHINE = 8345, + SPELL_MOBILITY_MALFUNCTION = 8346, + SPELL_TARGET_LOCK = 8347 +}; + +class spell_item_universal_remote : public SpellScriptLoader +{ + public: + spell_item_universal_remote() : SpellScriptLoader("spell_item_universal_remote") { } + + class spell_item_universal_remote_SpellScript : public SpellScript + { + PrepareSpellScript(spell_item_universal_remote_SpellScript); + + bool Load() override + { + if (!GetCastItem()) + return false; + return true; + } + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_CONTROL_MACHINE) || !sSpellMgr->GetSpellInfo(SPELL_MOBILITY_MALFUNCTION) || !sSpellMgr->GetSpellInfo(SPELL_TARGET_LOCK)) + return false; + return true; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + { + uint8 chance = urand(0, 99); + if (chance < 15) + GetCaster()->CastSpell(target, SPELL_TARGET_LOCK, true, GetCastItem()); + else if (chance < 25) + GetCaster()->CastSpell(target, SPELL_MOBILITY_MALFUNCTION, true, GetCastItem()); + else + GetCaster()->CastSpell(target, SPELL_CONTROL_MACHINE, true, GetCastItem()); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_universal_remote_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_item_universal_remote_SpellScript(); + } +}; + enum ZandalarianCharms { SPELL_UNSTABLE_POWER_AURA_STACK = 24659, @@ -4515,6 +4630,8 @@ void AddSC_item_spell_scripts() new spell_item_charm_witch_doctor(); new spell_item_mana_drain(); new spell_item_taunt_flag_targeting(); + new spell_item_mind_control_cap(); + new spell_item_universal_remote(); new spell_item_zandalarian_charm("spell_item_unstable_power", SPELL_UNSTABLE_POWER_AURA_STACK); new spell_item_zandalarian_charm("spell_item_restless_strength", SPELL_RESTLESS_STRENGTH_AURA_STACK); diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index 30dfd2cd5d0..2aaa2e29d9c 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -1303,6 +1303,8 @@ class spell_pal_infusion_of_light : public SpellScriptLoader Unit* target = GetTarget(); int32 duration = sSpellMgr->AssertSpellInfo(SPELL_PALADIN_FLASH_OF_LIGHT_PROC)->GetMaxDuration() / 1000; int32 pct = GetSpellInfo()->Effects[EFFECT_2].CalcValue(); + ASSERT(duration > 0); + int32 bp0 = CalculatePct(healInfo->GetHeal() / duration, pct); // Item - Paladin T9 Holy 4P Bonus @@ -1540,7 +1542,7 @@ class spell_pal_judgement_of_light_heal : public SpellScriptLoader Unit* caster = eventInfo.GetProcTarget(); int32 amount = static_cast<int32>(caster->CountPctFromMaxHealth(aurEff->GetAmount())); - caster->CastCustomSpell(SPELL_PALADIN_JUDGEMENT_OF_LIGHT_HEAL, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true); + caster->CastCustomSpell(SPELL_PALADIN_JUDGEMENT_OF_LIGHT_HEAL, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff, GetCasterGUID()); } void Register() override @@ -1584,7 +1586,7 @@ class spell_pal_judgement_of_wisdom_mana : public SpellScriptLoader Unit* caster = eventInfo.GetProcTarget(); int32 amount = CalculatePct(static_cast<int32>(caster->GetCreateMana()), aurEff->GetAmount()); - caster->CastCustomSpell(SPELL_PALADIN_JUDGEMENT_OF_WISDOM_MANA, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true); + caster->CastCustomSpell(SPELL_PALADIN_JUDGEMENT_OF_WISDOM_MANA, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff, GetCasterGUID()); } void Register() override diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp index 2eca20199b4..fd99888f8d9 100644 --- a/src/server/scripts/Spells/spell_quest.cpp +++ b/src/server/scripts/Spells/spell_quest.cpp @@ -2565,6 +2565,146 @@ public: } }; +enum ApplyHeatAndStir +{ + SPELL_SPURTS_AND_SMOKE = 38594, + SPELL_FAILED_MIX_1 = 43376, + SPELL_FAILED_MIX_2 = 43378, + SPELL_FAILED_MIX_3 = 43970, + SPELL_SUCCESSFUL_MIX = 43377, + + CREATURE_GENERIC_TRIGGER_LAB = 24042, + + TALK_0 = 0, + TALK_1 = 1 +}; + +class spell_q11306_mixing_blood : public SpellScriptLoader +{ +public: + spell_q11306_mixing_blood() : SpellScriptLoader("spell_q11306_mixing_blood") { } + + class spell_q11306_mixing_blood_SpellScript : public SpellScript + { + PrepareSpellScript(spell_q11306_mixing_blood_SpellScript); + + void HandleEffect(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + if (Creature* trigger = caster->FindNearestCreature(CREATURE_GENERIC_TRIGGER_LAB, 100.0f)) + trigger->AI()->DoCastSelf(SPELL_SPURTS_AND_SMOKE); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_q11306_mixing_blood_SpellScript::HandleEffect, EFFECT_1, SPELL_EFFECT_SEND_EVENT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_q11306_mixing_blood_SpellScript(); + } +}; + +class spell_q11306_mixing_vrykul_blood : public SpellScriptLoader +{ + public: + spell_q11306_mixing_vrykul_blood() : SpellScriptLoader("spell_q11306_mixing_vrykul_blood") { } + + class spell_q11306_mixing_vrykul_blood_SpellScript : public SpellScript + { + PrepareSpellScript(spell_q11306_mixing_vrykul_blood_SpellScript); + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + { + uint8 chance = urand(0, 99); + uint32 spellId = 0; + + // 90% chance of getting one out of three failure effects + if (chance < 30) + spellId = SPELL_FAILED_MIX_1; + else if (chance < 60) + spellId = SPELL_FAILED_MIX_2; + else if (chance < 90) + spellId = SPELL_FAILED_MIX_3; + else // 10% chance of successful cast + spellId = SPELL_SUCCESSFUL_MIX; + + caster->CastSpell(caster, spellId, true); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_q11306_mixing_vrykul_blood_SpellScript::HandleDummy, EFFECT_1, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_q11306_mixing_vrykul_blood_SpellScript(); + } +}; + +class spell_q11306_failed_mix_43376 : public SpellScriptLoader +{ +public: + spell_q11306_failed_mix_43376() : SpellScriptLoader("spell_q11306_failed_mix_43376") { } + + class spell_q11306_failed_mix_43376_SpellScript : public SpellScript + { + PrepareSpellScript(spell_q11306_failed_mix_43376_SpellScript); + + void HandleEffect(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + if (Creature* trigger = caster->FindNearestCreature(CREATURE_GENERIC_TRIGGER_LAB, 100.0f)) + trigger->AI()->Talk(TALK_0, caster); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_q11306_failed_mix_43376_SpellScript::HandleEffect, EFFECT_1, SPELL_EFFECT_SEND_EVENT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_q11306_failed_mix_43376_SpellScript(); + } +}; + +class spell_q11306_failed_mix_43378 : public SpellScriptLoader +{ +public: + spell_q11306_failed_mix_43378() : SpellScriptLoader("spell_q11306_failed_mix_43378") { } + + class spell_q11306_failed_mix_43378_SpellScript : public SpellScript + { + PrepareSpellScript(spell_q11306_failed_mix_43378_SpellScript); + + void HandleEffect(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + if (Creature* trigger = caster->FindNearestCreature(CREATURE_GENERIC_TRIGGER_LAB, 100.0f)) + trigger->AI()->Talk(TALK_1, caster); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_q11306_failed_mix_43378_SpellScript::HandleEffect, EFFECT_2, SPELL_EFFECT_SEND_EVENT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_q11306_failed_mix_43378_SpellScript(); + } +}; + void AddSC_quest_spell_scripts() { new spell_q55_sacred_cleansing(); @@ -2628,4 +2768,8 @@ void AddSC_quest_spell_scripts() new spell_q12414_hand_over_reins(); new spell_q13665_q13790_bested_trigger(); new spell_59064_59439_portals(); + new spell_q11306_mixing_blood(); + new spell_q11306_mixing_vrykul_blood(); + new spell_q11306_failed_mix_43376(); + new spell_q11306_failed_mix_43378(); } diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index cff94b6e219..397427b505f 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -83,7 +83,8 @@ enum ShamanSpells SPELL_SHAMAN_LIGHTNING_SHIELD_DAMAGE_R1 = 26364, SPELL_SHAMAN_SHAMANISTIC_RAGE_PROC = 30824, SPELL_SHAMAN_MAELSTROM_POWER = 70831, - SPELL_SHAMAN_T10_ENHANCEMENT_4P_BONUS = 70832 + SPELL_SHAMAN_T10_ENHANCEMENT_4P_BONUS = 70832, + SPELL_SHAMAN_BLESSING_OF_THE_ETERNALS_R1 = 51554 }; enum ShamanSpellIcons @@ -537,6 +538,46 @@ class spell_sha_earthen_power : public SpellScriptLoader } }; +// -51940 - Earthliving Weapon (Passive) +class spell_sha_earthliving_weapon : public SpellScriptLoader +{ + public: + spell_sha_earthliving_weapon() : SpellScriptLoader("spell_sha_earthliving_weapon") { } + + class spell_sha_earthliving_weapon_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_earthliving_weapon_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_BLESSING_OF_THE_ETERNALS_R1)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + int32 chance = 20; + Unit* caster = eventInfo.GetActor(); + if (AuraEffect const* aurEff = caster->GetAuraEffectOfRankedSpell(SPELL_SHAMAN_BLESSING_OF_THE_ETERNALS_R1, EFFECT_1, caster->GetGUID())) + if (eventInfo.GetProcTarget()->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT)) + chance += aurEff->GetAmount(); + + return roll_chance_i(chance); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_sha_earthliving_weapon_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_earthliving_weapon_AuraScript(); + } +}; + // -1535 - Fire Nova class spell_sha_fire_nova : public SpellScriptLoader { @@ -1222,19 +1263,12 @@ class spell_sha_item_mana_surge : public SpellScriptLoader return true; } - bool CheckProc(ProcEventInfo& eventInfo) - { - DamageInfo* damageInfo = eventInfo.GetDamageInfo(); - if (!damageInfo || !damageInfo->GetSpellInfo()) - return false; - - return true; - } - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; int32 mana = spellInfo->CalcPowerCost(GetTarget(), eventInfo.GetSchoolMask()); int32 damage = CalculatePct(mana, 35); @@ -1244,7 +1278,6 @@ class spell_sha_item_mana_surge : public SpellScriptLoader void Register() override { - DoCheckProc += AuraCheckProcFn(spell_sha_item_mana_surge_AuraScript::CheckProc); OnEffectProc += AuraEffectProcFn(spell_sha_item_mana_surge_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); } }; @@ -2276,6 +2309,7 @@ void AddSC_shaman_spell_scripts() new spell_sha_earth_shield(); new spell_sha_earthbind_totem(); new spell_sha_earthen_power(); + new spell_sha_earthliving_weapon(); new spell_sha_fire_nova(); new spell_sha_flame_shock(); new spell_sha_flametongue_weapon(); diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index 492da48c455..cccebab6259 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -74,6 +74,7 @@ enum WarlockSpells SPELL_WARLOCK_SHADOWFLAME = 37378, SPELL_WARLOCK_FLAMESHADOW = 37379, SPELL_WARLOCK_GLYPH_OF_SUCCUBUS = 56250, + SPELL_WARLOCK_IMPROVED_DRAIN_SOUL_R1 = 18213, SPELL_WARLOCK_IMPROVED_DRAIN_SOUL_PROC = 18371 }; @@ -449,6 +450,50 @@ class spell_warl_demonic_empowerment : public SpellScriptLoader } }; +// -1120 - Drain Soul +class spell_warl_drain_soul : public SpellScriptLoader +{ + public: + spell_warl_drain_soul() : SpellScriptLoader("spell_warl_drain_soul") { } + + class spell_warl_drain_soul_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_drain_soul_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_IMPROVED_DRAIN_SOUL_R1) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_IMPROVED_DRAIN_SOUL_PROC)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + // Improved Drain Soul + Aura const* impDrainSoul = caster->GetAuraOfRankedSpell(SPELL_WARLOCK_IMPROVED_DRAIN_SOUL_R1, caster->GetGUID()); + if (!impDrainSoul) + return; + + int32 amount = CalculatePct(caster->GetMaxPower(POWER_MANA), impDrainSoul->GetSpellInfo()->Effects[EFFECT_2].CalcValue()); + caster->CastCustomSpell(SPELL_WARLOCK_IMPROVED_DRAIN_SOUL_PROC, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_warl_drain_soul_AuraScript::HandleProc, EFFECT_2, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warl_drain_soul_AuraScript(); + } +}; + // 47422 - Everlasting Affliction class spell_warl_everlasting_affliction : public SpellScriptLoader { @@ -509,7 +554,7 @@ class spell_warl_fel_synergy : public SpellScriptLoader if (!damageInfo || !damageInfo->GetDamage()) return false; - return GetTarget()->GetGuardianPet(); + return GetTarget()->GetGuardianPet() != nullptr; } void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) @@ -744,44 +789,6 @@ class spell_warl_health_funnel : public SpellScriptLoader } }; -// -18213 - Improved Drain Soul -class spell_warl_improved_drain_soul : public SpellScriptLoader -{ - public: - spell_warl_improved_drain_soul() : SpellScriptLoader("spell_warl_improved_drain_soul") { } - - class spell_warl_improved_drain_soul_AuraScript : public AuraScript - { - PrepareAuraScript(spell_warl_improved_drain_soul_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_IMPROVED_DRAIN_SOUL_PROC)) - return false; - return true; - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) - { - PreventDefaultAction(); - - Unit* target = GetTarget(); - int32 bp0 = CalculatePct(target->GetMaxPower(POWER_MANA), GetSpellInfo()->Effects[EFFECT_2].BasePoints); - target->CastCustomSpell(SPELL_WARLOCK_IMPROVED_DRAIN_SOUL_PROC, SPELLVALUE_BASE_POINT0, bp0, target, true, nullptr, aurEff); - } - - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_warl_improved_drain_soul_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_warl_improved_drain_soul_AuraScript(); - } -}; - // -1454 - Life Tap class spell_warl_life_tap : public SpellScriptLoader { @@ -1484,6 +1491,7 @@ void AddSC_warlock_spell_scripts() new spell_warl_demonic_circle_teleport(); new spell_warl_demonic_empowerment(); new spell_warl_demonic_pact(); + new spell_warl_drain_soul(); new spell_warl_everlasting_affliction(); new spell_warl_fel_synergy(); new spell_warl_glyph_of_life_tap(); @@ -1491,7 +1499,6 @@ void AddSC_warlock_spell_scripts() new spell_warl_haunt(); new spell_warl_health_funnel(); new spell_warl_glyph_of_corruption_nightfall(); - new spell_warl_improved_drain_soul(); new spell_warl_life_tap(); new spell_warl_nether_protection(); new spell_warl_ritual_of_doom_effect(); diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp index 272ba71d609..91166294355 100644 --- a/src/server/scripts/World/go_scripts.cpp +++ b/src/server/scripts/World/go_scripts.cpp @@ -1429,30 +1429,23 @@ public: { switch (eventId) { - case EVENT_MM_START_MUSIC: - { - if (!IsHolidayActive(HOLIDAY_FIRE_FESTIVAL)) - break; - - Map::PlayerList const &players = go->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + case EVENT_MM_START_MUSIC: { - if (Player* player = itr->GetSource()) + if (!IsHolidayActive(HOLIDAY_FIRE_FESTIVAL)) + break; + + std::vector<Player*> playersNearby; + go->GetPlayerListInGrid(playersNearby, go->GetMap()->GetVisibilityRange()); + for (Player* player : playersNearby) { if (player->GetTeamId() == TEAM_HORDE) - { go->PlayDirectMusic(EVENTMIDSUMMERFIREFESTIVAL_H, player); - } else - { go->PlayDirectMusic(EVENTMIDSUMMERFIREFESTIVAL_A, player); - } } + _events.ScheduleEvent(EVENT_MM_START_MUSIC, 5000); // Every 5 second's SMSG_PLAY_MUSIC packet (PlayDirectMusic) is pushed to the client (sniffed value) + break; } - - _events.ScheduleEvent(EVENT_MM_START_MUSIC, 5000); // Every 5 second's SMSG_PLAY_MUSIC packet (PlayDirectMusic) is pushed to the client (sniffed value) - break; - } default: break; } diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index fab4cf54a9a..1d173057e6f 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -3070,6 +3070,15 @@ PreventRenameCharacterOnCustomization = 0 ################################################################################################### # AUCTION HOUSE BOT SETTINGS # +# AuctionHouseBot.Account +# Description: Account ID for AHBot characters. If non-zero, all auctions and bids associated +# with the AHBot will randomly be assigned one of this account's characters. +# Default: 0 +# + +AuctionHouseBot.Account = 0 + +# # AuctionHouseBot.Update.Interval # Description: Interval in seconds for AHBot to get updated # Default: 20 diff --git a/src/tools/vmap4_extractor/mpq_libmpq.cpp b/src/tools/vmap4_extractor/mpq_libmpq.cpp index 690600867d9..f106f96f5ec 100644 --- a/src/tools/vmap4_extractor/mpq_libmpq.cpp +++ b/src/tools/vmap4_extractor/mpq_libmpq.cpp @@ -19,6 +19,7 @@ #include "mpq_libmpq04.h" #include <deque> #include <cstdio> +#include <algorithm> ArchiveSet gOpenArchives; @@ -52,6 +53,11 @@ MPQArchive::MPQArchive(const char* filename) gOpenArchives.push_front(this); } +bool MPQArchive::isOpened() const +{ + return std::find(gOpenArchives.begin(), gOpenArchives.end(), this) != gOpenArchives.end(); +} + void MPQArchive::close() { //gOpenArchives.erase(erase(&mpq_a); diff --git a/src/tools/vmap4_extractor/mpq_libmpq04.h b/src/tools/vmap4_extractor/mpq_libmpq04.h index 97b77d4643b..f4a9d2aa596 100644 --- a/src/tools/vmap4_extractor/mpq_libmpq04.h +++ b/src/tools/vmap4_extractor/mpq_libmpq04.h @@ -34,7 +34,7 @@ public: mpq_archive_s *mpq_a; MPQArchive(const char* filename); - ~MPQArchive() { close(); } + ~MPQArchive() { if (isOpened()) close(); } void GetFileListTo(std::vector<std::string>& filelist) { uint32_t filenum; @@ -66,6 +66,7 @@ public: private: void close(); + bool isOpened() const; }; typedef std::deque<MPQArchive*> ArchiveSet; @@ -95,13 +96,8 @@ public: inline void flipcc(char *fcc) { - char t; - t=fcc[0]; - fcc[0]=fcc[3]; - fcc[3]=t; - t=fcc[1]; - fcc[1]=fcc[2]; - fcc[2]=t; + std::swap(fcc[0], fcc[3]); + std::swap(fcc[1], fcc[2]); } #endif |
