diff options
Diffstat (limited to 'src')
183 files changed, 21245 insertions, 12769 deletions
diff --git a/src/common/Common.h b/src/common/Common.h index aa04abacd30..af9b9b17321 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -35,6 +35,7 @@ #include <unordered_map> #include <unordered_set> #include <vector> +#include <numeric> #include <cmath> #include <cstdio> 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 e5e9524896f..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 { @@ -657,7 +651,7 @@ bool AuthSession::HandleLogonProof() if (sConfigMgr->GetBoolDefault("WrongPass.Logging", false)) { PreparedStatement* logstmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FALP_IP_LOGGING); - logstmt->setString(0, _accountInfo.Login); + logstmt->setUInt32(0, _accountInfo.Id); logstmt->setString(1, GetRemoteIpAddress().to_string()); logstmt->setString(2, "Logged on failed AccountLogin due wrong password"); @@ -671,7 +665,7 @@ bool AuthSession::HandleLogonProof() stmt->setString(0, _accountInfo.Login); LoginDatabase.Execute(stmt); - if (_accountInfo.FailedLogins >= MaxWrongPassCount) + if (++_accountInfo.FailedLogins >= MaxWrongPassCount) { uint32 WrongPassBanTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600); bool WrongPassBanType = sConfigMgr->GetBoolDefault("WrongPass.BanType", false); @@ -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 658827662ec..6122e5aca7b 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -226,7 +226,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHAR_DATA_FOR_GUILD, "SELECT name, level, class, zone, account FROM characters WHERE guid = ?", CONNECTION_SYNCH); // Chat channel handling - PrepareStatement(CHAR_SEL_CHANNEL, "SELECT announce, ownership, password, bannedList FROM channels WHERE name = ? AND team = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_CHANNEL, "SELECT name, announce, ownership, password, bannedList FROM channels WHERE name = ? AND team = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_INS_CHANNEL, "INSERT INTO channels(name, team, lastUsed) VALUES (?, ?, UNIX_TIMESTAMP())", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHANNEL, "UPDATE channels SET announce = ?, ownership = ?, password = ?, bannedList = ?, lastUsed = UNIX_TIMESTAMP() WHERE name = ? AND team = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHANNEL_USAGE, "UPDATE channels SET lastUsed = UNIX_TIMESTAMP() WHERE name = ? AND team = ?", CONNECTION_ASYNC); @@ -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/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 7ca949e01bb..c6f439e0d3d 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -108,8 +108,8 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_INS_FACL_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, (SELECT last_attempt_ip FROM account WHERE id = ?), ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC); // 0: uint32, 1: uint32, 2: uint8, 3: string, 4: string // Complete name: "Login_Insert_CharacterDelete_IP_Logging" PrepareStatement(LOGIN_INS_CHAR_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, ?, ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC); - // 0: string, 1: string, 2: string // Complete name: "Login_Insert_Failed_Account_Login_due_password_IP_Logging" - PrepareStatement(LOGIN_INS_FALP_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES ((SELECT id FROM account WHERE username = ?), 0, 1, ?, ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC); + // 0: uint32, 1: string, 2: string // Complete name: "Login_Insert_Failed_Account_Login_due_password_IP_Logging" + PrepareStatement(LOGIN_INS_FALP_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, 0, 1, ?, ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS_BY_ID, "SELECT gmlevel, RealmID FROM account_access WHERE id = ? and (RealmID = ? OR RealmID = -1) ORDER BY gmlevel desc", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS, "SELECT permissionId, granted FROM rbac_account_permissions WHERE accountId = ? AND (realmId = ? OR realmId = -1) ORDER BY permissionId, realmId", CONNECTION_BOTH); 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 e310c954838..37a7020752b 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -89,6 +89,12 @@ public: return storage_.size(); } + // Clear the underlying storage. This does NOT despawn the creatures - use DespawnAll for that! + void clear() + { + storage_.clear(); + } + void Summon(Creature const* summon) { storage_.push_back(summon->GetGUID()); } void Despawn(Creature const* summon) { storage_.remove(summon->GetGUID()); } void DespawnEntry(uint32 entry); @@ -411,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/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index 80fc0b237b1..907a650e602 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -602,7 +602,7 @@ enum RBACPermissions RBAC_PERM_COMMAND_RELOAD_SPELL_LOOT_TEMPLATE = 695, RBAC_PERM_COMMAND_RELOAD_SPELL_LINKED_SPELL = 696, RBAC_PERM_COMMAND_RELOAD_SPELL_PET_AURAS = 697, - RBAC_PERM_COMMAND_RELOAD_SPELL_PROC_EVENT = 698, + // 698 - reuse RBAC_PERM_COMMAND_RELOAD_SPELL_PROC = 699, RBAC_PERM_COMMAND_RELOAD_SPELL_SCRIPTS = 700, RBAC_PERM_COMMAND_RELOAD_SPELL_TARGET_POSITION = 701, 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/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index 093fccb5977..bcbdb948708 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -375,6 +375,14 @@ void Battlefield::AskToLeaveQueue(Player* player) m_PlayersInQueue[player->GetTeamId()].erase(player->GetGUID()); } +// Called in WorldSession::HandleHearthAndResurrect +void Battlefield::PlayerAskToLeave(Player* player) +{ + // Player leaving Wintergrasp, teleport to Dalaran. + // ToDo: confirm teleport destination. + player->TeleportTo(571, 5804.1499f, 624.7710f, 647.7670f, 1.6400f); +} + // Called in WorldSession::HandleBfEntryInviteResponse void Battlefield::PlayerAcceptInviteToWar(Player* player) { diff --git a/src/server/game/Battlefield/Battlefield.h b/src/server/game/Battlefield/Battlefield.h index 566029cf9a1..a96b5400ccd 100644 --- a/src/server/game/Battlefield/Battlefield.h +++ b/src/server/game/Battlefield/Battlefield.h @@ -309,6 +309,7 @@ class TC_GAME_API Battlefield : public ZoneScript void PlayerAcceptInviteToWar(Player* player); uint32 GetBattleId() const { return m_BattleId; } void AskToLeaveQueue(Player* player); + void PlayerAskToLeave(Player* player); virtual void DoCompleteOrIncrementAchievement(uint32 /*achievement*/, Player* /*player*/, uint8 /*incrementNumber = 1*/) { } diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp index 361ee2b3e1f..8df95619ae5 100644 --- a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp +++ b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp @@ -61,9 +61,6 @@ G3D::Quat const WintergraspRelicRot = { 0.f, 0.f, -0.7933531f, 0.6087617f }; uint8 const WG_MAX_OBJ = 32; uint8 const WG_MAX_TURRET = 15; -uint8 const WG_MAX_KEEP_NPC = 39; -uint8 const WG_MAX_OUTSIDE_NPC = 14; -uint8 const WG_OUTSIDE_ALLIANCE_NPC = 7; uint8 const WG_MAX_TELEPORTER = 12; uint8 const WG_MAX_WORKSHOP = 6; uint8 const WG_MAX_TOWER = 7; @@ -177,76 +174,6 @@ struct WintergraspObjectPositionData uint32 AllianceEntry; }; -// Here there is all npc keeper spawn point -WintergraspObjectPositionData const WGKeepNPC[WG_MAX_KEEP_NPC] = -{ - // X Y Z O horde alliance - // North East - { { 5326.203125f, 2660.026367f, 409.100891f, 2.543383f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Roaming Guard - { { 5298.430176f, 2738.760010f, 409.316010f, 3.971740f }, BATTLEFIELD_WG_NPC_VIERON_BLAZEFEATHER, BATTLEFIELD_WG_NPC_BOWYER_RANDOLPH }, // Vieron Blazefeather - { { 5335.310059f, 2764.110107f, 409.274994f, 4.834560f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5349.810059f, 2763.629883f, 409.333008f, 4.660030f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - // North - { { 5373.470215f, 2789.060059f, 409.322998f, 2.600540f }, BATTLEFIELD_WG_NPC_STONE_GUARD_MUKAR, BATTLEFIELD_WG_NPC_KNIGHT_DAMERON }, // Stone Guard Mukar - { { 5296.560059f, 2789.870117f, 409.274994f, 0.733038f }, BATTLEFIELD_WG_NPC_HOODOO_MASTER_FU_JIN, BATTLEFIELD_WG_NPC_SORCERESS_KAYLANA }, // Voodoo Master Fu'jin - { { 5372.670000f, 2786.740000f, 409.442000f, 2.809980f }, BATTLEFIELD_WG_NPC_CHAMPION_ROS_SLAI, BATTLEFIELD_WG_NPC_MARSHAL_MAGRUDER }, // Wintergrasp Quartermaster - { { 5368.709961f, 2856.360107f, 409.322998f, 2.949610f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5367.910156f, 2826.520020f, 409.322998f, 3.333580f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5389.270020f, 2847.370117f, 418.759003f, 3.106690f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5388.560059f, 2834.770020f, 418.759003f, 3.071780f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5359.129883f, 2837.989990f, 409.364014f, 4.698930f }, BATTLEFIELD_WG_NPC_COMMANDER_DARDOSH, BATTLEFIELD_WG_NPC_COMMANDER_ZANNETH }, // Commander Dardosh - { { 5366.129883f, 2833.399902f, 409.322998f, 3.141590f }, BATTLEFIELD_WG_NPC_TACTICAL_OFFICER_KILRATH, BATTLEFIELD_WG_NPC_TACTICAL_OFFICER_AHBRAMIS }, // Tactical Officer Kilrath - // X Y Z O horde alliance - // North West - { { 5350.680176f, 2917.010010f, 409.274994f, 1.466080f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5335.120117f, 2916.800049f, 409.444000f, 1.500980f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5295.560059f, 2926.669922f, 409.274994f, 0.872665f }, BATTLEFIELD_WG_NPC_SIEGESMITH_STRONGHOOF, BATTLEFIELD_WG_NPC_SIEGE_MASTER_STOUTHANDLE }, // Stronghoof - { { 5371.399902f, 3026.510010f, 409.205994f, 3.250030f }, BATTLEFIELD_WG_NPC_PRIMALIST_MULFORT, BATTLEFIELD_WG_NPC_ANCHORITE_TESSA }, // Primalist Mulfort - { { 5392.123535f, 3031.110352f, 409.187683f, 3.677212f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Roaming Guard - // South - { { 5270.060059f, 2847.550049f, 409.274994f, 3.071780f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5270.160156f, 2833.479980f, 409.274994f, 3.124140f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5179.109863f, 2837.129883f, 409.274994f, 3.211410f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5179.669922f, 2846.600098f, 409.274994f, 3.089230f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5234.970215f, 2883.399902f, 409.274994f, 4.293510f }, BATTLEFIELD_WG_NPC_LIEUTENANT_MURP, BATTLEFIELD_WG_NPC_SENIOR_DEMOLITIONIST_LEGOSO }, // Lieutenant Murp - // X Y Z O horde alliance - // Portal guards (from around the fortress) - { { 5319.209473f, 3055.947754f, 409.176636f, 1.020201f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5311.612305f, 3061.207275f, 408.734161f, 0.965223f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5264.713379f, 3017.283447f, 408.479706f, 3.482424f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5269.096191f, 3008.315918f, 408.826294f, 3.843706f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5201.414551f, 2945.096924f, 409.190735f, 0.945592f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5193.386230f, 2949.617188f, 409.190735f, 1.145859f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5148.116211f, 2904.761963f, 409.193756f, 3.368532f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5153.355957f, 2895.501465f, 409.199310f, 3.549174f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5154.353027f, 2787.349365f, 409.250183f, 2.555644f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5150.066406f, 2777.876953f, 409.343903f, 2.708797f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5193.706543f, 2732.882812f, 409.189514f, 4.845073f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5202.126953f, 2737.570557f, 409.189514f, 5.375215f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5269.181152f, 2671.174072f, 409.098999f, 2.457459f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5264.960938f, 2662.332520f, 409.098999f, 2.598828f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5307.111816f, 2616.006836f, 409.095734f, 5.355575f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A }, // Standing Guard - { { 5316.770996f, 2619.430176f, 409.027740f, 5.363431f }, BATTLEFIELD_WG_NPC_GUARD_H, BATTLEFIELD_WG_NPC_GUARD_A } // Standing Guard -}; - -WintergraspObjectPositionData const WGOutsideNPC[WG_MAX_OUTSIDE_NPC] = -{ - { { 5032.04f, 3681.79f, 362.980f, 4.210f }, BATTLEFIELD_WG_NPC_VIERON_BLAZEFEATHER, 0 }, - { { 5020.71f, 3626.19f, 360.150f, 4.640f }, BATTLEFIELD_WG_NPC_HOODOO_MASTER_FU_JIN, 0 }, - { { 4994.85f, 3660.51f, 359.150f, 2.260f }, BATTLEFIELD_WG_NPC_COMMANDER_DARDOSH, 0 }, - { { 5015.46f, 3677.11f, 362.970f, 6.009f }, BATTLEFIELD_WG_NPC_TACTICAL_OFFICER_KILRATH, 0 }, - { { 5031.12f, 3663.77f, 363.500f, 3.110f }, BATTLEFIELD_WG_NPC_SIEGESMITH_STRONGHOOF, 0 }, - { { 5042.74f, 3675.82f, 363.060f, 3.358f }, BATTLEFIELD_WG_NPC_PRIMALIST_MULFORT, 0 }, - { { 5014.45f, 3640.87f, 361.390f, 3.280f }, BATTLEFIELD_WG_NPC_LIEUTENANT_MURP, 0 }, - { { 5100.07f, 2168.89f, 365.779f, 1.972f }, 0, BATTLEFIELD_WG_NPC_BOWYER_RANDOLPH }, - { { 5081.70f, 2173.73f, 365.878f, 0.855f }, 0, BATTLEFIELD_WG_NPC_SORCERESS_KAYLANA }, - { { 5078.28f, 2183.70f, 365.029f, 1.466f }, 0, BATTLEFIELD_WG_NPC_COMMANDER_ZANNETH }, - { { 5088.49f, 2188.18f, 365.647f, 5.253f }, 0, BATTLEFIELD_WG_NPC_TACTICAL_OFFICER_AHBRAMIS }, - { { 5095.67f, 2193.28f, 365.924f, 4.939f }, 0, BATTLEFIELD_WG_NPC_SIEGE_MASTER_STOUTHANDLE }, - { { 5088.61f, 2167.66f, 365.689f, 0.680f }, 0, BATTLEFIELD_WG_NPC_ANCHORITE_TESSA }, - { { 5080.40f, 2199.00f, 359.489f, 2.967f }, 0, BATTLEFIELD_WG_NPC_SENIOR_DEMOLITIONIST_LEGOSO }, -}; - struct WintergraspGameObjectData { Position Pos; @@ -551,7 +478,7 @@ bool BattlefieldWG::SetupBattlefield() for (uint8 i = 0; i < WG_MAX_WORKSHOP; i++) { WintergraspWorkshop* workshop = new WintergraspWorkshop(this, i); - if (i < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST) + if (i < BATTLEFIELD_WG_WORKSHOP_NE) workshop->GiveControlTo(GetAttackerTeam(), true); else workshop->GiveControlTo(GetDefenderTeam(), true); @@ -560,38 +487,6 @@ bool BattlefieldWG::SetupBattlefield() Workshops[i] = workshop; } - // Spawn NPCs in the defender's keep, both Horde and Alliance - for (uint8 i = 0; i < WG_MAX_KEEP_NPC; ++i) - { - // Horde npc - if (Creature* creature = SpawnCreature(WGKeepNPC[i].HordeEntry, WGKeepNPC[i].Pos)) - KeepCreature[TEAM_HORDE].push_back(creature->GetGUID()); - - // Alliance npc - if (Creature* creature = SpawnCreature(WGKeepNPC[i].AllianceEntry, WGKeepNPC[i].Pos)) - KeepCreature[TEAM_ALLIANCE].push_back(creature->GetGUID()); - } - - // Hide NPCs from the Attacker's team in the keep - for (auto itr = KeepCreature[GetAttackerTeam()].begin(); itr != KeepCreature[GetAttackerTeam()].end(); ++itr) - if (Creature* creature = GetCreature(*itr)) - HideNpc(creature); - - // Spawn Horde NPCs outside the keep - for (uint8 i = 0; i < WG_OUTSIDE_ALLIANCE_NPC; ++i) - if (Creature* creature = SpawnCreature(WGOutsideNPC[i].HordeEntry, WGOutsideNPC[i].Pos)) - OutsideCreature[TEAM_HORDE].push_back(creature->GetGUID()); - - // Spawn Alliance NPCs outside the keep - for (uint8 i = WG_OUTSIDE_ALLIANCE_NPC; i < WG_MAX_OUTSIDE_NPC; ++i) - if (Creature* creature = SpawnCreature(WGOutsideNPC[i].AllianceEntry, WGOutsideNPC[i].Pos)) - OutsideCreature[TEAM_ALLIANCE].push_back(creature->GetGUID()); - - // Hide units outside the keep that are defenders - for (auto itr = OutsideCreature[GetDefenderTeam()].begin(); itr != OutsideCreature[GetDefenderTeam()].end(); ++itr) - if (Creature* creature = GetCreature(*itr)) - HideNpc(creature); - // Spawn turrets and hide them per default for (uint8 i = 0; i < WG_MAX_TURRET; i++) { @@ -667,7 +562,7 @@ void BattlefieldWG::OnBattleStart() // Update faction of relic, only attacker can click on relic->SetFaction(WintergraspFaction[GetAttackerTeam()]); // Set in use (not allow to click on before last door is broken) - relic->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); + relic->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE | GO_FLAG_NOT_SELECTABLE); m_titansRelicGUID = relic->GetGUID(); } else @@ -769,27 +664,6 @@ void BattlefieldWG::OnBattleEnd(bool endByTimer) } } - if (!endByTimer) // One player triggered the relic - { - // Change all npc in keep - for (auto itr = KeepCreature[GetAttackerTeam()].begin(); itr != KeepCreature[GetAttackerTeam()].end(); ++itr) - if (Creature* creature = GetCreature(*itr)) - HideNpc(creature); - - for (auto itr = KeepCreature[GetDefenderTeam()].begin(); itr != KeepCreature[GetDefenderTeam()].end(); ++itr) - if (Creature* creature = GetCreature(*itr)) - ShowNpc(creature, true); - - // Change all npc out of keep - for (auto itr = OutsideCreature[GetDefenderTeam()].begin(); itr != OutsideCreature[GetDefenderTeam()].end(); ++itr) - if (Creature* creature = GetCreature(*itr)) - HideNpc(creature); - - for (auto itr = OutsideCreature[GetAttackerTeam()].begin(); itr != OutsideCreature[GetAttackerTeam()].end(); ++itr) - if (Creature* creature = GetCreature(*itr)) - ShowNpc(creature, true); - } - // Update all graveyard, control is to defender when no wartime for (uint8 i = 0; i < BATTLEFIELD_WG_GY_HORDE; i++) if (BfGraveyard* graveyard = GetGraveyardById(i)) @@ -817,6 +691,9 @@ void BattlefieldWG::OnBattleEnd(bool endByTimer) { player->CastSpell(player, SPELL_ESSENCE_OF_WINTERGRASP, true); player->CastSpell(player, SPELL_VICTORY_REWARD, true); + // Complete victory quests + player->AreaExploredOrEventHappens(QUEST_VICTORY_WINTERGRASP_A); + player->AreaExploredOrEventHappens(QUEST_VICTORY_WINTERGRASP_H); // Send Wintergrasp victory achievement DoCompleteOrIncrementAchievement(ACHIEVEMENTS_WIN_WG, player); // Award achievement for succeeding in Wintergrasp in 10 minutes or less @@ -1071,33 +948,9 @@ void BattlefieldWG::HandleKill(Player* killer, Unit* victim) if (killer == victim) return; - bool again = false; - TeamId killerTeam = killer->GetTeamId(); - if (victim->GetTypeId() == TYPEID_PLAYER) - { - for (auto itr = m_PlayersInWar[killerTeam].begin(); itr != m_PlayersInWar[killerTeam].end(); ++itr) - if (Player* player = ObjectAccessor::FindPlayer(*itr)) - if (player->GetDistance2d(killer) < 40) - PromotePlayer(player); - return; - } + HandlePromotion(killer, victim); - for (auto itr = KeepCreature[GetOtherTeam(killerTeam)].begin(); - itr != KeepCreature[GetOtherTeam(killerTeam)].end(); ++itr) - { - if (Creature* creature = GetCreature(*itr)) - { - if (victim->GetEntry() == creature->GetEntry() && !again) - { - again = true; - for (auto iter = m_PlayersInWar[killerTeam].begin(); iter != m_PlayersInWar[killerTeam].end(); ++iter) - if (Player* player = ObjectAccessor::FindPlayer(*iter)) - if (player->GetDistance2d(killer) < 40.0f) - PromotePlayer(player); - } - } - } /// @todoRecent PvP activity worldstate } @@ -1129,6 +982,16 @@ void BattlefieldWG::OnUnitDeath(Unit* unit) UpdateVehicleCountWG(); } +void BattlefieldWG::HandlePromotion(Player* playerKiller, Unit* unitKilled) +{ + uint32 teamId = playerKiller->GetTeamId(); + + for (auto iter = m_PlayersInWar[teamId].begin(); iter != m_PlayersInWar[teamId].end(); ++iter) + if (Player* player = ObjectAccessor::FindPlayer(*iter)) + if (player->GetDistance2d(unitKilled) < 40.0f) + PromotePlayer(player); +} + // Update rank for player void BattlefieldWG::PromotePlayer(Player* killer) { @@ -1316,31 +1179,23 @@ void BattlefieldWG::SendInitWorldStatesToAll() SendInitWorldStatesTo(player); } -void BattlefieldWG::BrokenWallOrTower(TeamId /*team*/) -{ -/* -uint32 const WGQuest[2][6] = +void BattlefieldWG::BrokenWallOrTower(TeamId team, BfWGGameObjectBuilding* building) { - { 13186, 13181, 13222, 13538, 13177, 13179 }, - { 13185, 13183, 13223, 13539, 13178, 13180 }, -}; -*/ - -// might be some use for this in the future. old code commented out below. KL -/* if (team == GetDefenderTeam()) + if (team == GetDefenderTeam()) { - for (GuidSet::const_iterator itr = m_PlayersInWar[GetAttackerTeam()].begin(); itr != m_PlayersInWar[GetAttackerTeam()].end(); ++itr) + for (auto itr = m_PlayersInWar[GetAttackerTeam()].begin(); itr != m_PlayersInWar[GetAttackerTeam()].end(); ++itr) { if (Player* player = ObjectAccessor::FindPlayer(*itr)) - IncrementQuest(player, WGQuest[player->GetTeamId()][2], true); + if (player->GetDistance2d(GetGameObject(building->GetGUID())) < 50.0f) + player->KilledMonsterCredit(QUEST_CREDIT_DEFEND_SIEGE); } - }*/ + } } // Called when a tower is broke void BattlefieldWG::UpdatedDestroyedTowerCount(TeamId team) { - // Destroy an attack tower + // Southern tower if (team == GetAttackerTeam()) { // Update counter @@ -1352,12 +1207,13 @@ void BattlefieldWG::UpdatedDestroyedTowerCount(TeamId team) if (Player* player = ObjectAccessor::FindPlayer(*itr)) player->RemoveAuraFromStack(SPELL_TOWER_CONTROL); - // Add buff stack to defenders + // Add buff stack to defenders and give achievement/quest credit for (auto itr = m_PlayersInWar[GetDefenderTeam()].begin(); itr != m_PlayersInWar[GetDefenderTeam()].end(); ++itr) { if (Player* player = ObjectAccessor::FindPlayer(*itr)) { player->CastSpell(player, SPELL_TOWER_CONTROL, true); + player->KilledMonsterCredit(QUEST_CREDIT_TOWERS_DESTROYED); DoCompleteOrIncrementAchievement(ACHIEVEMENTS_WG_TOWER_DESTROY, player); } } @@ -1372,7 +1228,7 @@ void BattlefieldWG::UpdatedDestroyedTowerCount(TeamId team) SendInitWorldStatesToAll(); } } - else + else // Keep tower { UpdateData(BATTLEFIELD_WG_DATA_DAMAGED_TOWER_DEF, -1); UpdateData(BATTLEFIELD_WG_DATA_BROKEN_TOWER_DEF, 1); @@ -1620,7 +1476,7 @@ void BfWGGameObjectBuilding::Destroyed() go->SetGoState(GO_STATE_ACTIVE); _wg->SetRelicInteractible(true); if (_wg->GetRelic()) - _wg->GetRelic()->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); + _wg->GetRelic()->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE | GO_FLAG_NOT_SELECTABLE); else TC_LOG_ERROR("bg.battlefield.wg", "Titan Relic not found."); break; @@ -1628,7 +1484,7 @@ void BfWGGameObjectBuilding::Destroyed() break; } - _wg->BrokenWallOrTower(_teamControl); + _wg->BrokenWallOrTower(_teamControl, this); } void BfWGGameObjectBuilding::Init(GameObject* go) @@ -1952,8 +1808,8 @@ void WintergraspWorkshop::GiveControlTo(TeamId teamId, bool init /*= false*/) void WintergraspWorkshop::UpdateGraveyardAndWorkshop() { - if (_staticInfo->WorkshopId < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST) - _wg->GetGraveyardById(_staticInfo->WorkshopId)->GiveControlTo(_teamControl); + if (_staticInfo->WorkshopId < BATTLEFIELD_WG_WORKSHOP_NE) + GiveControlTo(_wg->GetAttackerTeam(), true); else GiveControlTo(_wg->GetDefenderTeam(), true); } diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.h b/src/server/game/Battlefield/Zones/BattlefieldWG.h index a357791468b..3bd719c1a60 100644 --- a/src/server/game/Battlefield/Zones/BattlefieldWG.h +++ b/src/server/game/Battlefield/Zones/BattlefieldWG.h @@ -150,6 +150,14 @@ enum WintergraspAreaIds AREA_THE_CHILLED_QUAGMIRE = 4589 }; +enum WintergraspQuests +{ + QUEST_VICTORY_WINTERGRASP_A = 13181, + QUEST_VICTORY_WINTERGRASP_H = 13183, + QUEST_CREDIT_TOWERS_DESTROYED = 35074, + QUEST_CREDIT_DEFEND_SIEGE = 31284 +}; + /*######################### *####### Graveyards ###### *#########################*/ @@ -195,26 +203,6 @@ enum WintergraspNpcs BATTLEFIELD_WG_NPC_GUARD_A = 30740, BATTLEFIELD_WG_NPC_STALKER = 15214, - BATTLEFIELD_WG_NPC_VIERON_BLAZEFEATHER = 31102, - BATTLEFIELD_WG_NPC_STONE_GUARD_MUKAR = 32296, // <WINTERGRASP QUARTERMASTER> - BATTLEFIELD_WG_NPC_HOODOO_MASTER_FU_JIN = 31101, // <MASTER HEXXER> - BATTLEFIELD_WG_NPC_CHAMPION_ROS_SLAI = 39173, // <WINTERGRASP QUARTERMASTER> - BATTLEFIELD_WG_NPC_COMMANDER_DARDOSH = 31091, - BATTLEFIELD_WG_NPC_TACTICAL_OFFICER_KILRATH = 31151, - BATTLEFIELD_WG_NPC_SIEGESMITH_STRONGHOOF = 31106, - BATTLEFIELD_WG_NPC_PRIMALIST_MULFORT = 31053, - BATTLEFIELD_WG_NPC_LIEUTENANT_MURP = 31107, - - BATTLEFIELD_WG_NPC_BOWYER_RANDOLPH = 31052, - BATTLEFIELD_WG_NPC_KNIGHT_DAMERON = 32294, // <WINTERGRASP QUARTERMASTER> - BATTLEFIELD_WG_NPC_SORCERESS_KAYLANA = 31051, // <ENCHANTRESS> - BATTLEFIELD_WG_NPC_MARSHAL_MAGRUDER = 39172, // <WINTERGRASP QUARTERMASTER> - BATTLEFIELD_WG_NPC_COMMANDER_ZANNETH = 31036, - BATTLEFIELD_WG_NPC_TACTICAL_OFFICER_AHBRAMIS = 31153, - BATTLEFIELD_WG_NPC_SIEGE_MASTER_STOUTHANDLE = 31108, - BATTLEFIELD_WG_NPC_ANCHORITE_TESSA = 31054, - BATTLEFIELD_WG_NPC_SENIOR_DEMOLITIONIST_LEGOSO = 31109, - NPC_TAUNKA_SPIRIT_GUIDE = 31841, // Horde spirit guide for Wintergrasp NPC_DWARVEN_SPIRIT_GUIDE = 31842, // Alliance spirit guide for Wintergrasp @@ -336,7 +324,7 @@ class TC_GAME_API BattlefieldWG : public Battlefield * \brief Called when a wall/tower is broken * - Update quest */ - void BrokenWallOrTower(TeamId team); + void BrokenWallOrTower(TeamId team, BfWGGameObjectBuilding* building); /** * \brief Called when a tower is damaged @@ -381,6 +369,7 @@ class TC_GAME_API BattlefieldWG : public Battlefield void HandleKill(Player* killer, Unit* victim) override; void OnUnitDeath(Unit* unit) override; + void HandlePromotion(Player* killer, Unit* killed); void PromotePlayer(Player* killer); void UpdateTenacity(); @@ -403,8 +392,6 @@ class TC_GAME_API BattlefieldWG : public Battlefield GuidUnorderedSet m_vehicles[BG_TEAMS_COUNT]; GuidVector CanonList; - GuidVector KeepCreature[BG_TEAMS_COUNT]; - GuidVector OutsideCreature[BG_TEAMS_COUNT]; TeamId m_tenacityTeam; uint32 m_tenacityStack; @@ -450,10 +437,10 @@ enum WintergraspTowerIds enum WintergraspWorkshopIds { - BATTLEFIELD_WG_WORKSHOP_NE, - BATTLEFIELD_WG_WORKSHOP_NW, BATTLEFIELD_WG_WORKSHOP_SE, BATTLEFIELD_WG_WORKSHOP_SW, + BATTLEFIELD_WG_WORKSHOP_NE, + BATTLEFIELD_WG_WORKSHOP_NW, BATTLEFIELD_WG_WORKSHOP_KEEP_WEST, BATTLEFIELD_WG_WORKSHOP_KEEP_EAST }; 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/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 2e66c587e15..2319a13233f 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -514,20 +514,15 @@ inline void Battleground::_ProcessJoin(uint32 diff) if (!player->IsGameMaster()) { // remove auras with duration lower than 30s - Unit::AuraApplicationMap & auraMap = player->GetAppliedAuras(); - for (Unit::AuraApplicationMap::iterator iter = auraMap.begin(); iter != auraMap.end();) + player->RemoveAppliedAuras([](AuraApplication const* aurApp) { - AuraApplication * aurApp = iter->second; Aura* aura = aurApp->GetBase(); - if (!aura->IsPermanent() - && aura->GetDuration() <= 30*IN_MILLISECONDS + return !aura->IsPermanent() + && aura->GetDuration() <= 30 * IN_MILLISECONDS && aurApp->IsPositive() - && (!aura->GetSpellInfo()->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) - && (!aura->HasEffectType(SPELL_AURA_MOD_INVISIBILITY))) - player->RemoveAura(iter); - else - ++iter; - } + && !aura->GetSpellInfo()->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) + && !aura->HasEffectType(SPELL_AURA_MOD_INVISIBILITY); + }); } } 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 22601b711d1..afaa0063a45 100644 --- a/src/server/game/Chat/Channels/Channel.cpp +++ b/src/server/game/Chat/Channels/Channel.cpp @@ -17,93 +17,124 @@ */ #include "Channel.h" +#include "ChannelAppenders.h" #include "Chat.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" #include "ObjectMgr.h" +#include "Language.h" #include "SocialMgr.h" #include "World.h" #include "DatabaseEnv.h" #include "AccountMgr.h" #include "Player.h" -Channel::Channel(std::string const& name, uint32 channelId, uint32 team /*= 0*/): - _announceEnabled(true), - _ownershipEnabled(true), +Channel::Channel(uint32 channelId, uint32 team /*= 0*/, AreaTableEntry const* zoneEntry /*= nullptr*/) : + _announceEnabled(false), // no join/leave announces + _ownershipEnabled(false), // no ownership handout _persistentChannel(false), _isOwnerInvisible(false), - _channelFlags(0), + _channelFlags(CHANNEL_FLAG_GENERAL), // for all built-in channels _channelId(channelId), _channelTeam(team), _ownerGuid(), - _channelName(name), - _channelPassword() + _channelName(), + _channelPassword(), + _zoneEntry(zoneEntry) { - // set special flags if built-in channel - if (ChatChannelsEntry const* ch = sChatChannelsStore.LookupEntry(channelId)) // check whether it's a built-in channel - { - _announceEnabled = false; // no join/leave announces - _ownershipEnabled = false; // no ownership handout + ChatChannelsEntry const* channelEntry = sChatChannelsStore.AssertEntry(channelId); + if (channelEntry->flags & CHANNEL_DBC_FLAG_TRADE) // for trade channel + _channelFlags |= CHANNEL_FLAG_TRADE; - _channelFlags |= CHANNEL_FLAG_GENERAL; // for all built-in channels + if (channelEntry->flags & CHANNEL_DBC_FLAG_CITY_ONLY2) // for city only channels + _channelFlags |= CHANNEL_FLAG_CITY; - if (ch->flags & CHANNEL_DBC_FLAG_TRADE) // for trade channel - _channelFlags |= CHANNEL_FLAG_TRADE; - - if (ch->flags & CHANNEL_DBC_FLAG_CITY_ONLY2) // for city only channels - _channelFlags |= CHANNEL_FLAG_CITY; + if (channelEntry->flags & CHANNEL_DBC_FLAG_LFG) // for LFG channel + _channelFlags |= CHANNEL_FLAG_LFG; + else // for all other channels + _channelFlags |= CHANNEL_FLAG_NOT_LFG; +} - if (ch->flags & CHANNEL_DBC_FLAG_LFG) // for LFG channel - _channelFlags |= CHANNEL_FLAG_LFG; - else // for all other channels - _channelFlags |= CHANNEL_FLAG_NOT_LFG; - } - else // it's custom channel +Channel::Channel(std::string const& name, uint32 team /*= 0*/) : + _announceEnabled(true), + _ownershipEnabled(true), + _persistentChannel(false), + _isOwnerInvisible(false), + _channelFlags(CHANNEL_FLAG_CUSTOM), + _channelId(0), + _channelTeam(team), + _ownerGuid(), + _channelName(name), + _channelPassword(), + _zoneEntry(nullptr) +{ + // If storing custom channels in the db is enabled either load or save the channel + if (sWorld->getBoolConfig(CONFIG_PRESERVE_CUSTOM_CHANNELS)) { - _channelFlags |= CHANNEL_FLAG_CUSTOM; - - // If storing custom channels in the db is enabled either load or save the channel - if (sWorld->getBoolConfig(CONFIG_PRESERVE_CUSTOM_CHANNELS)) + PreparedStatement *stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHANNEL); + stmt->setString(0, name); + stmt->setUInt32(1, _channelTeam); + if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) // load { - PreparedStatement *stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHANNEL); - stmt->setString(0, name); - stmt->setUInt32(1, _channelTeam); - PreparedQueryResult result = CharacterDatabase.Query(stmt); - - if (result) //load + Field* fields = result->Fetch(); + _channelName = fields[0].GetString(); // re-get channel name. MySQL table collation is case insensitive + _announceEnabled = fields[1].GetBool(); + _ownershipEnabled = fields[2].GetBool(); + _channelPassword = fields[3].GetString(); + std::string db_BannedList = fields[4].GetString(); + + if (!db_BannedList.empty()) { - Field* fields = result->Fetch(); - _announceEnabled = fields[0].GetBool(); - _ownershipEnabled = fields[1].GetBool(); - _channelPassword = fields[2].GetString(); - char const* db_BannedList = fields[3].GetCString(); - - if (db_BannedList) + Tokenizer tokens(db_BannedList, ' '); + for (auto const& token : tokens) { - Tokenizer tokens(db_BannedList, ' '); - for (Tokenizer::const_iterator i = tokens.begin(); i != tokens.end(); ++i) + ObjectGuid banned_guid(uint64(atoull(token))); + if (banned_guid) { - ObjectGuid banned_guid(uint64(atoull(*i))); - if (banned_guid) - { - TC_LOG_DEBUG("chat.system", "Channel(%s) loaded bannedStore %s", name.c_str(), banned_guid.ToString().c_str()); - _bannedStore.insert(banned_guid); - } + TC_LOG_DEBUG("chat.system", "Channel(%s) loaded player %s into bannedStore", name.c_str(), banned_guid.ToString().c_str()); + _bannedStore.insert(banned_guid); } } } - else // save - { - stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHANNEL); - stmt->setString(0, name); - stmt->setUInt32(1, _channelTeam); - CharacterDatabase.Execute(stmt); - TC_LOG_DEBUG("chat.system", "Channel(%s) saved in database", name.c_str()); - } + } + else // save + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHANNEL); + stmt->setString(0, name); + stmt->setUInt32(1, _channelTeam); + CharacterDatabase.Execute(stmt); + TC_LOG_DEBUG("chat.system", "Channel(%s) saved in database", name.c_str()); + } - _persistentChannel = true; + _persistentChannel = true; + } +} + +void Channel::GetChannelName(std::string& channelName, uint32 channelId, LocaleConstant locale, AreaTableEntry const* zoneEntry) +{ + if (channelId) + { + ChatChannelsEntry const* channelEntry = sChatChannelsStore.AssertEntry(channelId); + if (!(channelEntry->flags & CHANNEL_DBC_FLAG_GLOBAL)) + { + if (channelEntry->flags & CHANNEL_DBC_FLAG_CITY_ONLY) + channelName = Trinity::StringFormat(channelEntry->pattern[locale], sObjectMgr->GetTrinityString(LANG_CHANNEL_CITY, locale)); + else + channelName = Trinity::StringFormat(channelEntry->pattern[locale], ASSERT_NOTNULL(zoneEntry)->area_name[locale]); } + else + channelName = channelEntry->pattern[locale]; } } +std::string Channel::GetName(LocaleConstant locale /*= DEFAULT_LOCALE*/) const +{ + std::string result = _channelName; + Channel::GetChannelName(result, _channelId, locale, _zoneEntry); + + return result; +} + void Channel::UpdateChannelInDB() const { if (_persistentChannel) @@ -155,26 +186,26 @@ void Channel::JoinChannel(Player* player, std::string const& pass) // Do not send error message for built-in channels if (!IsConstant()) { - WorldPacket data; - MakePlayerAlreadyMember(&data, guid); - SendToOne(&data, guid); + PlayerAlreadyMemberAppend appender(guid); + ChannelNameBuilder<PlayerAlreadyMemberAppend> builder(this, appender); + SendToOne(builder, guid); } return; } if (IsBanned(guid)) { - WorldPacket data; - MakeBanned(&data); - SendToOne(&data, guid); + BannedAppend appender; + ChannelNameBuilder<BannedAppend> builder(this, appender); + SendToOne(builder, guid); return; } if (!_channelPassword.empty() && pass != _channelPassword) { - WorldPacket data; - MakeWrongPassword(&data); - SendToOne(&data, guid); + WrongPasswordAppend appender; + ChannelNameBuilder<WrongPasswordAppend> builder(this, appender); + SendToOne(builder, guid); return; } @@ -183,9 +214,9 @@ void Channel::JoinChannel(Player* player, std::string const& pass) AccountMgr::IsPlayerAccount(player->GetSession()->GetSecurity()) && //FIXME: Move to RBAC player->GetGroup()) { - WorldPacket data; - MakeNotInLfg(&data); - SendToOne(&data, guid); + NotInLFGAppend appender; + ChannelNameBuilder<NotInLFGAppend> builder(this, appender); + SendToOne(builder, guid); return; } @@ -193,9 +224,9 @@ void Channel::JoinChannel(Player* player, std::string const& pass) if (_announceEnabled && !player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL)) { - WorldPacket data; - MakeJoined(&data, guid); - SendToAll(&data); + JoinedAppend appender(guid); + ChannelNameBuilder<JoinedAppend> builder(this, appender); + SendToAll(builder); } bool newChannel = _playersStore.empty(); @@ -204,9 +235,9 @@ void Channel::JoinChannel(Player* player, std::string const& pass) pinfo.flags = MEMBER_FLAG_NONE; pinfo.invisible = !player->isGMVisible(); - WorldPacket data; - MakeYouJoined(&data); - SendToOne(&data, guid); + YouJoinedAppend appender(this); + ChannelNameBuilder<YouJoinedAppend> builder(this, appender); + SendToOne(builder, guid); JoinNotify(guid); @@ -236,20 +267,20 @@ void Channel::LeaveChannel(Player* player, bool send) { if (send) { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, guid); + NotMemberAppend appender; + ChannelNameBuilder<NotMemberAppend> builder(this, appender); + SendToOne(builder, guid); } return; } if (send) { - WorldPacket data; - MakeYouLeft(&data); - SendToOne(&data, guid); + YouLeftAppend appender(this); + ChannelNameBuilder<YouLeftAppend> builder(this, appender); + SendToOne(builder, guid); + player->LeftChannel(this); - data.clear(); } PlayerInfo& info = _playersStore.at(guid); @@ -258,9 +289,9 @@ void Channel::LeaveChannel(Player* player, bool send) if (_announceEnabled && !player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL)) { - WorldPacket data; - MakeLeft(&data, guid); - SendToAll(&data); + LeftAppend appender(guid); + ChannelNameBuilder<LeftAppend> builder(this, appender); + SendToAll(builder); } LeaveNotify(guid); @@ -302,18 +333,18 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b if (!IsOn(good)) { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, good); + NotMemberAppend appender; + ChannelNameBuilder<NotMemberAppend> builder(this, appender); + SendToOne(builder, good); return; } PlayerInfo& info = _playersStore.at(good); if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { - WorldPacket data; - MakeNotModerator(&data); - SendToOne(&data, good); + NotModeratorAppend appender; + ChannelNameBuilder<NotModeratorAppend> builder(this, appender); + SendToOne(builder, good); return; } @@ -321,9 +352,9 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b ObjectGuid victim = bad ? bad->GetGUID() : ObjectGuid::Empty; if (!victim || !IsOn(victim)) { - WorldPacket data; - MakePlayerNotFound(&data, badname); - SendToOne(&data, good); + PlayerNotFoundAppend appender(badname); + ChannelNameBuilder<PlayerNotFoundAppend> builder(this, appender); + SendToOne(builder, good); return; } @@ -331,9 +362,9 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && changeowner && good != _ownerGuid) { - WorldPacket data; - MakeNotOwner(&data); - SendToOne(&data, good); + NotOwnerAppend appender; + ChannelNameBuilder<NotOwnerAppend> builder(this, appender); + SendToOne(builder, good); return; } @@ -344,16 +375,16 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL)) { - WorldPacket data; - MakePlayerBanned(&data, victim, good); - SendToAll(&data); + PlayerBannedAppend appender(good, victim); + ChannelNameBuilder<PlayerBannedAppend> builder(this, appender); + SendToAll(builder); } } else if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL)) { - WorldPacket data; - MakePlayerKicked(&data, victim, good); - SendToAll(&data); + PlayerKickedAppend appender(good, victim); + ChannelNameBuilder<PlayerKickedAppend> builder(this, appender); + SendToAll(builder); } _playersStore.erase(victim); @@ -373,18 +404,18 @@ void Channel::UnBan(Player const* player, std::string const& badname) if (!IsOn(good)) { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, good); + NotMemberAppend appender; + ChannelNameBuilder<NotMemberAppend> builder(this, appender); + SendToOne(builder, good); return; } PlayerInfo& info = _playersStore.at(good); if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { - WorldPacket data; - MakeNotModerator(&data); - SendToOne(&data, good); + NotModeratorAppend appender; + ChannelNameBuilder<NotModeratorAppend> builder(this, appender); + SendToOne(builder, good); return; } @@ -393,17 +424,17 @@ void Channel::UnBan(Player const* player, std::string const& badname) if (!victim || !IsBanned(victim)) { - WorldPacket data; - MakePlayerNotFound(&data, badname); - SendToOne(&data, good); + PlayerNotFoundAppend appender(badname); + ChannelNameBuilder<PlayerNotFoundAppend> builder(this, appender); + SendToOne(builder, good); return; } _bannedStore.erase(victim); - WorldPacket data; - MakePlayerUnbanned(&data, victim, good); - SendToAll(&data); + PlayerUnbannedAppend appender(good, victim); + ChannelNameBuilder<PlayerUnbannedAppend> builder(this, appender); + SendToAll(builder); UpdateChannelInDB(); } @@ -415,26 +446,26 @@ void Channel::Password(Player const* player, std::string const& pass) ChatHandler chat(player->GetSession()); if (!IsOn(guid)) { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, guid); + NotMemberAppend appender; + ChannelNameBuilder<NotMemberAppend> builder(this, appender); + SendToOne(builder, guid); return; } PlayerInfo& info = _playersStore.at(guid); if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { - WorldPacket data; - MakeNotModerator(&data); - SendToOne(&data, guid); + NotModeratorAppend appender; + ChannelNameBuilder<NotModeratorAppend> builder(this, appender); + SendToOne(builder, guid); return; } _channelPassword = pass; - WorldPacket data; - MakePasswordChanged(&data, guid); - SendToAll(&data); + PasswordChangedAppend appender(guid); + ChannelNameBuilder<PasswordChangedAppend> builder(this, appender); + SendToAll(builder); UpdateChannelInDB(); } @@ -445,18 +476,18 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo if (!IsOn(guid)) { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, guid); + NotMemberAppend appender; + ChannelNameBuilder<NotMemberAppend> builder(this, appender); + SendToOne(builder, guid); return; } PlayerInfo& info = _playersStore.at(guid); if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { - WorldPacket data; - MakeNotModerator(&data); - SendToOne(&data, guid); + NotModeratorAppend appender; + ChannelNameBuilder<NotModeratorAppend> builder(this, appender); + SendToOne(builder, guid); return; } @@ -471,17 +502,17 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo (!player->GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHANNEL) || !newp->GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHANNEL)))) { - WorldPacket data; - MakePlayerNotFound(&data, p2n); - SendToOne(&data, guid); + PlayerNotFoundAppend appender(p2n); + ChannelNameBuilder<PlayerNotFoundAppend> builder(this, appender); + SendToOne(builder, guid); return; } if (_ownerGuid == victim && _ownerGuid != guid) { - WorldPacket data; - MakeNotOwner(&data); - SendToOne(&data, guid); + NotOwnerAppend appender; + ChannelNameBuilder<NotOwnerAppend> builder(this, appender); + SendToOne(builder, guid); return; } @@ -504,23 +535,57 @@ void Channel::SetInvisible(Player const* player, bool on) _isOwnerInvisible = on; } +void Channel::SetModerator(ObjectGuid guid, bool set) +{ + if (!IsOn(guid)) + return; + + PlayerInfo& playerInfo = _playersStore.at(guid); + if (playerInfo.IsModerator() != set) + { + uint8 oldFlag = GetPlayerFlags(guid); + playerInfo.SetModerator(set); + + ModeChangeAppend appender(guid, oldFlag, GetPlayerFlags(guid)); + ChannelNameBuilder<ModeChangeAppend> builder(this, appender); + SendToAll(builder); + } +} + +void Channel::SetMute(ObjectGuid guid, bool set) +{ + if (!IsOn(guid)) + return; + + PlayerInfo& playerInfo = _playersStore.at(guid); + if (playerInfo.IsMuted() != set) + { + uint8 oldFlag = GetPlayerFlags(guid); + playerInfo.SetMuted(set); + + ModeChangeAppend appender(guid, oldFlag, GetPlayerFlags(guid)); + ChannelNameBuilder<ModeChangeAppend> builder(this, appender); + SendToAll(builder); + } +} + void Channel::SetOwner(Player const* player, std::string const& newname) { ObjectGuid guid = player->GetGUID(); if (!IsOn(guid)) { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, guid); + NotMemberAppend appender; + ChannelNameBuilder<NotMemberAppend> builder(this, appender); + SendToOne(builder, guid); return; } if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && guid != _ownerGuid) { - WorldPacket data; - MakeNotOwner(&data); - SendToOne(&data, guid); + NotOwnerAppend appender; + ChannelNameBuilder<NotOwnerAppend> builder(this, appender); + SendToOne(builder, guid); return; } @@ -532,9 +597,9 @@ void Channel::SetOwner(Player const* player, std::string const& newname) (!player->GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHANNEL) || !newp->GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHANNEL)))) { - WorldPacket data; - MakePlayerNotFound(&data, newname); - SendToOne(&data, guid); + PlayerNotFoundAppend appender(newname); + ChannelNameBuilder<PlayerNotFoundAppend> builder(this, appender); + SendToOne(builder, guid); return; } @@ -545,12 +610,18 @@ void Channel::SetOwner(Player const* player, std::string const& newname) void Channel::SendWhoOwner(ObjectGuid guid) { - WorldPacket data; if (IsOn(guid)) - MakeChannelOwner(&data); + { + ChannelOwnerAppend appender(this, _ownerGuid); + ChannelNameBuilder<ChannelOwnerAppend> builder(this, appender); + SendToOne(builder, guid); + } else - MakeNotMember(&data); - SendToOne(&data, guid); + { + NotMemberAppend appender; + ChannelNameBuilder<NotMemberAppend> builder(this, appender); + SendToOne(builder, guid); + } } void Channel::List(Player const* player) const @@ -559,18 +630,19 @@ void Channel::List(Player const* player) const if (!IsOn(guid)) { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, guid); + NotMemberAppend appender; + ChannelNameBuilder<NotMemberAppend> builder(this, appender); + SendToOne(builder, guid); return; } + std::string channelName = GetName(player->GetSession()->GetSessionDbcLocale()); TC_LOG_DEBUG("chat.system", "SMSG_CHANNEL_LIST %s Channel: %s", - player->GetSession()->GetPlayerInfo().c_str(), GetName().c_str()); + player->GetSession()->GetPlayerInfo().c_str(), channelName.c_str()); - WorldPacket data(SMSG_CHANNEL_LIST, 1+(GetName().size()+1)+1+4+_playersStore.size()*(8+1)); + WorldPacket data(SMSG_CHANNEL_LIST, 1 + (channelName.size() + 1) + 1 + 4 + _playersStore.size() * (8 + 1)); data << uint8(1); // channel type? - data << GetName(); // channel name + data << channelName; // channel name data << uint8(GetFlags()); // channel flags? size_t pos = data.wpos(); @@ -597,8 +669,7 @@ void Channel::List(Player const* player) const } data.put<uint32>(pos, count); - - SendToOne(&data, guid); + player->SendDirectMessage(&data); } void Channel::Announce(Player const* player) @@ -607,29 +678,35 @@ void Channel::Announce(Player const* player) if (!IsOn(guid)) { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, guid); + NotMemberAppend appender; + ChannelNameBuilder<NotMemberAppend> builder(this, appender); + SendToOne(builder, guid); return; } PlayerInfo& info = _playersStore.at(guid); if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { - WorldPacket data; - MakeNotModerator(&data); - SendToOne(&data, guid); + NotModeratorAppend appender; + ChannelNameBuilder<NotModeratorAppend> builder(this, appender); + SendToOne(builder, guid); return; } _announceEnabled = !_announceEnabled; - WorldPacket data; if (_announceEnabled) - MakeAnnouncementsOn(&data, guid); + { + AnnouncementsOnAppend appender(guid); + ChannelNameBuilder<AnnouncementsOnAppend> builder(this, appender); + SendToAll(builder); + } else - MakeAnnouncementsOff(&data, guid); - SendToAll(&data); + { + AnnouncementsOffAppend appender(guid); + ChannelNameBuilder<AnnouncementsOffAppend> builder(this, appender); + SendToAll(builder); + } UpdateChannelInDB(); } @@ -645,28 +722,32 @@ void Channel::Say(ObjectGuid guid, std::string const& what, uint32 lang) const if (!IsOn(guid)) { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, guid); + NotMemberAppend appender; + ChannelNameBuilder<NotMemberAppend> builder(this, appender); + SendToOne(builder, guid); return; } PlayerInfo const& info = _playersStore.at(guid); if (info.IsMuted()) { - WorldPacket data; - MakeMuted(&data); - SendToOne(&data, guid); + MutedAppend appender; + ChannelNameBuilder<MutedAppend> builder(this, appender); + SendToOne(builder, guid); return; } - WorldPacket data; - if (Player* player = ObjectAccessor::FindConnectedPlayer(guid)) - ChatHandler::BuildChatPacket(data, CHAT_MSG_CHANNEL, Language(lang), player, player, what, 0, _channelName); - else - ChatHandler::BuildChatPacket(data, CHAT_MSG_CHANNEL, Language(lang), guid, guid, what, 0, "", "", 0, false, _channelName); + auto builder = [&](WorldPacket& data, LocaleConstant locale) + { + LocaleConstant localeIdx = sWorld->GetAvailableDbcLocale(locale); + + if (Player* player = ObjectAccessor::FindConnectedPlayer(guid)) + ChatHandler::BuildChatPacket(data, CHAT_MSG_CHANNEL, Language(lang), player, player, what, 0, GetName(localeIdx)); + else + ChatHandler::BuildChatPacket(data, CHAT_MSG_CHANNEL, Language(lang), guid, guid, what, 0, "", "", 0, false, GetName(localeIdx)); + }; - SendToAll(&data, !info.IsModerator() ? guid : ObjectGuid::Empty); + SendToAll(builder, !info.IsModerator() ? guid : ObjectGuid::Empty); } void Channel::Invite(Player const* player, std::string const& newname) @@ -675,26 +756,26 @@ void Channel::Invite(Player const* player, std::string const& newname) if (!IsOn(guid)) { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, guid); + NotMemberAppend appender; + ChannelNameBuilder<NotMemberAppend> builder(this, appender); + SendToOne(builder, guid); return; } Player* newp = ObjectAccessor::FindConnectedPlayerByName(newname); if (!newp || !newp->isGMVisible()) { - WorldPacket data; - MakePlayerNotFound(&data, newname); - SendToOne(&data, guid); + PlayerNotFoundAppend appender(newname); + ChannelNameBuilder<PlayerNotFoundAppend> builder(this, appender); + SendToOne(builder, guid); return; } if (IsBanned(newp->GetGUID())) { - WorldPacket data; - MakePlayerInviteBanned(&data, newname); - SendToOne(&data, guid); + PlayerInviteBannedAppend appender(newname); + ChannelNameBuilder<PlayerInviteBannedAppend> builder(this, appender); + SendToOne(builder, guid); return; } @@ -702,31 +783,30 @@ void Channel::Invite(Player const* player, std::string const& newname) (!player->GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHANNEL) || !newp->GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHANNEL))) { - WorldPacket data; - MakeInviteWrongFaction(&data); - SendToOne(&data, guid); + InviteWrongFactionAppend appender; + ChannelNameBuilder<InviteWrongFactionAppend> builder(this, appender); + SendToOne(builder, guid); return; } if (IsOn(newp->GetGUID())) { - WorldPacket data; - MakePlayerAlreadyMember(&data, newp->GetGUID()); - SendToOne(&data, guid); + PlayerAlreadyMemberAppend appender(newp->GetGUID()); + ChannelNameBuilder<PlayerAlreadyMemberAppend> builder(this, appender); + SendToOne(builder, guid); return; } if (!newp->GetSocial()->HasIgnore(guid.GetCounter())) { - WorldPacket data; - MakeInvite(&data, guid); - SendToOne(&data, newp->GetGUID()); - data.clear(); + InviteAppend appender(guid); + ChannelNameBuilder<InviteAppend> builder(this, appender); + SendToOne(builder, newp->GetGUID()); } - WorldPacket data; - MakePlayerInvited(&data, newp->GetName()); - SendToOne(&data, guid); + PlayerInvitedAppend appender(newp->GetName()); + ChannelNameBuilder<PlayerInvitedAppend> builder(this, appender); + SendToOne(builder, guid); } void Channel::SetOwner(ObjectGuid guid, bool exclaim) @@ -749,42 +829,21 @@ void Channel::SetOwner(ObjectGuid guid, bool exclaim) itr->second.SetModerator(true); itr->second.SetOwner(true); - WorldPacket data; - MakeModeChange(&data, _ownerGuid, oldFlag); - SendToAll(&data); + ModeChangeAppend appender(_ownerGuid, oldFlag, GetPlayerFlags(_ownerGuid)); + ChannelNameBuilder<ModeChangeAppend> builder(this, appender); + SendToAll(builder); if (exclaim) { - MakeOwnerChanged(&data, _ownerGuid); - SendToAll(&data); + OwnerChangedAppend appender(_ownerGuid); + ChannelNameBuilder<OwnerChangedAppend> builder(this, appender); + SendToAll(builder); } UpdateChannelInDB(); } } -void Channel::SendToAll(WorldPacket* data, ObjectGuid guid) const -{ - for (PlayerContainer::const_iterator i = _playersStore.begin(); i != _playersStore.end(); ++i) - if (Player* player = ObjectAccessor::FindConnectedPlayer(i->first)) - if (!guid || !player->GetSocial()->HasIgnore(guid.GetCounter())) - player->GetSession()->SendPacket(data); -} - -void Channel::SendToAllButOne(WorldPacket* data, ObjectGuid who) const -{ - for (PlayerContainer::const_iterator i = _playersStore.begin(); i != _playersStore.end(); ++i) - if (i->first != who) - if (Player* player = ObjectAccessor::FindConnectedPlayer(i->first)) - player->GetSession()->SendPacket(data); -} - -void Channel::SendToOne(WorldPacket* data, ObjectGuid who) const -{ - if (Player* player = ObjectAccessor::FindConnectedPlayer(who)) - player->GetSession()->SendPacket(data); -} - void Channel::Voice(ObjectGuid /*guid1*/, ObjectGuid /*guid2*/) const { @@ -795,245 +854,72 @@ void Channel::DeVoice(ObjectGuid /*guid1*/, ObjectGuid /*guid2*/) const } -void Channel::MakeNotifyPacket(WorldPacket* data, uint8 notify_type) const -{ - data->Initialize(SMSG_CHANNEL_NOTIFY, 1 + _channelName.size()); - *data << uint8(notify_type); - *data << _channelName; -} - -void Channel::MakeJoined(WorldPacket* data, ObjectGuid guid) const -{ - MakeNotifyPacket(data, CHAT_JOINED_NOTICE); - *data << uint64(guid); -} - -void Channel::MakeLeft(WorldPacket* data, ObjectGuid guid) const -{ - MakeNotifyPacket(data, CHAT_LEFT_NOTICE); - *data << uint64(guid); -} - -void Channel::MakeYouJoined(WorldPacket* data) const -{ - MakeNotifyPacket(data, CHAT_YOU_JOINED_NOTICE); - *data << uint8(GetFlags()); - *data << uint32(GetChannelId()); - *data << uint32(0); -} - -void Channel::MakeYouLeft(WorldPacket* data) const -{ - MakeNotifyPacket(data, CHAT_YOU_LEFT_NOTICE); - *data << uint32(GetChannelId()); - *data << uint8(IsConstant()); -} - -void Channel::MakeWrongPassword(WorldPacket* data) const -{ - MakeNotifyPacket(data, CHAT_WRONG_PASSWORD_NOTICE); -} - -void Channel::MakeNotMember(WorldPacket* data) const -{ - MakeNotifyPacket(data, CHAT_NOT_MEMBER_NOTICE); -} - -void Channel::MakeNotModerator(WorldPacket* data) const -{ - MakeNotifyPacket(data, CHAT_NOT_MODERATOR_NOTICE); -} - -void Channel::MakePasswordChanged(WorldPacket* data, ObjectGuid guid) const -{ - MakeNotifyPacket(data, CHAT_PASSWORD_CHANGED_NOTICE); - *data << uint64(guid); -} - -void Channel::MakeOwnerChanged(WorldPacket* data, ObjectGuid guid) const -{ - MakeNotifyPacket(data, CHAT_OWNER_CHANGED_NOTICE); - *data << uint64(guid); -} - -void Channel::MakePlayerNotFound(WorldPacket* data, std::string const& name) const -{ - MakeNotifyPacket(data, CHAT_PLAYER_NOT_FOUND_NOTICE); - *data << name; -} - -void Channel::MakeNotOwner(WorldPacket* data) const +void Channel::JoinNotify(ObjectGuid guid) const { - MakeNotifyPacket(data, CHAT_NOT_OWNER_NOTICE); -} + auto builder = [&](WorldPacket& data, LocaleConstant locale) + { + LocaleConstant localeIdx = sWorld->GetAvailableDbcLocale(locale); -void Channel::MakeChannelOwner(WorldPacket* data) const -{ - std::string name; + data.Initialize(IsConstant() ? SMSG_USERLIST_ADD : SMSG_USERLIST_UPDATE, 8 + 1 + 1 + 4 + 30 /*channelName buffer*/); + data << uint64(guid); + data << uint8(GetPlayerFlags(guid)); + data << uint8(GetFlags()); + data << uint32(GetNumPlayers()); + data << GetName(localeIdx); + }; - CharacterInfo const* cInfo = sWorld->GetCharacterInfo(_ownerGuid); - if (!cInfo || cInfo->Name.empty()) - name = "PLAYER_NOT_FOUND"; + if (IsConstant()) + SendToAllButOne(builder, guid); else - name = cInfo->Name; - - MakeNotifyPacket(data, CHAT_CHANNEL_OWNER_NOTICE); - *data << ((IsConstant() || !_ownerGuid) ? "Nobody" : name); -} - -void Channel::MakeModeChange(WorldPacket* data, ObjectGuid guid, uint8 oldflags) const -{ - MakeNotifyPacket(data, CHAT_MODE_CHANGE_NOTICE); - *data << uint64(guid); - *data << uint8(oldflags); - *data << uint8(GetPlayerFlags(guid)); -} - -void Channel::MakeAnnouncementsOn(WorldPacket* data, ObjectGuid guid) const -{ - MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_ON_NOTICE); - *data << uint64(guid); -} - -void Channel::MakeAnnouncementsOff(WorldPacket* data, ObjectGuid guid) const -{ - MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_OFF_NOTICE); - *data << uint64(guid); -} - -void Channel::MakeMuted(WorldPacket* data) const -{ - MakeNotifyPacket(data, CHAT_MUTED_NOTICE); -} - -void Channel::MakePlayerKicked(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const -{ - MakeNotifyPacket(data, CHAT_PLAYER_KICKED_NOTICE); - *data << uint64(bad); - *data << uint64(good); -} - -void Channel::MakeBanned(WorldPacket* data) const -{ - MakeNotifyPacket(data, CHAT_BANNED_NOTICE); -} - -void Channel::MakePlayerBanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const -{ - MakeNotifyPacket(data, CHAT_PLAYER_BANNED_NOTICE); - *data << uint64(bad); - *data << uint64(good); -} - -void Channel::MakePlayerUnbanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const -{ - MakeNotifyPacket(data, CHAT_PLAYER_UNBANNED_NOTICE); - *data << uint64(bad); - *data << uint64(good); -} - -void Channel::MakePlayerNotBanned(WorldPacket* data, const std::string &name) const -{ - MakeNotifyPacket(data, CHAT_PLAYER_NOT_BANNED_NOTICE); - *data << name; + SendToAll(builder); } -void Channel::MakePlayerAlreadyMember(WorldPacket* data, ObjectGuid guid) const -{ - MakeNotifyPacket(data, CHAT_PLAYER_ALREADY_MEMBER_NOTICE); - *data << uint64(guid); -} - -void Channel::MakeInvite(WorldPacket* data, ObjectGuid guid) const -{ - MakeNotifyPacket(data, CHAT_INVITE_NOTICE); - *data << uint64(guid); -} - -void Channel::MakeInviteWrongFaction(WorldPacket* data) const -{ - MakeNotifyPacket(data, CHAT_INVITE_WRONG_FACTION_NOTICE); -} - -void Channel::MakeWrongFaction(WorldPacket* data) const -{ - MakeNotifyPacket(data, CHAT_WRONG_FACTION_NOTICE); -} - -void Channel::MakeInvalidName(WorldPacket* data) const -{ - MakeNotifyPacket(data, CHAT_INVALID_NAME_NOTICE); -} - -void Channel::MakeNotModerated(WorldPacket* data) const -{ - MakeNotifyPacket(data, CHAT_NOT_MODERATED_NOTICE); -} - -void Channel::MakePlayerInvited(WorldPacket* data, std::string const& name) const -{ - MakeNotifyPacket(data, CHAT_PLAYER_INVITED_NOTICE); - *data << name; -} - -void Channel::MakePlayerInviteBanned(WorldPacket* data, std::string const& name) const -{ - MakeNotifyPacket(data, CHAT_PLAYER_INVITE_BANNED_NOTICE); - *data << name; -} - -void Channel::MakeThrottled(WorldPacket* data) const +void Channel::LeaveNotify(ObjectGuid guid) const { - MakeNotifyPacket(data, CHAT_THROTTLED_NOTICE); -} + auto builder = [&](WorldPacket& data, LocaleConstant locale) + { + LocaleConstant localeIdx = sWorld->GetAvailableDbcLocale(locale); -void Channel::MakeNotInArea(WorldPacket* data) const -{ - MakeNotifyPacket(data, CHAT_NOT_IN_AREA_NOTICE); -} + data.Initialize(SMSG_USERLIST_REMOVE, 8 + 1 + 4 + 30 /*channelName buffer*/); + data << uint64(guid); + data << uint8(GetFlags()); + data << uint32(GetNumPlayers()); + data << GetName(localeIdx); + }; -void Channel::MakeNotInLfg(WorldPacket* data) const -{ - MakeNotifyPacket(data, CHAT_NOT_IN_LFG_NOTICE); + if (IsConstant()) + SendToAllButOne(builder, guid); + else + SendToAll(builder); } -void Channel::MakeVoiceOn(WorldPacket* data, ObjectGuid guid) const +template<class Builder> +void Channel::SendToAll(Builder& builder, ObjectGuid guid /*= ObjectGuid::Empty*/) const { - MakeNotifyPacket(data, CHAT_VOICE_ON_NOTICE); - *data << uint64(guid); -} + Trinity::LocalizedPacketDo<Builder> localizer(builder); -void Channel::MakeVoiceOff(WorldPacket* data, ObjectGuid guid) const -{ - MakeNotifyPacket(data, CHAT_VOICE_OFF_NOTICE); - *data << uint64(guid); + for (PlayerContainer::const_iterator i = _playersStore.begin(); i != _playersStore.end(); ++i) + if (Player* player = ObjectAccessor::FindConnectedPlayer(i->first)) + if (!guid || !player->GetSocial()->HasIgnore(guid.GetCounter())) + localizer(player); } -void Channel::JoinNotify(ObjectGuid guid) const +template<class Builder> +void Channel::SendToAllButOne(Builder& builder, ObjectGuid who) const { - WorldPacket data(IsConstant() ? SMSG_USERLIST_ADD : SMSG_USERLIST_UPDATE, 8 + 1 + 1 + 4 + GetName().size()); - data << uint64(guid); - data << uint8(GetPlayerFlags(guid)); - data << uint8(GetFlags()); - data << uint32(GetNumPlayers()); - data << GetName(); + Trinity::LocalizedPacketDo<Builder> localizer(builder); - if (IsConstant()) - SendToAllButOne(&data, guid); - else - SendToAll(&data); + for (PlayerContainer::const_iterator i = _playersStore.begin(); i != _playersStore.end(); ++i) + if (i->first != who) + if (Player* player = ObjectAccessor::FindConnectedPlayer(i->first)) + localizer(player); } -void Channel::LeaveNotify(ObjectGuid guid) const +template<class Builder> +void Channel::SendToOne(Builder& builder, ObjectGuid who) const { - WorldPacket data(SMSG_USERLIST_REMOVE, 8 + 1 + 4 + GetName().size()); - data << uint64(guid); - data << uint8(GetFlags()); - data << uint32(GetNumPlayers()); - data << GetName(); + Trinity::LocalizedPacketDo<Builder> localizer(builder); - if (IsConstant()) - SendToAllButOne(&data, guid); - else - SendToAll(&data); + if (Player* player = ObjectAccessor::FindConnectedPlayer(who)) + localizer(player); } diff --git a/src/server/game/Chat/Channels/Channel.h b/src/server/game/Chat/Channels/Channel.h index 4859a984967..8c01a474854 100644 --- a/src/server/game/Chat/Channels/Channel.h +++ b/src/server/game/Chat/Channels/Channel.h @@ -150,9 +150,11 @@ class TC_GAME_API Channel }; public: - Channel(std::string const& name, uint32 channel_id, uint32 team = 0); + Channel(uint32 channelId, uint32 team = 0, AreaTableEntry const* zoneEntry = nullptr); // built-in channel ctor + Channel(std::string const& name, uint32 team = 0); // custom player channel ctor - std::string const& GetName() const { return _channelName; } + static void GetChannelName(std::string& channelName, uint32 channelId, LocaleConstant locale, AreaTableEntry const* zoneEntry); + std::string GetName(LocaleConstant locale = DEFAULT_LOCALE) const; uint32 GetChannelId() const { return _channelId; } bool IsConstant() const { return _channelId != 0; } @@ -169,6 +171,8 @@ class TC_GAME_API Channel uint8 GetFlags() const { return _channelFlags; } bool HasFlag(uint8 flag) const { return (_channelFlags & flag) != 0; } + AreaTableEntry const* GetZoneEntry() const { return _zoneEntry; } + void JoinChannel(Player* player, std::string const& pass); void LeaveChannel(Player* player, bool send = true); @@ -203,47 +207,15 @@ class TC_GAME_API Channel static void CleanOldChannelsInDB(); private: - // initial packet data (notify type and channel name) - void MakeNotifyPacket(WorldPacket* data, uint8 notify_type) const; - // type specific packet data - void MakeJoined(WorldPacket* data, ObjectGuid guid) const; //+ 0x00 - void MakeLeft(WorldPacket* data, ObjectGuid guid) const; //+ 0x01 - void MakeYouJoined(WorldPacket* data) const; //+ 0x02 - void MakeYouLeft(WorldPacket* data) const; //+ 0x03 - void MakeWrongPassword(WorldPacket* data) const; //? 0x04 - void MakeNotMember(WorldPacket* data) const; //? 0x05 - void MakeNotModerator(WorldPacket* data) const; //? 0x06 - void MakePasswordChanged(WorldPacket* data, ObjectGuid guid) const; //+ 0x07 - void MakeOwnerChanged(WorldPacket* data, ObjectGuid guid) const; //? 0x08 - void MakePlayerNotFound(WorldPacket* data, std::string const& name) const; //+ 0x09 - void MakeNotOwner(WorldPacket* data) const; //? 0x0A - void MakeChannelOwner(WorldPacket* data) const; //? 0x0B - void MakeModeChange(WorldPacket* data, ObjectGuid guid, uint8 oldflags) const; //+ 0x0C - void MakeAnnouncementsOn(WorldPacket* data, ObjectGuid guid) const; //+ 0x0D - void MakeAnnouncementsOff(WorldPacket* data, ObjectGuid guid) const; //+ 0x0E - void MakeMuted(WorldPacket* data) const; //? 0x11 - void MakePlayerKicked(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const; //? 0x12 - void MakeBanned(WorldPacket* data) const; //? 0x13 - void MakePlayerBanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const; //? 0x14 - void MakePlayerUnbanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const; //? 0x15 - void MakePlayerNotBanned(WorldPacket* data, std::string const& name) const; //? 0x16 - void MakePlayerAlreadyMember(WorldPacket* data, ObjectGuid guid) const; //+ 0x17 - void MakeInvite(WorldPacket* data, ObjectGuid guid) const; //? 0x18 - void MakeInviteWrongFaction(WorldPacket* data) const; //? 0x19 - void MakeWrongFaction(WorldPacket* data) const; //? 0x1A - void MakeInvalidName(WorldPacket* data) const; //? 0x1B - void MakeNotModerated(WorldPacket* data) const; //? 0x1C - void MakePlayerInvited(WorldPacket* data, std::string const& name) const; //+ 0x1D - void MakePlayerInviteBanned(WorldPacket* data, std::string const& name) const; //? 0x1E - void MakeThrottled(WorldPacket* data) const; //? 0x1F - void MakeNotInArea(WorldPacket* data) const; //? 0x20 - void MakeNotInLfg(WorldPacket* data) const; //? 0x21 - void MakeVoiceOn(WorldPacket* data, ObjectGuid guid) const; //+ 0x22 - void MakeVoiceOff(WorldPacket* data, ObjectGuid guid) const; //+ 0x23 - - void SendToAll(WorldPacket* data, ObjectGuid guid = ObjectGuid::Empty) const; - void SendToAllButOne(WorldPacket* data, ObjectGuid who) const; - void SendToOne(WorldPacket* data, ObjectGuid who) const; + + template<class Builder> + void SendToAll(Builder&, ObjectGuid guid = ObjectGuid::Empty) const; + + template<class Builder> + void SendToAllButOne(Builder& builder, ObjectGuid who) const; + + template<class Builder> + void SendToOne(Builder& builder, ObjectGuid who) const; bool IsOn(ObjectGuid who) const { return _playersStore.count(who) != 0; } bool IsBanned(ObjectGuid guid) const { return _bannedStore.count(guid) != 0; } @@ -257,39 +229,8 @@ class TC_GAME_API Channel return itr != _playersStore.end() ? itr->second.flags : 0; } - void SetModerator(ObjectGuid guid, bool set) - { - if (!IsOn(guid)) - return; - - PlayerInfo& playerInfo = _playersStore.at(guid); - if (playerInfo.IsModerator() != set) - { - uint8 oldFlag = GetPlayerFlags(guid); - playerInfo.SetModerator(set); - - WorldPacket data; - MakeModeChange(&data, guid, oldFlag); - SendToAll(&data); - } - } - - void SetMute(ObjectGuid guid, bool set) - { - if (!IsOn(guid)) - return; - - PlayerInfo& playerInfo = _playersStore.at(guid); - if (playerInfo.IsMuted() != set) - { - uint8 oldFlag = GetPlayerFlags(guid); - playerInfo.SetMuted(set); - - WorldPacket data; - MakeModeChange(&data, guid, oldFlag); - SendToAll(&data); - } - } + void SetModerator(ObjectGuid guid, bool set); + void SetMute(ObjectGuid guid, bool set); typedef std::map<ObjectGuid, PlayerInfo> PlayerContainer; typedef GuidUnorderedSet BannedContainer; @@ -307,6 +248,8 @@ class TC_GAME_API Channel std::string _channelPassword; PlayerContainer _playersStore; BannedContainer _bannedStore; + + AreaTableEntry const* _zoneEntry; }; #endif diff --git a/src/server/game/Chat/Channels/ChannelAppenders.h b/src/server/game/Chat/Channels/ChannelAppenders.h new file mode 100644 index 00000000000..3dfdc3f32cf --- /dev/null +++ b/src/server/game/Chat/Channels/ChannelAppenders.h @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * 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/>. + */ + +#ifndef _CHANNELAPPENDERS_H +#define _CHANNELAPPENDERS_H + +#include "Channel.h" + +// initial packet data (notify type and channel name) +template<class PacketModifier> +class ChannelNameBuilder +{ + public: + ChannelNameBuilder(Channel const* source, PacketModifier const& modifier) + : _source(source), _modifier(modifier){ } + + void operator()(WorldPacket& data, LocaleConstant locale) const + { + // LocalizedPacketDo sends client DBC locale, we need to get available to server locale + LocaleConstant localeIdx = sWorld->GetAvailableDbcLocale(locale); + + data.Initialize(SMSG_CHANNEL_NOTIFY, 60); // guess size + data << uint8(_modifier.NotificationType); + data << _source->GetName(localeIdx); + _modifier.Append(data); + } + + private: + Channel const* _source; + PacketModifier _modifier; +}; + +struct JoinedAppend +{ + explicit JoinedAppend(ObjectGuid const& guid) : _guid(guid) { } + + static uint8 const NotificationType = CHAT_JOINED_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_guid); + } + +private: + ObjectGuid _guid; +}; + +struct LeftAppend +{ + explicit LeftAppend(ObjectGuid const& guid) : _guid(guid) { } + + static uint8 const NotificationType = CHAT_LEFT_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_guid); + } + +private: + ObjectGuid _guid; +}; + +struct YouJoinedAppend +{ + explicit YouJoinedAppend(Channel const* channel) : _channel(channel) { } + + static uint8 const NotificationType = CHAT_YOU_JOINED_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint8(_channel->GetFlags()); + data << uint32(_channel->GetChannelId()); + data << uint32(0); + } + +private: + Channel const* _channel; +}; + +struct YouLeftAppend +{ + explicit YouLeftAppend(Channel const* channel) : _channel(channel) { } + + static uint8 const NotificationType = CHAT_YOU_LEFT_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint32(_channel->GetChannelId()); + data << uint8(_channel->IsConstant()); + } + +private: + Channel const* _channel; +}; + +struct WrongPasswordAppend +{ + static uint8 const NotificationType = CHAT_WRONG_PASSWORD_NOTICE; + + void Append(WorldPacket& /*data*/) const { } +}; + +struct NotMemberAppend +{ + static uint8 const NotificationType = CHAT_NOT_MEMBER_NOTICE; + + void Append(WorldPacket& /*data*/) const { } +}; + +struct NotModeratorAppend +{ + static uint8 const NotificationType = CHAT_NOT_MODERATOR_NOTICE; + + void Append(WorldPacket& /*data*/) const { } +}; + +struct PasswordChangedAppend +{ + explicit PasswordChangedAppend(ObjectGuid const& guid) : _guid(guid) { } + + static uint8 const NotificationType = CHAT_PASSWORD_CHANGED_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_guid); + } + +private: + ObjectGuid _guid; +}; + +struct OwnerChangedAppend +{ + explicit OwnerChangedAppend(ObjectGuid const& guid) : _guid(guid) { } + + static uint8 const NotificationType = CHAT_OWNER_CHANGED_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_guid); + } + +private: + ObjectGuid _guid; +}; + +struct PlayerNotFoundAppend +{ + explicit PlayerNotFoundAppend(std::string const& playerName) : _playerName(playerName) { } + + static uint8 const NotificationType = CHAT_PLAYER_NOT_FOUND_NOTICE; + + void Append(WorldPacket& data) const + { + data << _playerName; + } + +private: + std::string _playerName; +}; + +struct NotOwnerAppend +{ + static uint8 const NotificationType = CHAT_NOT_OWNER_NOTICE; + + void Append(WorldPacket& /*data*/) const { } +}; + +struct ChannelOwnerAppend +{ + explicit ChannelOwnerAppend(Channel const* channel, ObjectGuid const& ownerGuid) : _channel(channel), _ownerGuid(ownerGuid) + { + CharacterInfo const* cInfo = sWorld->GetCharacterInfo(_ownerGuid); + if (!cInfo || cInfo->Name.empty()) + _ownerName = "PLAYER_NOT_FOUND"; + else + _ownerName = cInfo->Name; + } + + static uint8 const NotificationType = CHAT_CHANNEL_OWNER_NOTICE; + + void Append(WorldPacket& data) const + { + data << ((_channel->IsConstant() || !_ownerGuid) ? "Nobody" : _ownerName); + } + +private: + Channel const* _channel; + ObjectGuid _ownerGuid; + + std::string _ownerName; +}; + +struct ModeChangeAppend +{ + explicit ModeChangeAppend(ObjectGuid const& guid, uint8 oldFlags, uint8 newFlags) : _guid(guid), _oldFlags(oldFlags), _newFlags(newFlags) { } + + static uint8 const NotificationType = CHAT_MODE_CHANGE_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_guid); + data << uint8(_oldFlags); + data << uint8(_newFlags); + } + +private: + ObjectGuid _guid; + uint8 _oldFlags; + uint8 _newFlags; +}; + +struct AnnouncementsOnAppend +{ + explicit AnnouncementsOnAppend(ObjectGuid const& guid) : _guid(guid) { } + + static uint8 const NotificationType = CHAT_ANNOUNCEMENTS_ON_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_guid); + } + +private: + ObjectGuid _guid; +}; + +struct AnnouncementsOffAppend +{ + explicit AnnouncementsOffAppend(ObjectGuid const& guid) : _guid(guid) { } + + static uint8 const NotificationType = CHAT_ANNOUNCEMENTS_OFF_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_guid); + } + +private: + ObjectGuid _guid; +}; + +struct MutedAppend +{ + static uint8 const NotificationType = CHAT_MUTED_NOTICE; + + void Append(WorldPacket& /*data*/) const { } +}; + +struct PlayerKickedAppend +{ + explicit PlayerKickedAppend(ObjectGuid const& kicker, ObjectGuid const& kickee) : _kicker(kicker), _kickee(kickee) { } + + static uint8 const NotificationType = CHAT_PLAYER_KICKED_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_kickee); + data << uint64(_kicker); + } + +private: + ObjectGuid _kicker; + ObjectGuid _kickee; +}; + +struct BannedAppend +{ + static uint8 const NotificationType = CHAT_BANNED_NOTICE; + + void Append(WorldPacket& /*data*/) const { } +}; + +struct PlayerBannedAppend +{ + explicit PlayerBannedAppend(ObjectGuid const& moderator, ObjectGuid const& banned) : _moderator(moderator), _banned(banned) { } + + static uint8 const NotificationType = CHAT_PLAYER_BANNED_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_banned); + data << uint64(_moderator); + } + +private: + ObjectGuid _moderator; + ObjectGuid _banned; +}; + +struct PlayerUnbannedAppend +{ + explicit PlayerUnbannedAppend(ObjectGuid const& moderator, ObjectGuid const& unbanned) : _moderator(moderator), _unbanned(unbanned) { } + + static uint8 const NotificationType = CHAT_PLAYER_UNBANNED_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_unbanned); + data << uint64(_moderator); + } + +private: + ObjectGuid _moderator; + ObjectGuid _unbanned; +}; + +struct PlayerNotBannedAppend +{ + explicit PlayerNotBannedAppend(std::string const& playerName) : _playerName(playerName) { } + + static uint8 const NotificationType = CHAT_PLAYER_NOT_BANNED_NOTICE; + + void Append(WorldPacket& data) const + { + data << _playerName; + } + +private: + std::string _playerName; +}; + +struct PlayerAlreadyMemberAppend +{ + explicit PlayerAlreadyMemberAppend(ObjectGuid const& guid) : _guid(guid) { } + + static uint8 const NotificationType = CHAT_PLAYER_ALREADY_MEMBER_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_guid); + } + +private: + ObjectGuid _guid; +}; + +struct InviteAppend +{ + explicit InviteAppend(ObjectGuid const& guid) : _guid(guid) { } + + static uint8 const NotificationType = CHAT_INVITE_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_guid); + } + +private: + ObjectGuid _guid; +}; + +struct InviteWrongFactionAppend +{ + static uint8 const NotificationType = CHAT_INVITE_WRONG_FACTION_NOTICE; + + void Append(WorldPacket& /*data*/) const { } +}; + +struct WrongFactionAppend +{ + static uint8 const NotificationType = CHAT_WRONG_FACTION_NOTICE; + + void Append(WorldPacket& /*data*/) const { } +}; + +struct InvalidNameAppend +{ + static uint8 const NotificationType = CHAT_INVALID_NAME_NOTICE; + + void Append(WorldPacket& /*data*/) const { } +}; + +struct NotModeratedAppend +{ + static uint8 const NotificationType = CHAT_NOT_MODERATED_NOTICE; + + void Append(WorldPacket& /*data*/) const { } +}; + +struct PlayerInvitedAppend +{ + explicit PlayerInvitedAppend(std::string const& playerName) : _playerName(playerName) { } + + static uint8 const NotificationType = CHAT_PLAYER_INVITED_NOTICE; + + void Append(WorldPacket& data) const + { + data << _playerName; + } + +private: + std::string _playerName; +}; + +struct PlayerInviteBannedAppend +{ + explicit PlayerInviteBannedAppend(std::string const& playerName) : _playerName(playerName) { } + + static uint8 const NotificationType = CHAT_PLAYER_INVITE_BANNED_NOTICE; + + void Append(WorldPacket& data) const + { + data << _playerName; + } + +private: + std::string _playerName; +}; + +struct ThrottledAppend +{ + static uint8 const NotificationType = CHAT_THROTTLED_NOTICE; + + void Append(WorldPacket& /*data*/) const { } +}; + +struct NotInAreaAppend +{ + static uint8 const NotificationType = CHAT_NOT_IN_AREA_NOTICE; + + void Append(WorldPacket& /*data*/) const { } +}; + +struct NotInLFGAppend +{ + static uint8 const NotificationType = CHAT_NOT_IN_LFG_NOTICE; + + void Append(WorldPacket& /*data*/) const { } +}; + +struct VoiceOnAppend +{ + explicit VoiceOnAppend(ObjectGuid const& guid) : _guid(guid) { } + + static uint8 const NotificationType = CHAT_VOICE_ON_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_guid); + } + +private: + ObjectGuid _guid; +}; + +struct VoiceOffAppend +{ + explicit VoiceOffAppend(ObjectGuid const& guid) : _guid(guid) { } + + static uint8 const NotificationType = CHAT_VOICE_OFF_NOTICE; + + void Append(WorldPacket& data) const + { + data << uint64(_guid); + } + +private: + ObjectGuid _guid; +}; + +#endif // _CHANNELAPPENDERS_H diff --git a/src/server/game/Chat/Channels/ChannelMgr.cpp b/src/server/game/Chat/Channels/ChannelMgr.cpp index 043d4bdc2bc..ee1f463aae2 100644 --- a/src/server/game/Chat/Channels/ChannelMgr.cpp +++ b/src/server/game/Chat/Channels/ChannelMgr.cpp @@ -16,20 +16,25 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "Channel.h" #include "ChannelMgr.h" #include "Player.h" #include "World.h" ChannelMgr::~ChannelMgr() { - for (ChannelMap::iterator itr = channels.begin(); itr != channels.end(); ++itr) + for (auto itr = _channels.begin(); itr != _channels.end(); ++itr) + delete itr->second; + + for (auto itr = _customChannels.begin(); itr != _customChannels.end(); ++itr) delete itr->second; } ChannelMgr* ChannelMgr::forTeam(uint32 team) { - static ChannelMgr allianceChannelMgr; - static ChannelMgr hordeChannelMgr; + static ChannelMgr allianceChannelMgr(ALLIANCE); + static ChannelMgr hordeChannelMgr(HORDE); + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) return &allianceChannelMgr; // cross-faction @@ -42,69 +47,148 @@ ChannelMgr* ChannelMgr::forTeam(uint32 team) return nullptr; } -Channel* ChannelMgr::GetJoinChannel(std::string const& name, uint32 channelId) +Channel* ChannelMgr::GetChannelForPlayerByNamePart(std::string const& namePart, Player* playerSearcher) { - std::wstring wname; - if (!Utf8toWStr(name, wname)) + std::wstring channelNamePart; + if (!Utf8toWStr(namePart, channelNamePart)) return nullptr; - wstrToLower(wname); + wstrToLower(channelNamePart); + for (Channel* channel : playerSearcher->GetJoinedChannels()) + { + std::string chanName = channel->GetName(playerSearcher->GetSession()->GetSessionDbcLocale()); - ChannelMap::const_iterator i = channels.find(wname); + std::wstring channelNameW; + if (!Utf8toWStr(chanName, channelNameW)) + continue; - if (i == channels.end()) - { - Channel* nchan = new Channel(name, channelId, team); - channels[wname] = nchan; - return nchan; + wstrToLower(channelNameW); + if (!channelNameW.compare(0, channelNamePart.size(), channelNamePart)) + return channel; } - return i->second; + return nullptr; } -Channel* ChannelMgr::GetChannel(std::string const& name, Player* player, bool pkt) +Channel* ChannelMgr::GetJoinChannel(uint32 channelId, std::string const& name, AreaTableEntry const* zoneEntry /*= nullptr*/) { - std::wstring wname; - if (!Utf8toWStr(name, wname)) - return nullptr; + if (channelId) // builtin + { + ChatChannelsEntry const* channelEntry = sChatChannelsStore.AssertEntry(channelId); + uint32 zoneId = zoneEntry ? zoneEntry->ID : 0; + if (channelEntry->flags & (CHANNEL_DBC_FLAG_GLOBAL | CHANNEL_DBC_FLAG_CITY_ONLY)) + zoneId = 0; - wstrToLower(wname); + std::pair<uint32, uint32> key = std::make_pair(channelId, zoneId); - ChannelMap::const_iterator i = channels.find(wname); + auto itr = _channels.find(key); + if (itr != _channels.end()) + return itr->second; - if (i == channels.end()) + Channel* newChannel = new Channel(channelId, _team, zoneEntry); + _channels[key] = newChannel; + return newChannel; + } + else // custom { - if (pkt) - { - WorldPacket data; - MakeNotOnPacket(&data, name); - player->GetSession()->SendPacket(&data); - } + std::wstring channelName; + if (!Utf8toWStr(name, channelName)) + return nullptr; + + wstrToLower(channelName); + auto itr = _customChannels.find(channelName); + if (itr != _customChannels.end()) + return itr->second; + + Channel* newChannel = new Channel(name, _team); + _customChannels[channelName] = newChannel; + return newChannel; + } +} - return nullptr; +Channel* ChannelMgr::GetChannel(uint32 channelId, std::string const& name, Player* player, bool pkt /*= true*/, AreaTableEntry const* zoneEntry /*= nullptr*/) const +{ + Channel* ret = nullptr; + bool send = false; + + if (channelId) // builtin + { + ChatChannelsEntry const* channelEntry = sChatChannelsStore.AssertEntry(channelId); + uint32 zoneId = zoneEntry ? zoneEntry->ID : 0; + if (channelEntry->flags & (CHANNEL_DBC_FLAG_GLOBAL | CHANNEL_DBC_FLAG_CITY_ONLY)) + zoneId = 0; + + std::pair<uint32, uint32> key = std::make_pair(channelId, zoneId); + + auto itr = _channels.find(key); + if (itr != _channels.end()) + ret = itr->second; + else + send = true; } + else // custom + { + std::wstring channelName; + if (!Utf8toWStr(name, channelName)) + return nullptr; + + wstrToLower(channelName); + auto itr = _customChannels.find(channelName); + if (itr != _customChannels.end()) + ret = itr->second; + else + send = true; + } + + if (send && pkt) + { + std::string channelName = name; + Channel::GetChannelName(channelName, channelId, player->GetSession()->GetSessionDbcLocale(), zoneEntry); - return i->second; + WorldPacket data; + ChannelMgr::MakeNotOnPacket(&data, channelName); + player->SendDirectMessage(&data); + } + + return ret; } void ChannelMgr::LeftChannel(std::string const& name) { - std::wstring wname; - if (!Utf8toWStr(name, wname)) + std::wstring channelName; + if (!Utf8toWStr(name, channelName)) return; - wstrToLower(wname); + wstrToLower(channelName); + auto itr = _customChannels.find(channelName); + if (itr == _customChannels.end()) + return; - ChannelMap::const_iterator i = channels.find(wname); + Channel* channel = itr->second; + if (!channel->GetNumPlayers()) + { + _customChannels.erase(itr); + delete channel; + } +} - if (i == channels.end()) - return; +void ChannelMgr::LeftChannel(uint32 channelId, AreaTableEntry const* zoneEntry) +{ + ChatChannelsEntry const* channelEntry = sChatChannelsStore.AssertEntry(channelId); + uint32 zoneId = zoneEntry ? zoneEntry->ID : 0; + if (channelEntry->flags & (CHANNEL_DBC_FLAG_GLOBAL | CHANNEL_DBC_FLAG_CITY_ONLY)) + zoneId = 0; + + std::pair<uint32, uint32> key = std::make_pair(channelId, zoneId); - Channel* channel = i->second; + auto itr = _channels.find(key); + if (itr == _channels.end()) + return; - if (!channel->GetNumPlayers() && !channel->IsConstant()) + Channel* channel = itr->second; + if (!channel->GetNumPlayers()) { - channels.erase(wname); + _channels.erase(itr); delete channel; } } @@ -112,5 +196,5 @@ void ChannelMgr::LeftChannel(std::string const& name) void ChannelMgr::MakeNotOnPacket(WorldPacket* data, std::string const& name) { data->Initialize(SMSG_CHANNEL_NOTIFY, 1 + name.size()); - (*data) << uint8(5) << name; + (*data) << uint8(CHAT_NOT_MEMBER_NOTICE) << name; } diff --git a/src/server/game/Chat/Channels/ChannelMgr.h b/src/server/game/Chat/Channels/ChannelMgr.h index abe45690997..5f6fa55516a 100644 --- a/src/server/game/Chat/Channels/ChannelMgr.h +++ b/src/server/game/Chat/Channels/ChannelMgr.h @@ -19,36 +19,33 @@ #define __TRINITY_CHANNELMGR_H #include "Common.h" -#include "Channel.h" -#include <map> -#include <string> - -#include "World.h" - -#define MAX_CHANNEL_PASS_STR 31 +class Channel; class TC_GAME_API ChannelMgr { - typedef std::map<std::wstring, Channel*> ChannelMap; + typedef std::unordered_map<std::wstring, Channel*> CustomChannelContainer; // custom channels only differ in name + typedef std::unordered_map<std::pair<uint32 /*channelId*/, uint32 /*zoneId*/>, Channel*> BuiltinChannelContainer; //identify builtin (DBC) channels by zoneId instead, since name changes by client locale protected: - ChannelMgr() : team(0) { } + explicit ChannelMgr(uint32 team) : _team(team) { } ~ChannelMgr(); public: static ChannelMgr* forTeam(uint32 team); - void setTeam(uint32 newTeam) { team = newTeam; } + static Channel* GetChannelForPlayerByNamePart(std::string const& namePart, Player* playerSearcher); - Channel* GetJoinChannel(std::string const& name, uint32 channel_id); - Channel* GetChannel(std::string const& name, Player* p, bool pkt = true); + Channel* GetJoinChannel(uint32 channelId, std::string const& name, AreaTableEntry const* zoneEntry = nullptr); + Channel* GetChannel(uint32 channelId, std::string const& name, Player* player, bool pkt = true, AreaTableEntry const* zoneEntry = nullptr) const; void LeftChannel(std::string const& name); + void LeftChannel(uint32 channelId, AreaTableEntry const* zoneEntry); private: - ChannelMap channels; - uint32 team; + CustomChannelContainer _customChannels; + BuiltinChannelContainer _channels; + uint32 const _team; - void MakeNotOnPacket(WorldPacket* data, std::string const& name); + static void MakeNotOnPacket(WorldPacket* data, std::string const& name); }; #endif diff --git a/src/server/game/Combat/HostileRefManager.cpp b/src/server/game/Combat/HostileRefManager.cpp index 397fd59b7d1..965b910c3f4 100644 --- a/src/server/game/Combat/HostileRefManager.cpp +++ b/src/server/game/Combat/HostileRefManager.cpp @@ -28,8 +28,8 @@ HostileRefManager::~HostileRefManager() } //================================================= -// send threat to all my hateres for the victim -// The victim is hated than by them as well +// send threat to all my haters for the victim +// The victim is then hated by them as well // use for buffs and healing threat functionality void HostileRefManager::threatAssist(Unit* victim, float baseThreat, SpellInfo const* threatSpell) @@ -37,9 +37,10 @@ void HostileRefManager::threatAssist(Unit* victim, float baseThreat, SpellInfo c if (getSize() == 0) return; - HostileReference* ref = getFirst(); float threat = ThreatCalcHelper::calcThreat(victim, iOwner, baseThreat, (threatSpell ? threatSpell->GetSchoolMask() : SPELL_SCHOOL_MASK_NORMAL), threatSpell); threat /= getSize(); + + HostileReference* ref = getFirst(); while (ref) { if (ThreatCalcHelper::isValidProcess(victim, ref->GetSource()->GetOwner(), threatSpell)) @@ -54,7 +55,6 @@ void HostileRefManager::threatAssist(Unit* victim, float baseThreat, SpellInfo c void HostileRefManager::addTempThreat(float threat, bool apply) { HostileReference* ref = getFirst(); - while (ref) { if (apply) diff --git a/src/server/game/Combat/HostileRefManager.h b/src/server/game/Combat/HostileRefManager.h index 855f9e3d272..859bee3caf7 100644 --- a/src/server/game/Combat/HostileRefManager.h +++ b/src/server/game/Combat/HostileRefManager.h @@ -31,18 +31,16 @@ class SpellInfo; class TC_GAME_API HostileRefManager : public RefManager<Unit, ThreatManager> { - private: - Unit* iOwner; public: - explicit HostileRefManager(Unit* owner) { iOwner = owner; } + explicit HostileRefManager(Unit* owner) : iOwner(owner) { } ~HostileRefManager(); - Unit* GetOwner() { return iOwner; } + Unit* GetOwner() const { return iOwner; } // send threat to all my hateres for the victim // The victim is hated than by them as well // use for buffs and healing threat functionality - void threatAssist(Unit* victim, float baseThreat, SpellInfo const* threatSpell = NULL); + void threatAssist(Unit* victim, float baseThreat, SpellInfo const* threatSpell = nullptr); void addTempThreat(float threat, bool apply); @@ -68,6 +66,9 @@ class TC_GAME_API HostileRefManager : public RefManager<Unit, ThreatManager> void deleteReference(Unit* creature); void UpdateVisibility(); + + private: + Unit* iOwner; }; //================================================= #endif diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp index 9767d69e2aa..fec4ec300cc 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -30,7 +30,7 @@ //============================================================== // The hatingUnit is not used yet -float ThreatCalcHelper::calcThreat(Unit* hatedUnit, Unit* /*hatingUnit*/, float threat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell) +float ThreatCalcHelper::calcThreat(Unit* hatedUnit, Unit* /*hatingUnit*/, float threat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell /*= nullptr*/) { if (threatSpell) { @@ -39,18 +39,18 @@ float ThreatCalcHelper::calcThreat(Unit* hatedUnit, Unit* /*hatingUnit*/, float threat *= threatEntry->pctMod; // Energize is not affected by Mods - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; i++) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (threatSpell->Effects[i].Effect == SPELL_EFFECT_ENERGIZE || threatSpell->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_ENERGIZE) return threat; if (Player* modOwner = hatedUnit->GetSpellModOwner()) - modOwner->ApplySpellMod(threatSpell->Id, SPELLMOD_THREAT, threat); + modOwner->ApplySpellMod<SPELLMOD_THREAT>(threatSpell->Id, threat); } return hatedUnit->ApplyTotalThreatModifier(threat, schoolMask); } -bool ThreatCalcHelper::isValidProcess(Unit* hatedUnit, Unit* hatingUnit, SpellInfo const* threatSpell) +bool ThreatCalcHelper::isValidProcess(Unit* hatedUnit, Unit* hatingUnit, SpellInfo const* threatSpell /*= nullptr*/) { //function deals with adding threat and adding players and pets into ThreatList //mobs, NPCs, guards have ThreatList and HateOfflineList @@ -134,18 +134,20 @@ void HostileReference::fireStatusChanged(ThreatRefStatusChangeEvent& threatRefSt void HostileReference::addThreat(float modThreat) { + if (!modThreat) + return; + iThreat += modThreat; + // the threat is changed. Source and target unit have to be available // if the link was cut before relink it again if (!isOnline()) updateOnlineStatus(); - if (modThreat != 0.0f) - { - ThreatRefStatusChangeEvent event(UEV_THREAT_REF_THREAT_CHANGE, this, modThreat); - fireStatusChanged(event); - } - if (isValid() && modThreat >= 0.0f) + ThreatRefStatusChangeEvent event(UEV_THREAT_REF_THREAT_CHANGE, this, modThreat); + fireStatusChanged(event); + + if (isValid() && modThreat > 0.0f) { Unit* victimOwner = getTarget()->GetCharmerOrOwner(); if (victimOwner && victimOwner->IsAlive()) @@ -155,9 +157,7 @@ void HostileReference::addThreat(float modThreat) void HostileReference::addThreatPercent(int32 percent) { - float tmpThreat = iThreat; - AddPct(tmpThreat, percent); - addThreat(tmpThreat - iThreat); + addThreat(CalculatePct(iThreat, percent)); } //============================================================ @@ -193,6 +193,7 @@ void HostileReference::updateOnlineStatus() else accessible = true; } + setAccessibleState(accessible); setOnlineOfflineState(online); } @@ -221,7 +222,7 @@ void HostileReference::setAccessibleState(bool isAccessible) { iAccessible = isAccessible; - ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ASSECCIBLE_STATUS, this); + ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ACCESSIBLE_STATUS, this); fireStatusChanged(event); } } diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h index a68d803304d..cad62659317 100644 --- a/src/server/game/Combat/ThreatManager.h +++ b/src/server/game/Combat/ThreatManager.h @@ -41,8 +41,8 @@ class SpellInfo; struct TC_GAME_API ThreatCalcHelper { - static float calcThreat(Unit* hatedUnit, Unit* hatingUnit, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = NULL); - static bool isValidProcess(Unit* hatedUnit, Unit* hatingUnit, SpellInfo const* threatSpell = NULL); + static float calcThreat(Unit* hatedUnit, Unit* hatingUnit, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = nullptr); + static bool isValidProcess(Unit* hatedUnit, Unit* hatingUnit, SpellInfo const* threatSpell = nullptr); }; //============================================================== @@ -54,11 +54,11 @@ class TC_GAME_API HostileReference : public Reference<Unit, ThreatManager> //================================================= void addThreat(float modThreat); - void setThreat(float threat) { addThreat(threat - getThreat()); } + void setThreat(float threat) { addThreat(threat - iThreat); } void addThreatPercent(int32 percent); - float getThreat() const { return iThreat; } + float getThreat() const { return iThreat + iTempThreatModifier; } bool isOnline() const { return iOnline; } @@ -66,27 +66,27 @@ class TC_GAME_API HostileReference : public Reference<Unit, ThreatManager> // in this case online = true, but accessible = false bool isAccessible() const { return iAccessible; } - // used for temporary setting a threat and reducting it later again. + // used for temporary setting a threat and reducing it later again. // the threat modification is stored void setTempThreat(float threat) { - addTempThreat(threat - getThreat()); + addTempThreat(threat - iTempThreatModifier); } void addTempThreat(float threat) { - iTempThreatModifier = threat; - if (iTempThreatModifier != 0.0f) - addThreat(iTempThreatModifier); + if (!threat) + return; + + iTempThreatModifier += threat; + + ThreatRefStatusChangeEvent event(UEV_THREAT_REF_THREAT_CHANGE, this, threat); + fireStatusChanged(event); } void resetTempThreat() { - if (iTempThreatModifier != 0.0f) - { - addThreat(-iTempThreatModifier); - iTempThreatModifier = 0.0f; - } + addTempThreat(-iTempThreatModifier); } float getTempThreatModifier() { return iTempThreatModifier; } @@ -100,7 +100,7 @@ class TC_GAME_API HostileReference : public Reference<Unit, ThreatManager> void setAccessibleState(bool isAccessible); //================================================= - bool operator == (const HostileReference& hostileRef) const { return hostileRef.getUnitGuid() == getUnitGuid(); } + bool operator==(HostileReference const& hostileRef) const { return hostileRef.getUnitGuid() == getUnitGuid(); } //================================================= @@ -113,7 +113,7 @@ class TC_GAME_API HostileReference : public Reference<Unit, ThreatManager> //================================================= - HostileReference* next() { return ((HostileReference*) Reference<Unit, ThreatManager>::next()); } + HostileReference* next() { return static_cast<HostileReference*>(Reference<Unit, ThreatManager>::next()); } //================================================= @@ -125,14 +125,17 @@ class TC_GAME_API HostileReference : public Reference<Unit, ThreatManager> // Tell our refFrom (source) object, that the link is cut (Target destroyed) void sourceObjectDestroyLink() override; + private: // Inform the source, that the status of that reference was changed void fireStatusChanged(ThreatRefStatusChangeEvent& threatRefStatusChangeEvent); Unit* GetSourceUnit(); + private: float iThreat; - float iTempThreatModifier; // used for taunt + float iTempThreatModifier; // used for SPELL_AURA_MOD_TOTAL_THREAT + ObjectGuid iUnitGuid; bool iOnline; bool iAccessible; diff --git a/src/server/game/Combat/UnitEvents.h b/src/server/game/Combat/UnitEvents.h index f50edcf3c7d..ee1a960c524 100644 --- a/src/server/game/Combat/UnitEvents.h +++ b/src/server/game/Combat/UnitEvents.h @@ -40,7 +40,7 @@ enum UNIT_EVENT_TYPE UEV_THREAT_REF_REMOVE_FROM_LIST = 1<<2, // Player/Pet entered/left water or some other place where it is/was not accessible for the creature - UEV_THREAT_REF_ASSECCIBLE_STATUS = 1<<3, + UEV_THREAT_REF_ACCESSIBLE_STATUS = 1<<3, // Threat list is going to be sorted (if dirty flag is set) UEV_THREAT_SORT_LIST = 1<<4, @@ -58,7 +58,7 @@ enum UNIT_EVENT_TYPE //UEV_UNIT_HEALTH_CHANGE = 1<<8, }; -#define UEV_THREAT_REF_EVENT_MASK (UEV_THREAT_REF_ONLINE_STATUS | UEV_THREAT_REF_THREAT_CHANGE | UEV_THREAT_REF_REMOVE_FROM_LIST | UEV_THREAT_REF_ASSECCIBLE_STATUS) +#define UEV_THREAT_REF_EVENT_MASK (UEV_THREAT_REF_ONLINE_STATUS | UEV_THREAT_REF_THREAT_CHANGE | UEV_THREAT_REF_REMOVE_FROM_LIST | UEV_THREAT_REF_ACCESSIBLE_STATUS) #define UEV_THREAT_MANAGER_EVENT_MASK (UEV_THREAT_SORT_LIST | UEV_THREAT_SET_NEXT_TARGET | UEV_THREAT_VICTIM_CHANGED) #define UEV_ALL_EVENT_MASK (0xffffffff) @@ -69,14 +69,16 @@ enum UNIT_EVENT_TYPE class UnitBaseEvent { - private: - uint32 iType; public: - UnitBaseEvent(uint32 pType) { iType = pType; } + explicit UnitBaseEvent(uint32 pType) { iType = pType; } uint32 getType() const { return iType; } bool matchesTypeMask(uint32 pMask) const { return (iType & pMask) != 0; } - void setType(uint32 pType) { iType = pType; } + private: + uint32 iType; + + protected: + ~UnitBaseEvent() { } }; //============================================================== @@ -92,14 +94,15 @@ class TC_GAME_API ThreatRefStatusChangeEvent : public UnitBaseEvent bool iBValue; }; ThreatManager* iThreatManager; + public: - ThreatRefStatusChangeEvent(uint32 pType) : UnitBaseEvent(pType), iThreatManager(NULL) { iHostileReference = NULL; } + explicit ThreatRefStatusChangeEvent(uint32 pType) : UnitBaseEvent(pType), iHostileReference(nullptr), iThreatManager(nullptr) { } - ThreatRefStatusChangeEvent(uint32 pType, HostileReference* pHostileReference) : UnitBaseEvent(pType), iThreatManager(NULL) { iHostileReference = pHostileReference; } + ThreatRefStatusChangeEvent(uint32 pType, HostileReference* pHostileReference) : UnitBaseEvent(pType), iHostileReference(pHostileReference), iThreatManager(nullptr) { } - ThreatRefStatusChangeEvent(uint32 pType, HostileReference* pHostileReference, float pValue) : UnitBaseEvent(pType), iThreatManager(NULL) { iHostileReference = pHostileReference; iFValue = pValue; } + ThreatRefStatusChangeEvent(uint32 pType, HostileReference* pHostileReference, float pValue) : UnitBaseEvent(pType), iHostileReference(pHostileReference), iFValue(pValue), iThreatManager(nullptr) { } - ThreatRefStatusChangeEvent(uint32 pType, HostileReference* pHostileReference, bool pValue) : UnitBaseEvent(pType), iThreatManager(NULL) { iHostileReference = pHostileReference; iBValue = pValue; } + ThreatRefStatusChangeEvent(uint32 pType, HostileReference* pHostileReference, bool pValue) : UnitBaseEvent(pType), iHostileReference(pHostileReference), iBValue(pValue), iThreatManager(nullptr) { } int32 getIValue() const { return iIValue; } @@ -116,20 +119,4 @@ class TC_GAME_API ThreatRefStatusChangeEvent : public UnitBaseEvent ThreatManager* getThreatManager() const { return iThreatManager; } }; -//============================================================== - -class ThreatManagerEvent : public ThreatRefStatusChangeEvent -{ - private: - ThreatContainer* iThreatContainer; - public: - ThreatManagerEvent(uint32 pType) : ThreatRefStatusChangeEvent(pType), iThreatContainer(NULL) { } - ThreatManagerEvent(uint32 pType, HostileReference* pHostileReference) : ThreatRefStatusChangeEvent(pType, pHostileReference), iThreatContainer(NULL) { } - - void setThreatContainer(ThreatContainer* pThreatContainer) { iThreatContainer = pThreatContainer; } - - ThreatContainer* getThreatContainer() const { return iThreatContainer; } -}; - -//============================================================== #endif 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 ba20bb68750..9df9d2ad53c 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -438,10 +438,9 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, if (updateLevel) SelectLevel(); + UpdateLevelDependantStats(); + SetMeleeDamageSchool(SpellSchools(cInfo->dmgschool)); - CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(getLevel(), cInfo->unit_class); - float armor = (float)stats->GenerateArmor(cInfo); /// @todo Why is this treated as uint32 when it's a float? - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, armor); SetModifierValue(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_HOLY])); SetModifierValue(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_FIRE])); SetModifierValue(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_NATURE])); @@ -1177,15 +1176,18 @@ void Creature::SelectLevel() { CreatureTemplate const* cInfo = GetCreatureTemplate(); - uint32 rank = IsPet() ? 0 : cInfo->rank; - // level uint8 minlevel = std::min(cInfo->maxlevel, cInfo->minlevel); uint8 maxlevel = std::max(cInfo->maxlevel, cInfo->minlevel); uint8 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel); SetLevel(level); +} - CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(level, cInfo->unit_class); +void Creature::UpdateLevelDependantStats() +{ + CreatureTemplate const* cInfo = GetCreatureTemplate(); + uint32 rank = IsPet() ? 0 : cInfo->rank; + CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(getLevel(), cInfo->unit_class); // health float healthmod = _GetHealthMod(rank); @@ -1228,6 +1230,9 @@ void Creature::SelectLevel() SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, stats->AttackPower); SetModifierValue(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE, stats->RangedAttackPower); + + float armor = (float)stats->GenerateArmor(cInfo); /// @todo Why is this treated as uint32 when it's a float? + SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, armor); } float Creature::_GetHealthMod(int32 Rank) @@ -1445,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 { @@ -1658,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); @@ -1691,6 +1695,7 @@ void Creature::setDeathState(DeathState s) SetLootRecipient(nullptr); ResetPlayerDamageReq(); + SetCannotReachTarget(false); UpdateMovementFlags(); ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~UNIT_STATE_IGNORE_PATHFINDING)); @@ -1762,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) @@ -1774,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 2bcd7b5cd23..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; @@ -441,6 +441,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma bool Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 entry, float x, float y, float z, float ang, CreatureData const* data = nullptr, uint32 vehId = 0); bool LoadCreaturesAddon(); void SelectLevel(); + void UpdateLevelDependantStats(); void LoadEquipment(int8 id = 1, bool force = false); void SetSpawnHealth(); @@ -690,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/Creature/GossipDef.h b/src/server/game/Entities/Creature/GossipDef.h index 01b27032286..623b3de00a1 100644 --- a/src/server/game/Entities/Creature/GossipDef.h +++ b/src/server/game/Entities/Creature/GossipDef.h @@ -51,6 +51,7 @@ enum Gossip_Option GOSSIP_OPTION_UNLEARNPETTALENTS = 17, //UNIT_NPC_FLAG_TRAINER (16) (bonus option for GOSSIP_OPTION_TRAINER) GOSSIP_OPTION_LEARNDUALSPEC = 18, //UNIT_NPC_FLAG_TRAINER (16) (bonus option for GOSSIP_OPTION_TRAINER) GOSSIP_OPTION_OUTDOORPVP = 19, //added by code (option for outdoor pvp creatures) + GOSSIP_OPTION_DUALSPEC_INFO = 20, //UNIT_NPC_FLAG_TRAINER (16) (bonus option for GOSSIP_OPTION_TRAINER) GOSSIP_OPTION_MAX }; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index b495b02ee72..f1365ac3150 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::NearestAttackableNoTotemUnitInObjectRangeCheck checker(this, owner, radius); + Trinity::UnitLastSearcher<Trinity::NearestAttackableNoTotemUnitInObjectRangeCheck> 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) { @@ -1092,25 +1110,8 @@ void GameObject::TriggeringLinkedGameObject(uint32 trapEntry, Unit* target) if (!trapSpell) // checked at load already return; - float range = float(target->GetSpellMaxRangeForTarget(GetOwner(), trapSpell)); - - // search nearest linked GO - GameObject* trapGO = nullptr; - { - // using original GO distance - CellCoord p(Trinity::ComputeCellCoord(GetPositionX(), GetPositionY())); - Cell cell(p); - - Trinity::NearestGameObjectEntryInObjectRangeCheck go_check(*target, trapEntry, range); - Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck> checker(this, trapGO, go_check); - - TypeContainerVisitor<Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker); - cell.Visit(p, object_checker, *GetMap(), *target, range); - } - - // found correct GO - if (trapGO) - trapGO->CastSpell(target, trapInfo->trap.spellId); + if (GameObject* trapGO = GetLinkedTrap()) + trapGO->CastSpell(target, trapSpell->Id); } GameObject* GameObject::LookupFishingHoleAround(float range) @@ -1828,18 +1829,23 @@ 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()); if (owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) trigger->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); + // copy pvp state flags from owner + trigger->SetByteValue(UNIT_FIELD_BYTES_2, 1, owner->GetByteValue(UNIT_FIELD_BYTES_2, 1)); // needed for GO casts for proper target validation checks trigger->SetOwnerGUID(owner->GetGUID()); trigger->CastSpell(target ? target : trigger, spellInfo, triggered, nullptr, nullptr, owner->GetGUID()); } 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); @@ -2187,7 +2193,7 @@ Group* GameObject::GetLootRecipientGroup() const return sGroupMgr->GetGroupByGUID(m_lootRecipientGroup); } -void GameObject::SetLootRecipient(Unit* unit) +void GameObject::SetLootRecipient(Unit* unit, Group* group) { // set the player whose group should receive the right // to loot the creature after it dies @@ -2196,7 +2202,7 @@ void GameObject::SetLootRecipient(Unit* unit) if (!unit) { m_lootRecipient.Clear(); - m_lootRecipientGroup = 0; + m_lootRecipientGroup = group ? group->GetLowGUID() : 0; return; } @@ -2208,8 +2214,12 @@ void GameObject::SetLootRecipient(Unit* unit) return; m_lootRecipient = player->GetGUID(); - if (Group* group = player->GetGroup()) + + // either get the group from the passed parameter or from unit's one + if (group) m_lootRecipientGroup = group->GetLowGUID(); + else if (Group* unitGroup = player->GetGroup()) + m_lootRecipientGroup = unitGroup->GetLowGUID(); } bool GameObject::IsLootAllowedFor(Player const* player) const @@ -2227,6 +2237,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 842d5400fb2..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; @@ -812,12 +813,15 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> Player* GetLootRecipient() const; Group* GetLootRecipientGroup() const; - void SetLootRecipient(Unit* unit); + void SetLootRecipient(Unit* unit, Group* group = nullptr); bool IsLootAllowedFor(Player const* player) const; bool HasLootRecipient() const { return !m_lootRecipient.IsEmpty() || m_lootRecipientGroup; } 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..1173831be0b 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())) @@ -824,13 +824,13 @@ bool Item::IsFitToSpellRequirements(SpellInfo const* spellInfo) const { ItemTemplate const* proto = GetTemplate(); + bool const isEnchantSpell = spellInfo->HasEffect(SPELL_EFFECT_ENCHANT_ITEM) || spellInfo->HasEffect(SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) || spellInfo->HasEffect(SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC); if (spellInfo->EquippedItemClass != -1) // -1 == any item class { // Special case - accept vellum for armor/weapon requirements - if ((spellInfo->EquippedItemClass == ITEM_CLASS_ARMOR && proto->IsArmorVellum()) - ||(spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON && proto->IsWeaponVellum())) - if (spellInfo->IsAbilityOfSkillType(SKILL_ENCHANTING)) // only for enchanting spells - return true; + if (isEnchantSpell && ((spellInfo->EquippedItemClass == ITEM_CLASS_ARMOR && proto->IsArmorVellum()) + || (spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON && proto->IsWeaponVellum()))) + return true; if (spellInfo->EquippedItemClass != int32(proto->Class)) return false; // wrong item class @@ -842,7 +842,7 @@ bool Item::IsFitToSpellRequirements(SpellInfo const* spellInfo) const } } - if (spellInfo->EquippedItemInventoryTypeMask != 0) // 0 == any inventory type + if (isEnchantSpell && spellInfo->EquippedItemInventoryTypeMask != 0) // 0 == any inventory type { // Special case - accept weapon type for main and offhand requirements if (proto->InventoryType == INVTYPE_WEAPON && @@ -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 d3daab43b91..a5f5aa18b1d 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -173,7 +173,7 @@ void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c if (flags & UPDATEFLAG_STATIONARY_POSITION) { // UPDATETYPE_CREATE_OBJECT2 dynamic objects, corpses... - if (isType(TYPEMASK_DYNAMICOBJECT) || isType(TYPEMASK_CORPSE) || isType(TYPEMASK_PLAYER)) + if (isType(TYPEMASK_DYNAMICOBJECT | TYPEMASK_CORPSE | TYPEMASK_PLAYER)) updateType = UPDATETYPE_CREATE_OBJECT2; // UPDATETYPE_CREATE_OBJECT2 for pets... @@ -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); @@ -2475,6 +2403,16 @@ void WorldObject::PlayDirectSound(uint32 sound_id, Player* target /*= NULL*/) SendMessageToSet(&data, true); } +void WorldObject::PlayDirectMusic(uint32 music_id, Player* target /*= NULL*/) +{ + WorldPacket data(SMSG_PLAY_MUSIC, 4); + data << uint32(music_id); + if (target) + target->SendDirectMessage(&data); + else + SendMessageToSet(&data, true); +} + void WorldObject::DestroyForNearbyPlayers() { if (!IsInWorld()) @@ -2613,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 22528f8508a..e107a5f6bfd 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -513,6 +513,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation void PlayDistanceSound(uint32 sound_id, Player* target = NULL); void PlayDirectSound(uint32 sound_id, Player* target = NULL); + void PlayDirectMusic(uint32 music_id, Player* target = NULL); void SendObjectDeSpawnAnim(ObjectGuid guid); @@ -557,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 7a07b9d980c..3e0f9078c2c 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -401,6 +401,7 @@ Player::Player(WorldSession* session): Unit(true) m_canParry = false; m_canBlock = false; m_canTitanGrip = false; + m_titanGripPenaltySpellId = 0; m_ammoDPS = 0.0f; m_temporaryUnsummonedPetNumber = 0; @@ -938,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); @@ -3313,7 +3323,7 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent if (active) { if (spellInfo->IsPassive() && IsNeedCastPassiveSpellAtLearn(spellInfo)) - CastSpell (this, spellId, true); + CastSpell(this, spellId, true); } else if (IsInWorld()) { @@ -3587,6 +3597,15 @@ bool Player::IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const bool need_cast = (!spellInfo->Stances || (form && (spellInfo->Stances & (UI64LIT(1) << (form - 1)))) || (!form && spellInfo->HasAttribute(SPELL_ATTR2_NOT_NEED_SHAPESHIFT))); + // Check EquippedItemClass + // passive spells which apply aura and have an item requirement are to be added in Player::ApplyItemDependentAuras + if (spellInfo->IsPassive() && spellInfo->EquippedItemClass >= 0) + { + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (spellInfo->Effects[i].IsAura()) + return false; + } + //Check CasterAuraStates return need_cast && (!spellInfo->CasterAuraState || HasAuraState(AuraStateType(spellInfo->CasterAuraState))); } @@ -3816,7 +3835,11 @@ void Player::RemoveSpell(uint32 spell_id, bool disabled, bool learn_low_rank) } if (spell_id == 46917 && m_canTitanGrip) + { + RemoveAurasDueToSpell(m_titanGripPenaltySpellId); SetCanTitanGrip(false); + } + if (spell_id == 674 && m_canDualWield) SetCanDualWield(false); @@ -4211,9 +4234,10 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe charDelete_method = CHAR_DELETE_REMOVE; } + SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (ObjectGuid::LowType guildId = GetGuildIdFromDB(playerguid)) if (Guild* guild = sGuildMgr->GetGuildById(guildId)) - guild->DeleteMember(playerguid, false, false, true); + guild->DeleteMember(trans, playerguid, false, false, true); // close player ticket if any GmTicket* ticket = sTicketMgr->GetTicketByPlayer(playerguid); @@ -4240,8 +4264,6 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe // Completely remove from the database case CHAR_DELETE_REMOVE: { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_COD_ITEM_MAIL); stmt->setUInt32(0, guid); PreparedQueryResult resultMail = CharacterDatabase.Query(stmt); @@ -4530,26 +4552,27 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe trans->Append(stmt); Corpse::DeleteFromDB(playerguid, trans); - - CharacterDatabase.CommitTransaction(trans); break; } // The character gets unlinked from the account, the name gets freed up and appears as deleted ingame case CHAR_DELETE_UNLINK: { stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_DELETE_INFO); - stmt->setUInt32(0, guid); - - CharacterDatabase.Execute(stmt); + trans->Append(stmt); break; } default: TC_LOG_ERROR("entities.player.cheat", "Player::DeleteFromDB: Tried to delete player (%s) with unsupported delete method (%u).", playerguid.ToString().c_str(), charDelete_method); + + if (trans->GetSize() > 0) + CharacterDatabase.CommitTransaction(trans); return; } + CharacterDatabase.CommitTransaction(trans); + if (updateRealmChars) sWorld->UpdateRealmCharCount(accountId); @@ -5167,8 +5190,15 @@ void Player::CleanupChannels() Channel* ch = *m_channels.begin(); m_channels.erase(m_channels.begin()); // remove from player's channel list ch->LeaveChannel(this, false); // not send to client, not remove from player's channel list + + // delete channel if empty if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetTeam())) - cMgr->LeftChannel(ch->GetName()); // deleted channel if empty + { + if (ch->IsConstant()) + cMgr->LeftChannel(ch->GetChannelId(), ch->GetZoneEntry()); + else + cMgr->LeftChannel(ch->GetName()); + } } TC_LOG_DEBUG("chat.system", "Player::CleanupChannels: Channels of player '%s' (%s) cleaned up.", GetName().c_str(), GetGUID().ToString().c_str()); } @@ -5186,7 +5216,6 @@ void Player::UpdateLocalChannels(uint32 newZone) if (!cMgr) return; - std::string current_zone_name = current_zone->area_name[GetSession()->GetSessionDbcLocale()]; for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) { ChatChannelsEntry const* channelEntry = sChatChannelsStore.LookupEntry(i); @@ -5214,14 +5243,7 @@ void Player::UpdateLocalChannels(uint32 newZone) if (channelEntry->flags & CHANNEL_DBC_FLAG_CITY_ONLY && usedChannel) continue; // Already on the channel, as city channel names are not changing - std::string currentNameExt; - if (channelEntry->flags & CHANNEL_DBC_FLAG_CITY_ONLY) - currentNameExt = sObjectMgr->GetTrinityStringForDBCLocale(LANG_CHANNEL_CITY); - else - currentNameExt = current_zone_name; - - std::string newChannelName = Trinity::StringFormat(channelEntry->pattern[m_session->GetSessionDbcLocale()], currentNameExt.c_str()); - joinChannel = cMgr->GetJoinChannel(newChannelName, channelEntry->ChannelID); + joinChannel = cMgr->GetJoinChannel(channelEntry->ChannelID, std::string(), current_zone); if (usedChannel) { if (joinChannel != usedChannel) @@ -5234,7 +5256,7 @@ void Player::UpdateLocalChannels(uint32 newZone) } } else - joinChannel = cMgr->GetJoinChannel(channelEntry->pattern[m_session->GetSessionDbcLocale()], channelEntry->ChannelID); + joinChannel = cMgr->GetJoinChannel(channelEntry->ChannelID, std::string()); } else removeChannel = usedChannel; @@ -5244,10 +5266,10 @@ void Player::UpdateLocalChannels(uint32 newZone) if (removeChannel) { - removeChannel->LeaveChannel(this, sendRemove); // Leave old channel - std::string name = removeChannel->GetName(); // Store name, (*i)erase in LeftChannel - LeftChannel(removeChannel); // Remove from player's channel list - cMgr->LeftChannel(name); // Delete if empty + removeChannel->LeaveChannel(this, sendRemove); // Leave old channel + + LeftChannel(removeChannel); // Remove from player's channel list + cMgr->LeftChannel(removeChannel->GetChannelId(), removeChannel->GetZoneEntry()); // Delete if empty } } } @@ -5892,7 +5914,7 @@ void Player::UpdateWeaponSkill(WeaponAttackType attType) UpdateAllCritPercentages(); } -void Player::UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool defence) +void Player::UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool defense) { uint8 plevel = getLevel(); // if defense than victim == attacker uint8 greylevel = Trinity::XP::GetGrayLevel(plevel); @@ -5905,12 +5927,12 @@ void Player::UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool def if (lvldif < 3) lvldif = 3; - uint32 skilldif = 5 * plevel - (defence ? GetBaseDefenseSkillValue() : GetBaseWeaponSkillValue(attType)); + uint32 skilldif = 5 * plevel - (defense ? GetBaseDefenseSkillValue() : GetBaseWeaponSkillValue(attType)); if (skilldif <= 0) return; float chance = float(3 * lvldif * skilldif) / plevel; - if (!defence) + if (!defense) if (getClass() == CLASS_WARRIOR || getClass() == CLASS_ROGUE) chance += chance * 0.02f * GetStat(STAT_INTELLECT); @@ -5918,7 +5940,7 @@ void Player::UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool def if (roll_chance_f(chance)) { - if (defence) + if (defense) UpdateDefense(); else UpdateWeaponSkill(attType); @@ -7373,20 +7395,16 @@ void Player::_ApplyItemMods(Item* item, uint8 slot, bool apply) TC_LOG_DEBUG("entities.player.items", "Player::_ApplyItemMods: Applying mods for item %s", item->GetGUID().ToString().c_str()); - uint8 attacktype = Player::GetAttackBySlot(slot); - if (proto->Socket[0].Color) //only (un)equipping of items with sockets can influence metagems, so no need to waste time with normal items CorrectMetaGemEnchants(slot, apply); - if (attacktype < MAX_ATTACK) - _ApplyWeaponDependentAuraMods(item, WeaponAttackType(attacktype), apply); - _ApplyItemBonuses(proto, slot, apply); if (slot == EQUIPMENT_SLOT_RANGED) _ApplyAmmoBonuses(); ApplyItemEquipSpell(item, apply); + ApplyItemDependentAuras(item, apply); ApplyEnchantment(item, apply); TC_LOG_DEBUG("entities.player.items", "Player::_ApplyItemMods: completed"); @@ -7740,86 +7758,26 @@ void Player::_ApplyWeaponDamage(uint8 slot, ItemTemplate const* proto, ScalingSt UpdateDamagePhysical(attType); } -void Player::_ApplyWeaponDependentAuraMods(Item* item, WeaponAttackType attackType, bool apply) -{ - AuraEffectList const& auraCritList = GetAuraEffectsByType(SPELL_AURA_MOD_WEAPON_CRIT_PERCENT); - for (AuraEffectList::const_iterator itr = auraCritList.begin(); itr != auraCritList.end(); ++itr) - _ApplyWeaponDependentAuraCritMod(item, attackType, *itr, apply); - - AuraEffectList const& auraDamageFlatList = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE); - for (AuraEffectList::const_iterator itr = auraDamageFlatList.begin(); itr != auraDamageFlatList.end(); ++itr) - _ApplyWeaponDependentAuraDamageMod(item, attackType, *itr, apply); - - AuraEffectList const& auraDamagePctList = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - for (AuraEffectList::const_iterator itr = auraDamagePctList.begin(); itr != auraDamagePctList.end(); ++itr) - _ApplyWeaponDependentAuraDamageMod(item, attackType, *itr, apply); -} - -void Player::_ApplyWeaponDependentAuraCritMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply) -{ - // don't apply mod if item is broken or cannot be used - if (item->IsBroken() || !CanUseAttackType(attackType)) - return; - - // generic not weapon specific case processes in aura code - if (aura->GetSpellInfo()->EquippedItemClass == -1) - return; - - BaseModGroup mod; - switch (attackType) - { - case BASE_ATTACK: mod = CRIT_PERCENTAGE; break; - case OFF_ATTACK: mod = OFFHAND_CRIT_PERCENTAGE;break; - case RANGED_ATTACK: mod = RANGED_CRIT_PERCENTAGE; break; - default: return; - } - - if (item->IsFitToSpellRequirements(aura->GetSpellInfo())) - HandleBaseModValue(mod, FLAT_MOD, float (aura->GetAmount()), apply); -} - -void Player::_ApplyWeaponDependentAuraDamageMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply) +void Player::ApplyItemDependentAuras(Item* item, bool apply) { - // don't apply mod if item is broken or cannot be used - if (item->IsBroken() || !CanUseAttackType(attackType)) - return; - - // ignore spell mods for not wands - if ((aura->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) == 0 && (getClassMask() & CLASSMASK_WAND_USERS) == 0) - return; - - // generic not weapon specific case processes in aura code - if (aura->GetSpellInfo()->EquippedItemClass == -1) - return; - - UnitMods unitMod; - switch (attackType) + if (apply) { - case BASE_ATTACK: unitMod = UNIT_MOD_DAMAGE_MAINHAND; break; - case OFF_ATTACK: unitMod = UNIT_MOD_DAMAGE_OFFHAND; break; - case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break; - default: return; - } + PlayerSpellMap const& spells = GetSpellMap(); + for (auto itr = spells.begin(); itr != spells.end(); ++itr) + { + if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled) + continue; - UnitModifierType unitModType; - switch (aura->GetAuraType()) - { - case SPELL_AURA_MOD_DAMAGE_DONE: unitModType = TOTAL_VALUE; break; - case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: unitModType = TOTAL_PCT; break; - default: return; - } + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); + if (!spellInfo || !spellInfo->IsPassive() || spellInfo->EquippedItemClass < 0) + continue; - if (item->IsFitToSpellRequirements(aura->GetSpellInfo())) - { - HandleStatModifier(unitMod, unitModType, float(aura->GetAmount()), apply); - if (unitModType == TOTAL_VALUE) - { - if (aura->GetAmount() > 0) - ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS, aura->GetAmount(), apply); - else - ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG, aura->GetAmount(), apply); + if (!HasAura(itr->first) && HasItemFitToSpellRequirements(spellInfo)) + AddAura(itr->first, this); // no SMSG_SPELL_GO in sniff found } } + else + RemoveItemDependentAurasAndCasts(item); } void Player::ApplyItemEquipSpell(Item* item, bool apply, bool form_change) @@ -7918,8 +7876,10 @@ void Player::UpdateEquipSpellsAtFormChange() } } } -void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx) + +void Player::CastItemCombatSpell(DamageInfo const& damageInfo) { + Unit* target = damageInfo.GetVictim(); if (!target || !target->IsAlive() || target == this) return; @@ -7927,7 +7887,9 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 { // If usable, try to cast item spell if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (!item->IsBroken() && CanUseAttackType(attType)) + { + if (!item->IsBroken() && CanUseAttackType(damageInfo.GetAttackType())) + { if (ItemTemplate const* proto = item->GetTemplate()) { // Additional check for weapons @@ -7935,30 +7897,42 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 { // offhand item cannot proc from main hand hit etc EquipmentSlots slot; - switch (attType) + switch (damageInfo.GetAttackType()) { - case BASE_ATTACK: slot = EQUIPMENT_SLOT_MAINHAND; break; - case OFF_ATTACK: slot = EQUIPMENT_SLOT_OFFHAND; break; - case RANGED_ATTACK: slot = EQUIPMENT_SLOT_RANGED; break; - default: slot = EQUIPMENT_SLOT_END; break; + case BASE_ATTACK: + slot = EQUIPMENT_SLOT_MAINHAND; + break; + case OFF_ATTACK: + slot = EQUIPMENT_SLOT_OFFHAND; + break; + case RANGED_ATTACK: + slot = EQUIPMENT_SLOT_RANGED; + break; + default: + slot = EQUIPMENT_SLOT_END; + break; } if (slot != i) continue; // Check if item is useable (forms or disarm) - if (attType == BASE_ATTACK) + if (damageInfo.GetAttackType() == BASE_ATTACK) if (!IsUseEquipedWeapon(true) && !IsInFeralForm()) continue; } - CastItemCombatSpell(target, attType, procVictim, procEx, item, proto); + + CastItemCombatSpell(damageInfo, item, proto); } + } + } } } -void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx, Item* item, ItemTemplate const* proto) +void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemTemplate const* proto) { // Can do effect if any damage done to target - if (procVictim & PROC_FLAG_TAKEN_DAMAGE) - //if (damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE) + // for done procs allow normal + critical + absorbs by default + bool canTrigger = (damageInfo.GetHitMask() & (PROC_HIT_NORMAL | PROC_HIT_CRITICAL | PROC_HIT_ABSORB)) != 0; + if (canTrigger) { for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { @@ -7988,14 +7962,14 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 if (spellData.SpellPPMRate) { - uint32 WeaponSpeed = GetAttackTime(attType); + uint32 WeaponSpeed = GetAttackTime(damageInfo.GetAttackType()); chance = GetPPMProcChance(WeaponSpeed, spellData.SpellPPMRate, spellInfo); } else if (chance > 100.0f) chance = GetWeaponProcChance(); if (roll_chance_f(chance)) - CastSpell(target, spellInfo->Id, true, item); + CastSpell(damageInfo.GetVictim(), spellInfo->Id, true, item); } } @@ -8013,18 +7987,17 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 continue; SpellEnchantProcEntry const* entry = sSpellMgr->GetSpellEnchantProcEvent(enchant_id); - if (entry && entry->procEx) { // Check hit/crit/dodge/parry requirement - if ((entry->procEx & procEx) == 0) + if ((entry->procEx & damageInfo.GetHitMask()) == 0) continue; } else { // Can do effect if any damage done to target - if (!(procVictim & PROC_FLAG_TAKEN_DAMAGE)) - //if (!(damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE)) + // for done procs allow normal + critical + absorbs by default + if (!canTrigger) continue; } @@ -8037,7 +8010,6 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 } float chance = pEnchant->amount[s] != 0 ? float(pEnchant->amount[s]) : GetWeaponProcChance(); - if (entry) { if (entry->PPMChance) @@ -8047,7 +8019,7 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 } // Apply spell mods - ApplySpellMod(pEnchant->spellid[s], SPELLMOD_CHANCE_OF_SUCCESS, chance); + ApplySpellMod<SPELLMOD_CHANCE_OF_SUCCESS>(pEnchant->spellid[s], chance); // Shiv has 100% chance to apply the poison if (FindCurrentSpellBySpellId(5938) && e_slot == TEMP_ENCHANTMENT_SLOT) @@ -8058,7 +8030,7 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 if (spellInfo->IsPositive()) CastSpell(this, spellInfo, true, item); else - CastSpell(target, spellInfo, true, item); + CastSpell(damageInfo.GetVictim(), spellInfo, true, item); } } } @@ -8180,10 +8152,7 @@ void Player::_RemoveAllItemMods() if (!proto) continue; - uint32 attacktype = Player::GetAttackBySlot(i); - if (attacktype < MAX_ATTACK) - _ApplyWeaponDependentAuraMods(m_items[i], WeaponAttackType(attacktype), false); - + ApplyItemDependentAuras(m_items[i], false); _ApplyItemBonuses(proto, i, false); if (i == EQUIPMENT_SLOT_RANGED) @@ -8209,10 +8178,7 @@ void Player::_ApplyAllItemMods() if (!proto) continue; - uint32 attacktype = Player::GetAttackBySlot(i); - if (attacktype < MAX_ATTACK) - _ApplyWeaponDependentAuraMods(m_items[i], WeaponAttackType(attacktype), true); - + ApplyItemDependentAuras(m_items[i], true); _ApplyItemBonuses(proto, i, true); if (i == EQUIPMENT_SLOT_RANGED) @@ -9368,7 +9334,7 @@ void Player::SendBattlefieldWorldStates() const /// Send misc stuff that needs to be sent on every login, like the battle timers. if (sWorld->getBoolConfig(CONFIG_WINTERGRASP_ENABLE)) { - if (BattlefieldWG* wg = (BattlefieldWG*)sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG)) + if (BattlefieldWG* wg = static_cast<BattlefieldWG*>(sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG))) { SendUpdateWorldState(BATTLEFIELD_WG_WORLD_STATE_ACTIVE, wg->IsWarTime() ? 0 : 1); uint32 timer = wg->IsWarTime() ? 0 : (wg->GetTimer() / 1000); // 0 - Time to next battle @@ -10122,7 +10088,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) @@ -10144,6 +10110,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)) { @@ -11609,10 +11595,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) @@ -12074,6 +12058,9 @@ Item* Player::EquipItem(uint16 pos, Item* pItem, bool update) return pItem2; } + if (slot == EQUIPMENT_SLOT_MAINHAND || slot == EQUIPMENT_SLOT_OFFHAND) + CheckTitanGripPenalty(); + // only for full equip instead adding to stack UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry()); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM, pItem->GetEntry(), slot); @@ -12097,6 +12084,9 @@ void Player::QuickEquipItem(uint16 pos, Item* pItem) pItem->SendUpdateToPlayer(this); } + if (slot == EQUIPMENT_SLOT_MAINHAND || slot == EQUIPMENT_SLOT_OFFHAND) + CheckTitanGripPenalty(); + UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry()); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM, pItem->GetEntry(), slot); } @@ -12179,9 +12169,6 @@ void Player::RemoveItem(uint8 bag, uint8 slot, bool update) // remove item dependent auras and casts (only weapon and armor slots) if (slot < EQUIPMENT_SLOT_END) { - if (update) - RemoveItemDependentAurasAndCasts(pItem); - // remove held enchantments, update expertise if (slot == EQUIPMENT_SLOT_MAINHAND) { @@ -12217,7 +12204,11 @@ void Player::RemoveItem(uint8 bag, uint8 slot, bool update) SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), ObjectGuid::Empty); if (slot < EQUIPMENT_SLOT_END) + { SetVisibleItemSlot(slot, nullptr); + if (slot == EQUIPMENT_SLOT_MAINHAND || slot == EQUIPMENT_SLOT_OFFHAND) + CheckTitanGripPenalty(); + } } else if (Bag* pBag = GetBagByPos(bag)) pBag->RemoveItem(slot, update); @@ -12268,7 +12259,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); } } @@ -12286,7 +12277,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); @@ -12328,9 +12319,6 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) if (slot < EQUIPMENT_SLOT_END) { - // remove item dependent auras and casts (only weapon and armor slots) - RemoveItemDependentAurasAndCasts(pItem); - // update expertise and armor penetration - passive auras may need it switch (slot) { @@ -12359,7 +12347,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) @@ -13299,12 +13287,52 @@ bool Player::IsUseEquipedWeapon(bool mainhand) const return !IsInFeralForm() && (!mainhand || !HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED)); } +void Player::SetCanTitanGrip(bool value, uint32 penaltySpellId /*= 0*/) +{ + if (value == m_canTitanGrip) + return; + + m_canTitanGrip = value; + m_titanGripPenaltySpellId = penaltySpellId; +} + +void Player::CheckTitanGripPenalty() +{ + if (!CanTitanGrip()) + return; + + bool apply = IsUsingTwoHandedWeaponInOneHand(); + if (apply) + { + if (!HasAura(m_titanGripPenaltySpellId)) + CastSpell((Unit*)nullptr, m_titanGripPenaltySpellId, true); + } + else + RemoveAurasDueToSpell(m_titanGripPenaltySpellId); +} + bool Player::IsTwoHandUsed() const { Item* mainItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); return mainItem && mainItem->GetTemplate()->InventoryType == INVTYPE_2HWEAPON && !CanTitanGrip(); } +bool Player::IsUsingTwoHandedWeaponInOneHand() const +{ + Item* offItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + if (offItem && offItem->GetTemplate()->InventoryType == INVTYPE_2HWEAPON) + return true; + + Item* mainItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); + if (!mainItem || mainItem->GetTemplate()->InventoryType != INVTYPE_2HWEAPON) + return false; + + if (!offItem) + return false; + + return true; +} + void Player::TradeCancel(bool sendback) { if (m_trade) @@ -13661,7 +13689,7 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool break; case ITEM_MOD_DEFENSE_SKILL_RATING: ApplyRatingMod(CR_DEFENSE_SKILL, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ %u DEFENCE", enchant_amount); + TC_LOG_DEBUG("entities.player.items", "+ %u DEFENSE", enchant_amount); break; case ITEM_MOD_DODGE_RATING: ApplyRatingMod(CR_DODGE, enchant_amount, apply); @@ -14014,6 +14042,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool break; } case GOSSIP_OPTION_LEARNDUALSPEC: + case GOSSIP_OPTION_DUALSPEC_INFO: if (!(GetSpecsCount() == 1 && creature->isCanTrainingAndResetTalentsOf(this) && !(getLevel() < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL)))) canTalk = false; break; @@ -14195,6 +14224,7 @@ void Player::OnGossipSelect(WorldObject* source, uint32 gossipListId, uint32 men switch (gossipOptionId) { case GOSSIP_OPTION_GOSSIP: + case GOSSIP_OPTION_DUALSPEC_INFO: { if (menuItemData->GossipActionPoi) PlayerTalkClass->SendPointOfInterest(menuItemData->GossipActionPoi); @@ -14236,8 +14266,8 @@ void Player::OnGossipSelect(WorldObject* source, uint32 gossipListId, uint32 men CastSpell(this, 63680, true, nullptr, nullptr, GetGUID()); CastSpell(this, 63624, true, nullptr, nullptr, GetGUID()); - // Should show another Gossip text with "Congratulations..." - PlayerTalkClass->SendCloseGossip(); + PrepareGossipMenu(source, menuItemData->GossipActionMenuId); + SendPreparedGossip(source); } break; case GOSSIP_OPTION_UNLEARNTALENTS: @@ -14755,7 +14785,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; } } @@ -14770,7 +14802,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; } } @@ -14919,20 +14953,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); } } @@ -15053,10 +15087,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, RemoveActiveQuest(quest_id, false); if (quest->CanIncreaseRewardedQuestCounters()) - { - m_RewardedQuests.insert(quest_id); - m_RewardedQuestsSave[quest_id] = QUEST_DEFAULT_SAVE_TYPE; - } + SetRewardedQuest(quest_id); // StoreNewItem, mail reward, etc. save data directly to the database // to prevent exploitable data desynchronisation we save the quest status to the database too @@ -15112,15 +15143,24 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, sScriptMgr->OnQuestStatusChange(this, quest_id); } -void Player::FailQuest(uint32 questId) +void Player::SetRewardedQuest(uint32 quest_id) { + m_RewardedQuests.insert(quest_id); + m_RewardedQuestsSave[quest_id] = QUEST_DEFAULT_SAVE_TYPE; +} +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); @@ -15146,13 +15186,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); } } @@ -15163,13 +15203,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); } } @@ -15419,6 +15459,17 @@ bool Player::SatisfyQuestReputation(Quest const* qInfo, bool msg) bool Player::SatisfyQuestStatus(Quest const* qInfo, bool msg) const { + if (GetQuestStatus(qInfo->GetQuestId()) == QUEST_STATUS_REWARDED) + { + if (msg) + { + SendCanTakeQuestResponse(INVALIDREASON_QUEST_ALREADY_DONE); + TC_LOG_DEBUG("misc", "Player::SatisfyQuestStatus: Sent QUEST_STATUS_REWARDED (QuestID: %u) because player '%s' (%s) quest status is already REWARDED.", + qInfo->GetQuestId(), GetName().c_str(), GetGUID().ToString().c_str()); + } + return false; + } + if (GetQuestStatus(qInfo->GetQuestId()) != QUEST_STATUS_NONE) { if (msg) @@ -15862,21 +15913,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; } @@ -15889,7 +15937,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); @@ -15899,9 +15947,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; @@ -16462,12 +16508,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); @@ -16476,7 +16522,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) @@ -16491,7 +16537,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 @@ -16501,17 +16547,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; } } @@ -17995,13 +18041,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)) { @@ -18012,7 +18058,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 { @@ -18030,11 +18076,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()); @@ -18058,7 +18104,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) @@ -19454,7 +19500,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); @@ -20748,13 +20793,13 @@ void Player::SendRemoveControlBar() const GetSession()->SendPacket(&data); } -bool Player::IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell) const +bool Player::IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell) { if (!mod || !spellInfo) return false; - // Mod out of charges - if (spell && mod->charges == -1 && spell->m_appliedMods.find(mod->ownerAura) == spell->m_appliedMods.end()) + // First time this aura applies a mod to us and is out of charges + if (spell && mod->ownerAura->IsUsingCharges() && !mod->ownerAura->GetCharges() && !spell->m_appliedMods.count(mod->ownerAura)) return false; // +duration to infinite duration spells making them limited @@ -20769,62 +20814,57 @@ 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 (int 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 (SpellModList::iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr) + modMask[i] = uint32(1) << eff; + if ((mod->mask & modMask)) { - if ((*itr)->type == mod->type && (*itr)->mask & _mask) - val += (*itr)->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) - m_spellMods[mod->op].push_back(mod); + m_spellMods[mod->op].insert(mod); else - { - m_spellMods[mod->op].remove(mod); - // mods bound to aura will be removed in AuraEffect::~AuraEffect - if (!mod->ownerAura) - delete mod; - } + m_spellMods[mod->op].erase(mod); } // Restore spellmods in case of failed cast -void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId, Aura* aura) +void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId /*= 0*/, Aura* aura /*= nullptr*/) { if (!spell || spell->m_appliedMods.empty()) return; std::list<Aura*> aurasQueue; - - for (uint8 i=0; i<MAX_SPELLMOD; ++i) + for (uint8 i = 0; i < MAX_SPELLMOD; ++i) { - for (SpellModList::iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end(); ++itr) + for (auto itr = m_spellMods[i].begin(); itr != m_spellMods[i].end(); ++itr) { SpellModifier* mod = *itr; - // Spellmods without aura set cannot be charged - if (!mod->ownerAura || !mod->ownerAura->IsUsingCharges()) + // Spellmods without charged aura cannot be charged + if (!mod->ownerAura->IsUsingCharges()) continue; // Restore only specific owner aura mods - if (ownerAuraId && (ownerAuraId != mod->ownerAura->GetSpellInfo()->Id)) + if (ownerAuraId && mod->spellId != ownerAuraId) continue; if (aura && mod->ownerAura != aura) @@ -20845,84 +20885,33 @@ void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId, Aura* aura) // only see the first of its modifier restored) aurasQueue.push_back(mod->ownerAura); - // add mod charges back to mod - if (mod->charges == -1) - mod->charges = 1; - else - mod->charges++; - - // Do not set more spellmods than available - if (mod->ownerAura->GetCharges() < mod->charges) - mod->charges = mod->ownerAura->GetCharges(); - - // Skip this check for now - aura charges may change due to various reason - /// @todo track these changes correctly - //ASSERT (mod->ownerAura->GetCharges() <= mod->charges); + // add charges back to aura + mod->ownerAura->ModCharges(1); } } - for (std::list<Aura*>::iterator itr = aurasQueue.begin(); itr != aurasQueue.end(); ++itr) - { - Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(*itr); - if (iterMod != spell->m_appliedMods.end()) - spell->m_appliedMods.erase(iterMod); - } + for (Aura* aura : aurasQueue) + spell->m_appliedMods.erase(aura); } -void Player::RestoreAllSpellMods(uint32 ownerAuraId, Aura* aura) +void Player::RestoreAllSpellMods(uint32 ownerAuraId /*= 0*/, Aura* aura /*= nullptr*/) { for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i) - if (m_currentSpells[i]) - RestoreSpellMods(m_currentSpells[i], ownerAuraId, aura); + if (Spell* spell = m_currentSpells[i]) + RestoreSpellMods(spell, ownerAuraId, aura); } -void Player::RemoveSpellMods(Spell* spell) +void Player::ApplyModToSpell(SpellModifier* mod, Spell* spell) { if (!spell) return; - if (spell->m_appliedMods.empty()) + // don't do anything with no charges + if (mod->ownerAura->IsUsingCharges() && !mod->ownerAura->GetCharges()) return; - for (uint8 i=0; i<MAX_SPELLMOD; ++i) - { - for (SpellModList::const_iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end();) - { - SpellModifier* mod = *itr; - ++itr; - - // spellmods without aura set cannot be charged - if (!mod->ownerAura || !mod->ownerAura->IsUsingCharges()) - continue; - - // check if mod affected this spell - Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(mod->ownerAura); - if (iterMod == spell->m_appliedMods.end()) - continue; - - // remove from list - spell->m_appliedMods.erase(iterMod); - - if (mod->ownerAura->DropCharge(AURA_REMOVE_BY_EXPIRE)) - itr = m_spellMods[i].begin(); - } - } -} - -void Player::DropModCharge(SpellModifier* mod, Spell* spell) -{ - // don't handle spells with proc_event entry defined - // this is a temporary workaround, because all spellmods should be handled like that - if (sSpellMgr->GetSpellProcEvent(mod->spellId)) - return; - - if (spell && mod->ownerAura && mod->charges > 0) - { - if (--mod->charges == 0) - mod->charges = -1; - - spell->m_appliedMods.insert(mod->ownerAura); - } + // register inside spell, proc system uses this to drop charges + spell->m_appliedMods.insert(mod->ownerAura); } void Player::SetSpellModTakingSpell(Spell* spell, bool apply) @@ -20933,7 +20922,7 @@ void Player::SetSpellModTakingSpell(Spell* spell, bool apply) if (apply && spell->getState() == SPELL_STATE_FINISHED) return; - m_spellModTakingSpell = apply ? spell : NULL; + m_spellModTakingSpell = apply ? spell : nullptr; } // send Proficiency @@ -21072,7 +21061,7 @@ void Player::SetRestBonus(float rest_bonus_new) SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, uint32(m_rest_bonus)); } -bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc /*= NULL*/, uint32 spellid /*= 0*/) +bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc /*= nullptr*/, uint32 spellid /*= 0*/) { if (nodes.size() < 2) return false; @@ -21142,11 +21131,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc // check node starting pos data set case if provided if (node->x != 0.0f || node->y != 0.0f || node->z != 0.0f) { - if (node->map_id != GetMapId() || - (node->x - GetPositionX())*(node->x - GetPositionX())+ - (node->y - GetPositionY())*(node->y - GetPositionY())+ - (node->z - GetPositionZ())*(node->z - GetPositionZ()) > - (2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE)) + if (node->map_id != GetMapId() || !IsInDist(node->x, node->y, node->z, 2 * INTERACTION_DISTANCE)) { GetSession()->SendActivateTaxiReply(ERR_TAXITOOFARAWAY); return false; @@ -21307,10 +21292,7 @@ void Player::ContinueTaxiFlight() const TaxiPathNodeList const& nodeList = sTaxiPathNodesByPath[path]; float distPrev; - float distNext = - (nodeList[0]->LocX - GetPositionX())*(nodeList[0]->LocX - GetPositionX()) + - (nodeList[0]->LocY - GetPositionY())*(nodeList[0]->LocY - GetPositionY()) + - (nodeList[0]->LocZ - GetPositionZ())*(nodeList[0]->LocZ - GetPositionZ()); + float distNext = GetExactDistSq(nodeList[0]->LocX, nodeList[0]->LocY, nodeList[0]->LocZ); for (uint32 i = 1; i < nodeList.size(); ++i) { @@ -21323,10 +21305,7 @@ void Player::ContinueTaxiFlight() const distPrev = distNext; - distNext = - (node->LocX - GetPositionX())*(node->LocX - GetPositionX()) + - (node->LocY - GetPositionY())*(node->LocY - GetPositionY()) + - (node->LocZ - GetPositionZ())*(node->LocZ - GetPositionZ()); + distNext = GetExactDistSq(node->LocX, node->LocY, node->LocZ); float distNodes = (node->LocX - prevNode->LocX)*(node->LocX - prevNode->LocX) + @@ -21465,9 +21444,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); @@ -21504,7 +21483,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); @@ -22692,9 +22671,10 @@ 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(); for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { _Spell const& spellData = pItem->GetTemplate()->Spells[i]; @@ -22703,6 +22683,18 @@ void Player::ApplyEquipCooldown(Item* pItem) if (spellData.SpellId <= 0) continue; + // apply proc cooldown to equip auras if we have any + if (spellData.SpellTrigger == ITEM_SPELLTRIGGER_ON_EQUIP) + { + SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(spellData.SpellId); + if (!procEntry) + continue; + + if (Aura* itemAura = GetAura(spellData.SpellId, GetGUID(), pItem->GetGUID())) + itemAura->AddProcCooldown(now + procEntry->Cooldown); + continue; + } + // wrong triggering type (note: ITEM_SPELLTRIGGER_ON_NO_DELAY_USE not have cooldown) if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE) continue; @@ -23532,9 +23524,9 @@ void Player::RemoveItemDependentAurasAndCasts(Item* pItem) { Aura* aura = itr->second; - // skip passive (passive item dependent spells work in another way) and not self applied auras + // skip not self applied auras SpellInfo const* spellInfo = aura->GetSpellInfo(); - if (aura->IsPassive() || aura->GetCasterGUID() != GetGUID()) + if (aura->GetCasterGUID() != GetGUID()) { ++itr; continue; @@ -24579,9 +24571,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); @@ -24847,7 +24839,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)) @@ -24867,7 +24859,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; } @@ -25831,6 +25825,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();*/ @@ -25948,6 +25956,13 @@ void Player::ActivateSpec(uint8 spec) SetPower(POWER_MANA, 0); // Mana must be 0 even if it isn't the active power type. SetPower(pw, 0); + + Unit::AuraEffectList const& shapeshiftAuras = GetAuraEffectsByType(SPELL_AURA_MOD_SHAPESHIFT); + for (AuraEffect* aurEff : shapeshiftAuras) + { + aurEff->HandleShapeshiftBoosts(this, false); + aurEff->HandleShapeshiftBoosts(this, true); + } } void Player::ResetTimeSync() @@ -26007,7 +26022,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; @@ -26067,7 +26082,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 5a572d9caa9..ed5dc7e954f 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -66,10 +66,10 @@ typedef std::deque<Mail*> PlayerMails; #define PLAYER_EXPLORED_ZONES_SIZE 128 // Note: SPELLMOD_* values is aura types in fact -enum SpellModType +enum SpellModType : uint8 { - SPELLMOD_FLAT = 107, // SPELL_AURA_ADD_FLAT_MODIFIER - SPELLMOD_PCT = 108 // SPELL_AURA_ADD_PCT_MODIFIER + SPELLMOD_FLAT = SPELL_AURA_ADD_FLAT_MODIFIER, + SPELLMOD_PCT = SPELL_AURA_ADD_PCT_MODIFIER }; // 2^n values, Player::m_isunderwater is a bitmask. These are Trinity internal values, they are never send to any client @@ -118,10 +118,11 @@ struct PlayerTalent // Spell modifier (used for modify other spells) struct SpellModifier { - SpellModifier(Aura* _ownerAura = nullptr) : op(SPELLMOD_DAMAGE), type(SPELLMOD_FLAT), charges(0), value(0), mask(), spellId(0), ownerAura(_ownerAura) { } - SpellModOp op : 8; - SpellModType type : 8; - int16 charges : 16; + SpellModifier(Aura* _ownerAura) : op(SPELLMOD_DAMAGE), type(SPELLMOD_FLAT), value(0), mask(), spellId(0), ownerAura(_ownerAura) { } + + SpellModOp op; + SpellModType type; + int32 value; flag96 mask; uint32 spellId; @@ -130,7 +131,7 @@ struct SpellModifier typedef std::unordered_map<uint32, PlayerTalent*> PlayerTalentMap; typedef std::unordered_map<uint32, PlayerSpell*> PlayerSpellMap; -typedef std::list<SpellModifier*> SpellModList; +typedef std::unordered_set<SpellModifier*> SpellModContainer; typedef std::unordered_map<uint32 /*instanceId*/, time_t/*releaseTime*/> InstanceTimeMap; @@ -1198,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; @@ -1268,6 +1270,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> uint32 GetArmorProficiency() const { return m_ArmorProficiency; } bool IsUseEquipedWeapon(bool mainhand) const; bool IsTwoHandUsed() const; + bool IsUsingTwoHandedWeaponInOneHand() const; void SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast = false); bool BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot); bool _StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot, int32 price, ItemTemplate const* pProto, Creature* pVendor, VendorItem const* crItem, bool bStore); @@ -1339,6 +1342,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void CompleteQuest(uint32 quest_id); void IncompleteQuest(uint32 quest_id); void RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, bool announce = true); + void SetRewardedQuest(uint32 quest_id); void FailQuest(uint32 quest_id); bool SatisfyQuestSkill(Quest const* qInfo, bool msg) const; bool SatisfyQuestLevel(Quest const* qInfo, bool msg) const; @@ -1402,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; @@ -1600,13 +1604,12 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> PlayerSpellMap & GetSpellMap() { return m_spells; } void AddSpellMod(SpellModifier* mod, bool apply); - bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr) const; - template <class T> - void ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell = nullptr); - void RemoveSpellMods(Spell* spell); + static bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr); + template <SpellModOp op, class T> + void ApplySpellMod(uint32 spellId, T& basevalue, Spell* spell = nullptr) const; void RestoreSpellMods(Spell* spell, uint32 ownerAuraId = 0, Aura* aura = nullptr); void RestoreAllSpellMods(uint32 ownerAuraId = 0, Aura* aura = nullptr); - void DropModCharge(SpellModifier* mod, Spell* spell); + static void ApplyModToSpell(SpellModifier* mod, Spell* spell); void SetSpellModTakingSpell(Spell* spell, bool apply); void RemoveArenaSpellCooldowns(bool removeActivePetCooldowns = false); @@ -1739,7 +1742,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void RecalculateRating(CombatRating cr) { ApplyRatingMod(cr, 0, true);} float GetMeleeCritFromAgility() const; void GetDodgeFromAgility(float &diminishing, float &nondiminishing) const; - float GetMissPercentageFromDefence() const; + float GetMissPercentageFromDefense() const; float GetSpellCritFromIntellect() const; float OCTRegenHPPerSpirit() const; float OCTRegenMPPerSpirit() const; @@ -1845,9 +1848,12 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void UpdateLocalChannels(uint32 newZone); void LeaveLFGChannel(); + typedef std::list<Channel*> JoinedChannelsList; + JoinedChannelsList const& GetJoinedChannels() const { return m_channels; } + void UpdateDefense(); void UpdateWeaponSkill (WeaponAttackType attType); - void UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool defence); + void UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool defense); void SetSkill(uint16 id, uint16 step, uint16 newVal, uint16 maxVal); uint16 GetMaxSkillValue(uint32 skill) const; // max + perm. bonus + temp bonus @@ -1937,7 +1943,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool CanBlock() const { return m_canBlock; } void SetCanBlock(bool value); bool CanTitanGrip() const { return m_canTitanGrip; } - void SetCanTitanGrip(bool value) { m_canTitanGrip = value; } + void SetCanTitanGrip(bool value, uint32 penaltySpellId = 0); + void CheckTitanGripPenalty(); bool CanTameExoticPets() const { return IsGameMaster() || HasAuraType(SPELL_AURA_ALLOW_TAME_PET_TYPE); } void SetRegularAttackTime(); @@ -1951,9 +1958,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void ResetAllPowers(); - void _ApplyWeaponDependentAuraMods(Item* item, WeaponAttackType attackType, bool apply); - void _ApplyWeaponDependentAuraCritMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply); - void _ApplyWeaponDependentAuraDamageMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply); + void ApplyItemDependentAuras(Item* item, bool apply); void _ApplyItemMods(Item* item, uint8 slot, bool apply); void _RemoveAllItemMods(); @@ -1970,9 +1975,9 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void ApplyItemEquipSpell(Item* item, bool apply, bool form_change = false); void ApplyEquipSpell(SpellInfo const* spellInfo, Item* item, bool apply, bool form_change = false); void UpdateEquipSpellsAtFormChange(); - void CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx); + void CastItemCombatSpell(DamageInfo const& damageInfo); + void CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemTemplate const* proto); void CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 cast_count, uint32 glyphIndex); - void CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx, Item* item, ItemTemplate const* proto); void SendEquipmentSetList(); void SetEquipmentSet(uint32 index, EquipmentSet eqset); @@ -2438,7 +2443,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> uint32 m_baseHealthRegen; int32 m_spellPenetrationItemMod; - SpellModList m_spellMods[MAX_SPELLMOD]; + SpellModContainer m_spellMods[MAX_SPELLMOD]; EnchantDurationList m_enchantDuration; ItemDurationList m_itemDuration; @@ -2451,7 +2456,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> WorldSession* m_session; - typedef std::list<Channel*> JoinedChannelsList; JoinedChannelsList m_channels; uint8 m_cinematic; @@ -2479,6 +2483,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool m_canParry; bool m_canBlock; bool m_canTitanGrip; + uint32 m_titanGripPenaltySpellId; uint8 m_swingErrorMsg; float m_ammoDPS; @@ -2610,8 +2615,8 @@ TC_GAME_API void AddItemsSetItem(Player* player, Item* item); TC_GAME_API void RemoveItemsSetItem(Player* player, ItemTemplate const* proto); // "the bodies of template functions must be made available in a header file" -template <class T> -void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell /*= nullptr*/) +template <SpellModOp op, class T> +void Player::ApplySpellMod(uint32 spellId, T& basevalue, Spell* spell /*= nullptr*/) const { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) @@ -2624,33 +2629,89 @@ void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* s if (m_spellModTakingSpell) spell = m_spellModTakingSpell; - for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr) + switch (op) { - SpellModifier* mod = *itr; + // special case, if a mod makes spell instant, only consume that mod + case SPELLMOD_CASTING_TIME: + { + SpellModifier* modInstantSpell = nullptr; + for (SpellModifier* mod : m_spellMods[SPELLMOD_CASTING_TIME]) + { + if (!IsAffectedBySpellmod(spellInfo, mod, spell)) + continue; + + if (mod->type == SPELLMOD_PCT && basevalue < T(10000) && mod->value <= -100) + { + modInstantSpell = mod; + break; + } + } + + if (modInstantSpell) + { + Player::ApplyModToSpell(modInstantSpell, spell); + basevalue = T(0); + return; + } + break; + } + // special case if two mods apply 100% critical chance, only consume one + case SPELLMOD_CRITICAL_CHANCE: + { + SpellModifier* modCritical = nullptr; + for (SpellModifier* mod : m_spellMods[SPELLMOD_CRITICAL_CHANCE]) + { + if (!IsAffectedBySpellmod(spellInfo, mod, spell)) + continue; + + if (mod->type == SPELLMOD_FLAT && mod->value >= 100) + { + modCritical = mod; + break; + } + } - // Charges can be set only for mods with auras - if (!mod->ownerAura) - ASSERT(mod->charges == 0); + if (modCritical) + { + Player::ApplyModToSpell(modCritical, spell); + basevalue = T(100); + return; + } + break; + } + default: + break; + } + for (SpellModifier* mod : m_spellMods[op]) + { if (!IsAffectedBySpellmod(spellInfo, mod, spell)) continue; - if (mod->type == SPELLMOD_FLAT) - totalflat += mod->value; - else if (mod->type == SPELLMOD_PCT) + switch (mod->type) { - // skip percent mods for null basevalue (most important for spell mods with charges) - if (basevalue == T(0)) - continue; - - // special case (skip > 10sec spell casts for instant cast setting) - if (mod->op == SPELLMOD_CASTING_TIME && basevalue >= T(10000) && mod->value <= -100) - continue; - - totalmul += CalculatePct(1.0f, mod->value); + case SPELLMOD_FLAT: + totalflat += mod->value; + break; + case SPELLMOD_PCT: + { + // skip percent mods for null basevalue (most important for spell mods with charges) + if (basevalue == T(0)) + continue; + + // special case (skip > 10sec spell casts for instant cast setting) + if (op == SPELLMOD_CASTING_TIME) + { + if (mod->value <= -100 && basevalue >= T(10000)) + continue; + } + + totalmul += CalculatePct(1.0f, mod->value); + break; + } } - DropModCharge(mod, spell); + Player::ApplyModToSpell(mod, spell); } basevalue = T(float(basevalue + totalflat) * totalmul); 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/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index d1984da9648..0cad0b2cb05 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -184,8 +184,17 @@ void Player::UpdateSpellDamageAndHealingBonus() // Get healing bonus for all schools SetStatInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, SpellBaseHealingBonusDone(SPELL_SCHOOL_MASK_ALL)); // Get damage bonus for all schools - for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) - SetStatInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, SpellBaseDamageBonusDone(SpellSchoolMask(1 << i))); + Unit::AuraEffectList const& modDamageAuras = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE); + for (uint16 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) + { + SetInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + i, std::accumulate(modDamageAuras.begin(), modDamageAuras.end(), 0, [i](int32 negativeMod, AuraEffect const* aurEff) + { + if (aurEff->GetAmount() < 0 && aurEff->GetMiscValue() & (1 << i)) + negativeMod += aurEff->GetAmount(); + return negativeMod; + })); + SetStatInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, SpellBaseDamageBonusDone(SpellSchoolMask(1 << i)) - GetInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + i)); + } } bool Player::UpdateAllStats() @@ -530,9 +539,9 @@ void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bo break; } - float attackSpeedMod = GetAPMultiplier(attType, normalized); + float const attackPowerMod = std::max(GetAPMultiplier(attType, normalized), 0.25f); - float baseValue = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType) / 14.0f * attackSpeedMod; + float baseValue = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType) / 14.0f * attackPowerMod; float basePct = GetModifierValue(unitMod, BASE_PCT); float totalValue = GetModifierValue(unitMod, TOTAL_VALUE); float totalPct = addTotalPct ? GetModifierValue(unitMod, TOTAL_PCT) : 1.0f; @@ -546,8 +555,8 @@ void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bo if (lvl > 60) lvl = 60; - weaponMinDamage = lvl * 0.85f * attackSpeedMod; - weaponMaxDamage = lvl * 1.25f * attackSpeedMod; + weaponMinDamage = lvl * 0.85f * attackPowerMod; + weaponMaxDamage = lvl * 1.25f * attackPowerMod; } else if (!CanUseAttackType(attType)) // check if player not in form but still can't use (disarm case) { @@ -563,8 +572,8 @@ void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bo } else if (attType == RANGED_ATTACK) // add ammo DPS to ranged damage { - weaponMinDamage += GetAmmoDPS() * attackSpeedMod; - weaponMaxDamage += GetAmmoDPS() * attackSpeedMod; + weaponMinDamage += GetAmmoDPS() * attackPowerMod; + weaponMaxDamage += GetAmmoDPS() * attackPowerMod; } minDamage = ((weaponMinDamage + baseValue) * basePct + totalValue) * totalPct; @@ -666,7 +675,7 @@ const float m_diminishing_k[MAX_CLASSES] = 0.9720f // Druid }; -float Player::GetMissPercentageFromDefence() const +float Player::GetMissPercentageFromDefense() const { float const miss_cap[MAX_CLASSES] = { diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 4581037d87a..7ff01efa363 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -92,37 +92,80 @@ float playerBaseMoveSpeed[MAX_MOVE_TYPE] = 3.14f // MOVE_PITCH_RATE }; -// Used for prepare can/can`t triggr aura -static bool InitTriggerAuraData(); -// Define can trigger auras -static bool isTriggerAura[TOTAL_AURAS]; -// Define can't trigger auras (need for disable second trigger) -static bool isNonTriggerAura[TOTAL_AURAS]; -// Triggered always, even from triggered spells -static bool isAlwaysTriggeredAura[TOTAL_AURAS]; -// Prepare lists -static bool procPrepared = InitTriggerAuraData(); - -DamageInfo::DamageInfo(Unit* _attacker, Unit* _victim, uint32 _damage, SpellInfo const* _spellInfo, SpellSchoolMask _schoolMask, DamageEffectType _damageType) -: m_attacker(_attacker), m_victim(_victim), m_damage(_damage), m_spellInfo(_spellInfo), m_schoolMask(_schoolMask), -m_damageType(_damageType), m_attackType(BASE_ATTACK) -{ - m_absorb = 0; - m_resist = 0; - m_block = 0; -} -DamageInfo::DamageInfo(CalcDamageInfo& dmgInfo) -: m_attacker(dmgInfo.attacker), m_victim(dmgInfo.target), m_damage(dmgInfo.damage), m_spellInfo(NULL), m_schoolMask(SpellSchoolMask(dmgInfo.damageSchoolMask)), -m_damageType(DIRECT_DAMAGE), m_attackType(dmgInfo.attackType) -{ - m_absorb = 0; - m_resist = 0; - m_block = 0; +DamageInfo::DamageInfo(Unit* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, DamageEffectType damageType, WeaponAttackType attackType) + : m_attacker(attacker), m_victim(victim), m_damage(damage), m_spellInfo(spellInfo), m_schoolMask(schoolMask), m_damageType(damageType), m_attackType(attackType), + m_absorb(0), m_resist(0), m_block(0), m_hitMask(0) +{ +} + +DamageInfo::DamageInfo(CalcDamageInfo const& dmgInfo) + : m_attacker(dmgInfo.attacker), m_victim(dmgInfo.target), m_damage(dmgInfo.damage), m_spellInfo(nullptr), m_schoolMask(SpellSchoolMask(dmgInfo.damageSchoolMask)), + m_damageType(DIRECT_DAMAGE), m_attackType(dmgInfo.attackType), m_absorb(dmgInfo.absorb), m_resist(dmgInfo.resist), m_block(dmgInfo.blocked_amount), m_hitMask(0) +{ + switch (dmgInfo.TargetState) + { + case VICTIMSTATE_IS_IMMUNE: + m_hitMask |= PROC_HIT_IMMUNE; + break; + case VICTIMSTATE_BLOCKS: + m_hitMask |= PROC_HIT_FULL_BLOCK; + break; + } + + if (dmgInfo.HitInfo & (HITINFO_PARTIAL_ABSORB | HITINFO_FULL_ABSORB)) + m_hitMask |= PROC_HIT_ABSORB; + + if (dmgInfo.HitInfo & HITINFO_FULL_RESIST) + m_hitMask |= PROC_HIT_FULL_RESIST; + + 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: + m_hitMask |= PROC_HIT_MISS; + break; + case MELEE_HIT_DODGE: + m_hitMask |= PROC_HIT_DODGE; + break; + case MELEE_HIT_PARRY: + m_hitMask |= PROC_HIT_PARRY; + break; + case MELEE_HIT_EVADE: + m_hitMask |= PROC_HIT_EVADE; + break; + case MELEE_HIT_CRUSHING: + case MELEE_HIT_GLANCING: + case MELEE_HIT_NORMAL: + if (!damageNullified) + m_hitMask |= PROC_HIT_NORMAL; + break; + case MELEE_HIT_CRIT: + if (!damageNullified) + m_hitMask |= PROC_HIT_CRITICAL; + break; + default: + break; + } +} + +DamageInfo::DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType, uint32 hitMask) + : m_attacker(spellNonMeleeDamage.attacker), m_victim(spellNonMeleeDamage.target), m_damage(spellNonMeleeDamage.damage), + m_spellInfo(sSpellMgr->GetSpellInfo(spellNonMeleeDamage.SpellID)), m_schoolMask(SpellSchoolMask(spellNonMeleeDamage.schoolMask)), m_damageType(damageType), + m_attackType(attackType), m_absorb(spellNonMeleeDamage.absorb), m_resist(spellNonMeleeDamage.resist), m_block(spellNonMeleeDamage.blocked), m_hitMask(hitMask) +{ + if (spellNonMeleeDamage.blocked) + m_hitMask |= PROC_HIT_BLOCK; + if (spellNonMeleeDamage.absorb) + m_hitMask |= PROC_HIT_ABSORB; } void DamageInfo::ModifyDamage(int32 amount) { - amount = std::min(amount, int32(GetDamage())); + amount = std::max(amount, -static_cast<int32>(GetDamage())); m_damage += amount; } @@ -131,6 +174,7 @@ void DamageInfo::AbsorbDamage(uint32 amount) amount = std::min(amount, GetDamage()); m_absorb += amount; m_damage -= amount; + m_hitMask |= PROC_HIT_ABSORB; } void DamageInfo::ResistDamage(uint32 amount) @@ -138,6 +182,11 @@ void DamageInfo::ResistDamage(uint32 amount) amount = std::min(amount, GetDamage()); 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) @@ -145,6 +194,37 @@ void DamageInfo::BlockDamage(uint32 amount) amount = std::min(amount, GetDamage()); m_block += 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 +{ + return m_hitMask; +} + +HealInfo::HealInfo(Unit* healer, Unit* target, uint32 heal, SpellInfo const* spellInfo, SpellSchoolMask schoolMask) + : _healer(healer), _target(target), _heal(heal), _effectiveHeal(0), _absorb(0), _spellInfo(spellInfo), _schoolMask(schoolMask), _hitMask(0) +{ +} + +void HealInfo::AbsorbHeal(uint32 amount) +{ + amount = std::min(amount, GetHeal()); + _absorb += amount; + _heal -= amount; + amount = std::min(amount, GetEffectiveHeal()); + _effectiveHeal -= amount; + _hitMask |= PROC_HIT_ABSORB; +} + +uint32 HealInfo::GetHitMask() const +{ + return _hitMask; } ProcEventInfo::ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, @@ -224,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; @@ -576,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())) { @@ -897,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) @@ -909,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); } @@ -918,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) @@ -972,10 +1049,10 @@ 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(GameObject* go, uint32 spellId, bool triggered, Item* castItem, AuraEffect* triggeredByAura, ObjectGuid 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) @@ -984,26 +1061,23 @@ void Unit::CastSpell(GameObject* go, uint32 spellId, bool triggered, Item* castI return; } SpellCastTargets targets; - targets.SetGOTarget(go); + targets.SetDst(x, y, z, GetOrientation()); - CastSpell(targets, spellInfo, NULL, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster); + CastSpell(targets, spellInfo, nullptr, triggerFlags, castItem, triggeredByAura, originalCaster); } -// Obsolete func need remove, here only for comotability vs another patches -uint32 Unit::SpellNonMeleeDamageLog(Unit* victim, uint32 spellID, uint32 damage) +void Unit::CastSpell(GameObject* go, uint32 spellId, bool triggered, Item* castItem, AuraEffect* triggeredByAura, ObjectGuid originalCaster) { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) - return 0; - SpellNonMeleeDamage damageInfo(this, victim, spellInfo->Id, spellInfo->SchoolMask); - damage = SpellDamageBonusDone(victim, spellInfo, damage, SPELL_DIRECT_DAMAGE); - damage = victim->SpellDamageBonusTaken(this, spellInfo, damage, SPELL_DIRECT_DAMAGE); + { + 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.SetGOTarget(go); - CalculateSpellDamageTaken(&damageInfo, damage, spellInfo); - DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); - SendSpellNonMeleeDamageLog(&damageInfo); - DealSpellDamage(&damageInfo, true); - return damageInfo.damage; + CastSpell(targets, spellInfo, nullptr, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster); } void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType, bool crit) @@ -1047,7 +1121,7 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama uint32 crit_bonus = damage; // Apply crit_damage bonus for melee spells if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus); + modOwner->ApplySpellMod<SPELLMOD_CRIT_DAMAGE_BONUS>(spellInfo->Id, crit_bonus); damage += crit_bonus; // Apply SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE or SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE @@ -1074,18 +1148,21 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama // double blocked amount if block is critical if (victim->isBlockCritical()) damageInfo->blocked += damageInfo->blocked; - if (damage < int32(damageInfo->blocked)) + if (damage <= int32(damageInfo->blocked)) + { damageInfo->blocked = uint32(damage); + damageInfo->fullBlock = true; + } damage -= damageInfo->blocked; } if (attackType != RANGED_ATTACK) - ApplyResilience(victim, NULL, &damage, crit, CR_CRIT_TAKEN_MELEE); + ApplyResilience(victim, nullptr, &damage, crit, CR_CRIT_TAKEN_MELEE); else - ApplyResilience(victim, NULL, &damage, crit, CR_CRIT_TAKEN_RANGED); + ApplyResilience(victim, nullptr, &damage, crit, CR_CRIT_TAKEN_RANGED); break; } - // Magical Attacks + // Magical Attacks case SPELL_DAMAGE_CLASS_NONE: case SPELL_DAMAGE_CLASS_MAGIC: { @@ -1096,7 +1173,7 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama damage = SpellCriticalDamageBonus(spellInfo, damage, victim); } - ApplyResilience(victim, NULL, &damage, crit, CR_CRIT_TAKEN_SPELL); + ApplyResilience(victim, nullptr, &damage, crit, CR_CRIT_TAKEN_SPELL); break; } default: @@ -1108,24 +1185,30 @@ 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->absorb = dmgInfo.GetAbsorb(); + damageInfo->resist = dmgInfo.GetResist(); + + if (damageInfo->absorb) + damageInfo->HitInfo |= (damageInfo->damage - damageInfo->absorb == 0 ? HITINFO_FULL_ABSORB : HITINFO_PARTIAL_ABSORB); + + if (damageInfo->resist) + damageInfo->HitInfo |= (damageInfo->damage - damageInfo->resist == 0 ? HITINFO_FULL_RESIST : HITINFO_PARTIAL_RESIST); + + damageInfo->damage = dmgInfo.GetDamage(); } void Unit::DealSpellDamage(SpellNonMeleeDamage* damageInfo, bool durabilityLoss) { - if (damageInfo == 0) + if (!damageInfo) return; Unit* victim = damageInfo->target; - if (!victim) return; @@ -1161,7 +1244,6 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam damageInfo->HitInfo = 0; damageInfo->procAttacker = PROC_FLAG_NONE; damageInfo->procVictim = PROC_FLAG_NONE; - damageInfo->procEx = PROC_EX_NONE; damageInfo->hitOutCome = MELEE_HIT_EVADE; if (!victim) @@ -1192,7 +1274,6 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam damageInfo->HitInfo |= HITINFO_NORMALSWING; damageInfo->TargetState = VICTIMSTATE_IS_IMMUNE; - damageInfo->procEx |= PROC_EX_IMMUNE; damageInfo->damage = 0; damageInfo->cleanDamage = 0; return; @@ -1222,27 +1303,23 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam case MELEE_HIT_EVADE: damageInfo->HitInfo |= HITINFO_MISS | HITINFO_SWINGNOHITSOUND; damageInfo->TargetState = VICTIMSTATE_EVADES; - damageInfo->procEx |= PROC_EX_EVADE; damageInfo->damage = 0; damageInfo->cleanDamage = 0; return; case MELEE_HIT_MISS: damageInfo->HitInfo |= HITINFO_MISS; damageInfo->TargetState = VICTIMSTATE_INTACT; - damageInfo->procEx |= PROC_EX_MISS; damageInfo->damage = 0; damageInfo->cleanDamage = 0; break; case MELEE_HIT_NORMAL: damageInfo->TargetState = VICTIMSTATE_HIT; - damageInfo->procEx |= PROC_EX_NORMAL_HIT; break; case MELEE_HIT_CRIT: { damageInfo->HitInfo |= HITINFO_CRITICALHIT; damageInfo->TargetState = VICTIMSTATE_HIT; - damageInfo->procEx |= PROC_EX_CRITICAL_HIT; // Crit bonus calc damageInfo->damage += damageInfo->damage; float mod = 0.0f; @@ -1265,32 +1342,27 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam } case MELEE_HIT_PARRY: damageInfo->TargetState = VICTIMSTATE_PARRY; - damageInfo->procEx |= PROC_EX_PARRY; damageInfo->cleanDamage += damageInfo->damage; damageInfo->damage = 0; break; case MELEE_HIT_DODGE: damageInfo->TargetState = VICTIMSTATE_DODGE; - damageInfo->procEx |= PROC_EX_DODGE; damageInfo->cleanDamage += damageInfo->damage; damageInfo->damage = 0; break; case MELEE_HIT_BLOCK: damageInfo->TargetState = VICTIMSTATE_HIT; damageInfo->HitInfo |= HITINFO_BLOCK; - damageInfo->procEx |= PROC_EX_BLOCK; damageInfo->blocked_amount = damageInfo->target->GetShieldBlockValue(); // double blocked amount if block is critical if (damageInfo->target->isBlockCritical()) - damageInfo->blocked_amount+=damageInfo->blocked_amount; + damageInfo->blocked_amount += damageInfo->blocked_amount; if (damageInfo->blocked_amount >= damageInfo->damage) { damageInfo->TargetState = VICTIMSTATE_BLOCKS; damageInfo->blocked_amount = damageInfo->damage; - damageInfo->procEx |= PROC_EX_FULL_BLOCK; } - else - damageInfo->procEx |= PROC_EX_NORMAL_HIT; + damageInfo->damage -= damageInfo->blocked_amount; damageInfo->cleanDamage += damageInfo->blocked_amount; break; @@ -1298,11 +1370,15 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam { damageInfo->HitInfo |= HITINFO_GLANCING; damageInfo->TargetState = VICTIMSTATE_HIT; - damageInfo->procEx |= PROC_EX_NORMAL_HIT; int32 leveldif = int32(victim->getLevel()) - int32(getLevel()); if (leveldif > 3) leveldif = 3; - float reducePercent = 1 - leveldif * 0.1f; + + // against boss-level targets - 24% chance of 25% average damage reduction (damage reduction range : 20-30%) + // against level 82 elites - 18% chance of 15% average damage reduction (damage reduction range : 10-20%) + int32 const reductionMax = leveldif * 10; + int32 const reductionMin = reductionMax - 10; + float reducePercent = 1.f - irand(reductionMin, reductionMax) / 100.0f; damageInfo->cleanDamage += damageInfo->damage - uint32(reducePercent * damageInfo->damage); damageInfo->damage = uint32(reducePercent * damageInfo->damage); break; @@ -1310,7 +1386,6 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam case MELEE_HIT_CRUSHING: damageInfo->HitInfo |= HITINFO_CRUSHING; damageInfo->TargetState = VICTIMSTATE_HIT; - damageInfo->procEx |= PROC_EX_NORMAL_HIT; // 150% normal damage damageInfo->damage += (damageInfo->damage / 2); break; @@ -1334,18 +1409,18 @@ 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); - damageInfo->procEx |= PROC_EX_ABSORB; - } 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; @@ -1430,7 +1505,10 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) } if (GetTypeId() == TYPEID_PLAYER) - ToPlayer()->CastItemCombatSpell(victim, damageInfo->attackType, damageInfo->procVictim, damageInfo->procEx); + { + DamageInfo dmgInfo(*damageInfo); + ToPlayer()->CastItemCombatSpell(dmgInfo); + } // Do effect if any damage done to target if (damageInfo->damage) @@ -1438,46 +1516,47 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) // 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 vDamageShieldsCopy(victim->GetAuraEffectsByType(SPELL_AURA_DAMAGE_SHIELD)); - for (AuraEffectList::const_iterator dmgShieldItr = vDamageShieldsCopy.begin(); dmgShieldItr != vDamageShieldsCopy.end(); ++dmgShieldItr) + for (AuraEffect const* aurEff : vDamageShieldsCopy) { - SpellInfo const* i_spellProto = (*dmgShieldItr)->GetSpellInfo(); + SpellInfo const* spellInfo = aurEff->GetSpellInfo(); + // Damage shield can be resisted... - if (SpellMissInfo missInfo = victim->SpellHitResult(this, i_spellProto, false)) + SpellMissInfo missInfo = victim->SpellHitResult(this, spellInfo, false); + if (missInfo != SPELL_MISS_NONE) { - victim->SendSpellMiss(this, i_spellProto->Id, missInfo); + victim->SendSpellMiss(this, spellInfo->Id, missInfo); continue; } // ...or immuned - if (IsImmunedToDamage(i_spellProto)) + if (IsImmunedToDamage(spellInfo)) { - victim->SendSpellDamageImmune(this, i_spellProto->Id); + victim->SendSpellDamageImmune(this, spellInfo->Id); continue; } - uint32 damage = (*dmgShieldItr)->GetAmount(); - - if (Unit* caster = (*dmgShieldItr)->GetCaster()) + uint32 damage = aurEff->GetAmount(); + if (Unit* caster = aurEff->GetCaster()) { - damage = caster->SpellDamageBonusDone(this, i_spellProto, damage, SPELL_DIRECT_DAMAGE); - damage = this->SpellDamageBonusTaken(caster, i_spellProto, damage, SPELL_DIRECT_DAMAGE); + damage = caster->SpellDamageBonusDone(this, spellInfo, damage, SPELL_DIRECT_DAMAGE); + damage = SpellDamageBonusTaken(caster, spellInfo, damage, SPELL_DIRECT_DAMAGE); } // No Unit::CalcAbsorbResist here - opcode doesn't send that data - this damage is probably not affected by that - victim->DealDamageMods(this, damage, NULL); + victim->DealDamageMods(this, damage, nullptr); /// @todo Move this to a packet handler WorldPacket data(SMSG_SPELLDAMAGESHIELD, 8 + 8 + 4 + 4 + 4 + 4 + 4); data << uint64(victim->GetGUID()); data << uint64(GetGUID()); - data << uint32(i_spellProto->Id); + data << uint32(spellInfo->Id); data << uint32(damage); // Damage - int32 overkill = int32(damage) - int32(GetHealth()); - data << uint32(overkill > 0 ? overkill : 0); // Overkill - data << uint32(i_spellProto->SchoolMask); + int32 const overkill = int32(damage) - int32(GetHealth()); + data << uint32(std::max(overkill, 0)); // Overkill + data << uint32(spellInfo->SchoolMask); victim->SendMessageToSet(&data, true); - victim->DealDamage(this, damage, 0, SPELL_DIRECT_DAMAGE, i_spellProto->GetSchoolMask(), i_spellProto, true); + victim->DealDamage(this, damage, nullptr, SPELL_DIRECT_DAMAGE, spellInfo->GetSchoolMask(), spellInfo, true); } } } @@ -1522,7 +1601,7 @@ uint32 Unit::CalcArmorReducedDamage(Unit* victim, const uint32 damage, SpellInfo if (spellInfo) if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_IGNORE_ARMOR, armor); + modOwner->ApplySpellMod<SPELLMOD_IGNORE_ARMOR>(spellInfo->Id, armor); AuraEffectList const& resIgnoreAurasAb = GetAuraEffectsByType(SPELL_AURA_MOD_ABILITY_IGNORE_TARGET_RESIST); for (AuraEffectList::const_iterator j = resIgnoreAurasAb.begin(); j != resIgnoreAurasAb.end(); ++j) @@ -1669,57 +1748,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); - - 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 @@ -1732,19 +1803,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) @@ -1758,16 +1829,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 @@ -1780,14 +1851,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; @@ -1795,15 +1866,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) @@ -1814,39 +1885,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; } @@ -1854,78 +1925,77 @@ 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 - caster->ProcDamageAndSpellFor(true, this, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_EX_NORMAL_HIT, BASE_ATTACK, (*itr)->GetSpellInfo(), splitDamage); + 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(Unit* victim, SpellInfo const* healSpell, uint32 &healAmount, uint32 &absorb) +void Unit::CalcHealAbsorb(HealInfo& healInfo) const { - if (!healAmount) + if (!healInfo.GetHeal()) return; - int32 RemainingHeal = healAmount; + int32 const healing = static_cast<int32>(healInfo.GetHeal()); + int32 absorbAmount = 0; // Need remove expired auras after bool existExpired = false; // absorb without mana cost - AuraEffectList const& vHealAbsorb = victim->GetAuraEffectsByType(SPELL_AURA_SCHOOL_HEAL_ABSORB); - for (AuraEffectList::const_iterator i = vHealAbsorb.begin(); i != vHealAbsorb.end() && RemainingHeal > 0; ++i) + AuraEffectList const& vHealAbsorb = healInfo.GetTarget()->GetAuraEffectsByType(SPELL_AURA_SCHOOL_HEAL_ABSORB); + for (AuraEffectList::const_iterator i = vHealAbsorb.begin(); i != vHealAbsorb.end() && absorbAmount <= healing; ++i) { - if (!((*i)->GetMiscValue() & healSpell->SchoolMask)) + if (!((*i)->GetMiscValue() & healInfo.GetSpellInfo()->SchoolMask)) continue; // Max Amount can be absorbed by this aura @@ -1940,10 +2010,10 @@ void Unit::CalcHealAbsorb(Unit* victim, SpellInfo const* healSpell, uint32 &heal // currentAbsorb - damage can be absorbed by shield // If need absorb less damage - if (RemainingHeal < currentAbsorb) - currentAbsorb = RemainingHeal; + if (healing < currentAbsorb + absorbAmount) + currentAbsorb = healing - absorbAmount; - RemainingHeal -= currentAbsorb; + absorbAmount += currentAbsorb; // Reduce shield amount (*i)->SetAmount((*i)->GetAmount() - currentAbsorb); @@ -1961,19 +2031,19 @@ void Unit::CalcHealAbsorb(Unit* victim, SpellInfo const* healSpell, uint32 &heal ++i; if (auraEff->GetAmount() <= 0) { - uint32 removedAuras = victim->m_removedAurasCount; + uint32 removedAuras = healInfo.GetTarget()->m_removedAurasCount; auraEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL); - if (removedAuras+1 < victim->m_removedAurasCount) + if (removedAuras + 1 < healInfo.GetTarget()->m_removedAurasCount) i = vHealAbsorb.begin(); } } } - absorb = RemainingHeal > 0 ? (healAmount - RemainingHeal) : healAmount; - healAmount = RemainingHeal; + if (absorbAmount > 0) + healInfo.AbsorbHeal(absorbAmount); } -void Unit::AttackerStateUpdate (Unit* victim, WeaponAttackType attType, bool extra) +void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extra) { if (HasUnitState(UNIT_STATE_CANNOT_AUTOATTACK) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED)) return; @@ -2007,8 +2077,8 @@ void Unit::AttackerStateUpdate (Unit* victim, WeaponAttackType attType, bool ext DealDamageMods(victim, damageInfo.damage, &damageInfo.absorb); SendAttackStateUpdate(&damageInfo); - //TriggerAurasProcOnEvent(damageInfo); - ProcDamageAndSpell(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, damageInfo.procEx, damageInfo.damage, damageInfo.attackType); + DamageInfo dmgInfo(damageInfo); + ProcSkillsAndAuras(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, PROC_SPELL_TYPE_NONE, PROC_SPELL_PHASE_NONE, dmgInfo.GetHitMask(), nullptr, &dmgInfo, nullptr); DealMeleeDamage(&damageInfo, true); @@ -2021,6 +2091,49 @@ void Unit::AttackerStateUpdate (Unit* victim, WeaponAttackType attType, bool ext } } +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) @@ -2030,7 +2143,7 @@ void Unit::HandleProcExtraAttackFor(Unit* victim) } } -MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit* victim, WeaponAttackType attType) const +MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(Unit const* victim, WeaponAttackType attType) const { // This is only wrapper @@ -2041,10 +2154,9 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit* victim, WeaponAttackTy // Critical hit chance float crit_chance = GetUnitCriticalChance(attType, victim); - // stunned target cannot dodge and this is check in GetUnitDodgeChance() (returned 0 in this case) - float dodge_chance = victim->GetUnitDodgeChance(); - float block_chance = victim->GetUnitBlockChance(); - float parry_chance = victim->GetUnitParryChance(); + float dodge_chance = GetUnitDodgeChance(attType, victim); + float block_chance = GetUnitBlockChance(attType, victim); + float parry_chance = GetUnitParryChance(attType, victim); // Useful if want to specify crit & miss chances for melee, else it could be removed TC_LOG_DEBUG("entities.unit", "MELEE OUTCOME: miss %f crit %f dodge %f parry %f block %f", miss_chance, crit_chance, dodge_chance, parry_chance, block_chance); @@ -2052,123 +2164,68 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit* victim, WeaponAttackTy return RollMeleeOutcomeAgainst(victim, attType, int32(crit_chance*100), int32(miss_chance*100), int32(dodge_chance*100), int32(parry_chance*100), int32(block_chance*100)); } -MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const +MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(Unit const* victim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const { if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks()) return MELEE_HIT_EVADE; + // melee attack table implementation + // outcome priority: + // 1. > 2. > 3. > 4. > 5. > 6. > 7. > 8. + // MISS > DODGE > PARRY > GLANCING > BLOCK > CRIT > CRUSHING > HIT + int32 attackerMaxSkillValueForLevel = GetMaxSkillValueForLevel(victim); int32 victimMaxSkillValueForLevel = victim->GetMaxSkillValueForLevel(this); int32 attackerWeaponSkill = GetWeaponSkillValue(attType, victim); int32 victimDefenseSkill = victim->GetDefenseSkillValue(this); - // bonus from skills is 0.04% - int32 skillBonus = 4 * (attackerWeaponSkill - victimMaxSkillValueForLevel); int32 sum = 0, tmp = 0; - int32 roll = urand (0, 10000); + int32 roll = urand(0, 9999); - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: skill bonus of %d for attacker", skillBonus); - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: rolled %d, miss %d, dodge %d, parry %d, block %d, crit %d", - roll, miss_chance, dodge_chance, parry_chance, block_chance, crit_chance); + // check if attack comes from behind, nobody can parry or block if attacker is behind + bool canParryOrBlock = victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION); - tmp = miss_chance; + // only creatures can dodge if attacker is behind + bool canDodge = victim->GetTypeId() != TYPEID_PLAYER || canParryOrBlock; - if (tmp > 0 && roll < (sum += tmp)) + // if victim is casting or cc'd it can't avoid attacks + if (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED)) { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: MISS"); - return MELEE_HIT_MISS; + canDodge = false; + canParryOrBlock = false; } + // 1. MISS + tmp = miss_chance; + if (tmp > 0 && roll < (sum += tmp)) + return MELEE_HIT_MISS; + // always crit against a sitting target (except 0 crit chance) if (victim->GetTypeId() == TYPEID_PLAYER && crit_chance > 0 && !victim->IsStandState()) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: CRIT (sitting victim)"); return MELEE_HIT_CRIT; - } - // Dodge chance - - // only players can't dodge if attacker is behind - if (victim->GetTypeId() == TYPEID_PLAYER && !victim->HasInArc(float(M_PI), this) && !victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: attack came from behind and victim was a player."); - } - else + // 2. DODGE + if (canDodge) { - // Reduce dodge chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - dodge_chance -= int32(ToPlayer()->GetExpertiseDodgeOrParryReduction(attType) * 100); - else - dodge_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) * 25; - - // Modify dodge chance by attacker SPELL_AURA_MOD_COMBAT_RESULT_CHANCE - dodge_chance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE) * 100; - dodge_chance = int32 (float (dodge_chance) * GetTotalAuraMultiplier(SPELL_AURA_MOD_ENEMY_DODGE)); - tmp = dodge_chance; - if ((tmp > 0) // check if unit _can_ dodge - && ((tmp -= skillBonus) > 0) + if (tmp > 0 // check if unit _can_ dodge && roll < (sum += tmp)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: DODGE <%d, %d)", sum-tmp, sum); return MELEE_HIT_DODGE; - } - } - - // parry & block chances - - // check if attack comes from behind, nobody can parry or block if attacker is behind - if (!victim->HasInArc(float(M_PI), this) && !victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)) - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: attack came from behind."); - else - { - // Reduce parry chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - parry_chance -= int32(ToPlayer()->GetExpertiseDodgeOrParryReduction(attType) * 100); - else - parry_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) * 25; - - if (victim->GetTypeId() == TYPEID_PLAYER || !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY)) - { - int32 tmp2 = int32(parry_chance); - if (tmp2 > 0 // check if unit _can_ parry - && (tmp2 -= skillBonus) > 0 - && roll < (sum += tmp2)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: PARRY <%d, %d)", sum-tmp2, sum); - return MELEE_HIT_PARRY; - } - } - - if (victim->GetTypeId() == TYPEID_PLAYER || !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK)) - { - tmp = block_chance; - if (tmp > 0 // check if unit _can_ block - && (tmp -= skillBonus) > 0 - && roll < (sum += tmp)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: BLOCK <%d, %d)", sum-tmp, sum); - return MELEE_HIT_BLOCK; - } - } } - // Critical chance - tmp = crit_chance; - - if (tmp > 0 && roll < (sum += tmp)) + // 3. PARRY + if (canParryOrBlock) { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: CRIT <%d, %d)", sum-tmp, sum); - if (GetTypeId() == TYPEID_UNIT && (ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRIT)) - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: CRIT DISABLED)"); - else - return MELEE_HIT_CRIT; + tmp = parry_chance; + if (tmp > 0 // check if unit _can_ parry + && roll < (sum += tmp)) + return MELEE_HIT_PARRY; } + // 4. GLANCING // Max 40% chance to score a glancing blow against mobs that are higher level (can do only players and pets and not with ranged weapon) - if (attType != RANGED_ATTACK && - (GetTypeId() == TYPEID_PLAYER || IsPet()) && + if ((GetTypeId() == TYPEID_PLAYER || IsPet()) && victim->GetTypeId() != TYPEID_PLAYER && !victim->IsPet() && getLevel() < victim->getLevelForTarget(this)) { @@ -2177,15 +2234,29 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackT int32 maxskill = attackerMaxSkillValueForLevel; skill = (skill > maxskill) ? maxskill : skill; - tmp = (10 + (victimDefenseSkill - skill)) * 100; - tmp = tmp > 4000 ? 4000 : tmp; - if (roll < (sum += tmp)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: GLANCING <%d, %d)", sum-4000, sum); + // against boss-level targets - 24% chance of 25% average damage reduction (damage reduction range : 20-30%) + // against level 82 elites - 18% chance of 15% average damage reduction (damage reduction range : 10-20%) + tmp = 600 + (victimDefenseSkill - skill) * 120; + tmp = std::min(tmp, 4000); + if (tmp > 0 && roll < (sum += tmp)) return MELEE_HIT_GLANCING; - } } + // 5. BLOCK + if (canParryOrBlock) + { + tmp = block_chance; + if (tmp > 0 // check if unit _can_ block + && roll < (sum += tmp)) + return MELEE_HIT_BLOCK; + } + + // 6.CRIT + tmp = crit_chance; + if (tmp > 0 && roll < (sum += tmp)) + return MELEE_HIT_CRIT; + + // 7. CRUSHING // mobs can score crushing blows if they're 4 or more levels above victim if (getLevelForTarget(victim) >= victim->getLevelForTarget(this) + 4 && // can be from by creature (if can) or from controlled player that considered as creature @@ -2194,24 +2265,20 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackT { // when their weapon skill is 15 or more above victim's defense skill tmp = victimDefenseSkill; - int32 tmpmax = victimMaxSkillValueForLevel; // having defense above your maximum (from items, talents etc.) has no effect - tmp = tmp > tmpmax ? tmpmax : tmp; + tmp = std::min(tmp, victimMaxSkillValueForLevel); // tmp = mob's level * 5 - player's current defense skill tmp = attackerMaxSkillValueForLevel - tmp; - if (tmp >= 15) - { - // add 2% chance per lacking skill point, min. is 15% - tmp = tmp * 200 - 1500; - if (roll < (sum += tmp)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: CRUSHING <%d, %d)", sum-tmp, sum); - return MELEE_HIT_CRUSHING; - } - } + // minimum of 20 points diff (4 levels difference) + tmp = std::max(tmp, 20); + + // add 2% chance per lacking skill point + tmp = tmp * 200 - 1500; + if (tmp > 0 && roll < (sum += tmp)) + return MELEE_HIT_CRUSHING; } - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: NORMAL"); + // 8. HIT return MELEE_HIT_NORMAL; } @@ -2302,21 +2369,20 @@ 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 + if (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED)) return false; 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 = victim->GetUnitBlockChance(); - blockChance += (int32(GetWeaponSkillValue(attackType)) - int32(victim->GetMaxSkillValueForLevel())) * 0.04f; - if (roll_chance_f(blockChance)) + float blockChance = GetUnitBlockChance(attackType, victim); + if (blockChance && roll_chance_f(blockChance)) return true; } + return false; } @@ -2346,7 +2412,8 @@ int32 Unit::GetMechanicResistChance(SpellInfo const* spellInfo) const resistMech = temp; } } - return resistMech; + + return std::max(resistMech, 0); } bool Unit::CanUseAttackType(uint8 attacktype) const @@ -2365,13 +2432,8 @@ bool Unit::CanUseAttackType(uint8 attacktype) const } // Melee based spells hit result calculations -SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) +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 @@ -2380,16 +2442,15 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo attType = RANGED_ATTACK; int32 attackerWeaponSkill; - // skill value for these spells (for example judgements) is 5* level + // skill value for these spells (for example judgements) is 5 * level if (spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED && !spellInfo->IsRangedWeaponSpell()) - attackerWeaponSkill = getLevel() * 5; - // bonus from skills is 0.04% per skill Diff + attackerWeaponSkill = GetMaxSkillValueForLevel(); else attackerWeaponSkill = int32(GetWeaponSkillValue(attType, victim)); int32 skillDiff = attackerWeaponSkill - int32(victim->GetMaxSkillValueForLevel(this)); - uint32 roll = urand (0, 10000); + uint32 roll = urand(0, 9999); uint32 missChance = uint32(MeleeSpellMissChance(victim, attType, skillDiff, spellInfo->Id) * 100.0f); // Roll miss @@ -2411,6 +2472,14 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo bool canParry = true; bool canBlock = spellInfo->HasAttribute(SPELL_ATTR3_BLOCKABLE_SPELL); + // if victim is casting or cc'd it can't avoid attacks + if (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED)) + { + canDodge = false; + canParry = false; + canBlock = false; + } + // Ranged attacks can only miss, resist and deflect if (attType == RANGED_ATTACK) { @@ -2418,7 +2487,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; @@ -2433,10 +2502,10 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo { if (!victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)) { - // Can`t dodge from behind in PvP (but its possible in PvE) + // Can't dodge from behind in PvP (but its possible in PvE) if (victim->GetTypeId() == TYPEID_PLAYER) canDodge = false; - // Can`t parry or block + // Can't parry or block canParry = false; canBlock = false; } @@ -2446,23 +2515,15 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo canParry = false; } } - // Check creatures flags_extra for disable parry - if (victim->GetTypeId() == TYPEID_UNIT) - { - uint32 flagEx = victim->ToCreature()->GetCreatureTemplate()->flags_extra; - if (flagEx & CREATURE_FLAG_EXTRA_NO_PARRY) - canParry = false; - // Check creatures flags_extra for disable block - if (flagEx & CREATURE_FLAG_EXTRA_NO_BLOCK) - canBlock = false; - } + // Ignore combat result aura AuraEffectList const& ignore = GetAuraEffectsByType(SPELL_AURA_IGNORE_COMBAT_RESULT); - for (AuraEffectList::const_iterator i = ignore.begin(); i != ignore.end(); ++i) + for (AuraEffect const* aurEff : ignore) { - if (!(*i)->IsAffectedOnSpell(spellInfo)) + if (!aurEff->IsAffectedOnSpell(spellInfo)) continue; - switch ((*i)->GetMiscValue()) + + switch (aurEff->GetMiscValue()) { case MELEE_HIT_DODGE: canDodge = false; @@ -2474,7 +2535,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo canParry = false; break; default: - TC_LOG_DEBUG("entities.unit", "Spell %u SPELL_AURA_IGNORE_COMBAT_RESULT has unhandled state %d", (*i)->GetId(), (*i)->GetMiscValue()); + TC_LOG_DEBUG("entities.unit", "Spell %u SPELL_AURA_IGNORE_COMBAT_RESULT has unhandled state %d", aurEff->GetId(), aurEff->GetMiscValue()); break; } } @@ -2482,15 +2543,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo if (canDodge) { // Roll dodge - int32 dodgeChance = int32(victim->GetUnitDodgeChance() * 100.0f) - skillDiff * 4; - // Reduce enemy dodge chance by SPELL_AURA_MOD_COMBAT_RESULT_CHANCE - dodgeChance += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE) * 100; - dodgeChance = int32(float(dodgeChance) * GetTotalAuraMultiplier(SPELL_AURA_MOD_ENEMY_DODGE)); - // Reduce dodge chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - dodgeChance -= int32(ToPlayer()->GetExpertiseDodgeOrParryReduction(attType) * 100.0f); - else - dodgeChance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) * 25; + int32 dodgeChance = int32(GetUnitDodgeChance(attType, victim) * 100.0f); if (dodgeChance < 0) dodgeChance = 0; @@ -2501,12 +2554,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo if (canParry) { // Roll parry - int32 parryChance = int32(victim->GetUnitParryChance() * 100.0f) - skillDiff * 4; - // Reduce parry chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - parryChance -= int32(ToPlayer()->GetExpertiseDodgeOrParryReduction(attType) * 100.0f); - else - parryChance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) * 25; + int32 parryChance = int32(GetUnitParryChance(attType, victim) * 100.0f); if (parryChance < 0) parryChance = 0; @@ -2517,7 +2565,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo if (canBlock) { - int32 blockChance = int32(victim->GetUnitBlockChance() * 100.0f) - skillDiff * 4; + int32 blockChance = int32(GetUnitBlockChance(attType, victim) * 100.0f); if (blockChance < 0) blockChance = 0; tmp += blockChance; @@ -2530,10 +2578,10 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo } /// @todo need use unit spell resistances in calculations -SpellMissInfo Unit::MagicSpellHitResult(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(); @@ -2553,7 +2601,7 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo // Spellmod from SPELLMOD_RESIST_MISS_CHANCE if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_MISS_CHANCE, modHitChance); + modOwner->ApplySpellMod<SPELLMOD_RESIST_MISS_CHANCE>(spellInfo->Id, modHitChance); // Increase from attacker SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT auras modHitChance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT, schoolMask); @@ -2576,8 +2624,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; @@ -2599,10 +2646,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 @@ -2610,7 +2654,7 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo return SPELL_MISS_RESIST; // cast by caster in front of victim - 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; @@ -2631,19 +2675,23 @@ 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; + // Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply + // returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit + if (spellInfo->HasOnlyDamageEffects() && victim->IsImmunedToDamage(spellInfo)) + return SPELL_MISS_IMMUNE; + // All positive spells can`t miss /// @todo client not show miss log for this spells - so need find info for this in dbc and use it! if (spellInfo->IsPositive() && !IsHostileTo(victim)) // prevent from affecting enemy by "positive" spell return SPELL_MISS_NONE; - // Check for immune - if (victim->IsImmunedToDamage(spellInfo)) - return SPELL_MISS_IMMUNE; - if (this == victim) return SPELL_MISS_NONE; @@ -2659,12 +2707,9 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, boo for (Unit::AuraEffectList::const_iterator i = mReflectSpellsSchool.begin(); i != mReflectSpellsSchool.end(); ++i) if ((*i)->GetMiscValue() & spellInfo->GetSchoolMask()) reflectchance += (*i)->GetAmount(); + if (reflectchance > 0 && roll_chance_i(reflectchance)) - { - // Start triggers for remove charges if need (trigger only for victim, and mark as active spell) - ProcDamageAndSpell(victim, PROC_FLAG_NONE, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_EX_REFLECT, 1, BASE_ATTACK, spellInfo); return SPELL_MISS_REFLECT; - } } switch (spellInfo->DmgClass) @@ -2715,55 +2760,93 @@ uint32 Unit::GetDefenseSkillValue(Unit const* target) const return GetUnitMeleeSkill(target); } -float Unit::GetUnitDodgeChance() const +float Unit::GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const { - if (IsNonMeleeSpellCast(false) || HasUnitState(UNIT_STATE_CONTROLLED)) - return 0.0f; + int32 const attackerWeaponSkill = GetWeaponSkillValue(attType, victim); + int32 const victimMaxSkillValueForLevel = victim->GetMaxSkillValueForLevel(this); + int32 const skillDiff = victimMaxSkillValueForLevel - attackerWeaponSkill; - if (GetTypeId() == TYPEID_PLAYER) - return GetFloatValue(PLAYER_DODGE_PERCENTAGE); + float chance = 0.0f; + float skillBonus = 0.0f; + if (victim->GetTypeId() == TYPEID_PLAYER) + { + chance = victim->GetFloatValue(PLAYER_DODGE_PERCENTAGE); + skillBonus = 0.04f * skillDiff; + } else { - if (IsTotem()) - return 0.0f; - else + if (!victim->IsTotem()) { - float dodge = 5.0f; - dodge += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT); - return dodge > 0.0f ? dodge : 0.0f; + chance = 5.0f; + chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT); + + if (skillDiff <= 10) + skillBonus = skillDiff * 0.1f; + else + skillBonus = 1.0f + (skillDiff - 10) * 0.1f; } } + + chance += skillBonus; + + // Reduce enemy dodge chance by SPELL_AURA_MOD_COMBAT_RESULT_CHANCE + chance += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE); + + // reduce dodge by SPELL_AURA_MOD_ENEMY_DODGE + chance += GetTotalAuraModifier(SPELL_AURA_MOD_ENEMY_DODGE); + + // Reduce dodge chance by attacker expertise rating + if (GetTypeId() == TYPEID_PLAYER) + chance -= ToPlayer()->GetExpertiseDodgeOrParryReduction(attType); + else + chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) / 4.0f; + return std::max(chance, 0.0f); } -float Unit::GetUnitParryChance() const +float Unit::GetUnitParryChance(WeaponAttackType attType, Unit const* victim) const { - if (IsNonMeleeSpellCast(false) || HasUnitState(UNIT_STATE_CONTROLLED)) - return 0.0f; + int32 const attackerWeaponSkill = GetWeaponSkillValue(attType, victim); + int32 const victimMaxSkillValueForLevel = victim->GetMaxSkillValueForLevel(this); + int32 const skillDiff = victimMaxSkillValueForLevel - attackerWeaponSkill; float chance = 0.0f; - - if (Player const* player = ToPlayer()) + float skillBonus = 0.0f; + if (Player const* playerVictim = victim->ToPlayer()) { - if (player->CanParry()) + if (playerVictim->CanParry()) { - Item* tmpitem = player->GetWeaponForAttack(BASE_ATTACK, true); + Item* tmpitem = playerVictim->GetWeaponForAttack(BASE_ATTACK, true); if (!tmpitem) - tmpitem = player->GetWeaponForAttack(OFF_ATTACK, true); + tmpitem = playerVictim->GetWeaponForAttack(OFF_ATTACK, true); if (tmpitem) - chance = GetFloatValue(PLAYER_PARRY_PERCENTAGE); + chance = playerVictim->GetFloatValue(PLAYER_PARRY_PERCENTAGE); + + skillBonus = 0.04f * skillDiff; } } - else if (GetTypeId() == TYPEID_UNIT) + else { - if (GetCreatureType() == CREATURE_TYPE_HUMANOID) + if (!victim->IsTotem() && !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY)) { chance = 5.0f; - chance += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT); + chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT); + + if (skillDiff <= 10) + skillBonus = skillDiff * 0.1f; + else + skillBonus = 1.0f + (skillDiff - 10) * 1.6f; } } - return chance > 0.0f ? chance : 0.0f; + chance += skillBonus; + + // Reduce parry chance by attacker expertise rating + if (GetTypeId() == TYPEID_PLAYER) + chance -= ToPlayer()->GetExpertiseDodgeOrParryReduction(attType); + else + chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) / 4.0f; + return std::max(chance, 0.0f); } float Unit::GetUnitMissChance(WeaponAttackType attType) const @@ -2771,7 +2854,7 @@ float Unit::GetUnitMissChance(WeaponAttackType attType) const float miss_chance = 5.00f; if (Player const* player = ToPlayer()) - miss_chance += player->GetMissPercentageFromDefence(); + miss_chance += player->GetMissPercentageFromDefense(); if (attType == RANGED_ATTACK) miss_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE); @@ -2781,70 +2864,86 @@ float Unit::GetUnitMissChance(WeaponAttackType attType) const return miss_chance; } -float Unit::GetUnitBlockChance() const +float Unit::GetUnitBlockChance(WeaponAttackType attType, Unit const* victim) const { - if (IsNonMeleeSpellCast(false) || HasUnitState(UNIT_STATE_CONTROLLED)) - return 0.0f; + int32 const attackerWeaponSkill = GetWeaponSkillValue(attType, victim); + int32 const victimMaxSkillValueForLevel = victim->GetMaxSkillValueForLevel(this); + int32 const skillDiff = victimMaxSkillValueForLevel - attackerWeaponSkill; - if (Player const* player = ToPlayer()) + float chance = 0.0f; + float skillBonus = 0.0f; + if (Player const* playerVictim = victim->ToPlayer()) { - if (player->CanBlock()) + if (playerVictim->CanBlock()) { - Item* tmpitem = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + Item* tmpitem = playerVictim->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); if (tmpitem && !tmpitem->IsBroken() && tmpitem->GetTemplate()->Block) - return GetFloatValue(PLAYER_BLOCK_PERCENTAGE); + { + chance = playerVictim->GetFloatValue(PLAYER_BLOCK_PERCENTAGE); + skillBonus = 0.04f * skillDiff; + } } - // is player but has no block ability or no not broken shield equipped - return 0.0f; } else { - if (IsTotem()) - return 0.0f; - else + if (!victim->IsTotem() && !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK)) { - float block = 5.0f; - block += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT); - return block > 0.0f ? block : 0.0f; + chance = 5.0f; + chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT); + + if (skillDiff <= 10) + skillBonus = skillDiff * 0.1f; + else + skillBonus = 1.0f + (skillDiff - 10) * 0.1f; } } + + chance += skillBonus; + return std::max(chance, 0.0f); } -float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit* victim) const +float Unit::GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victim) const { - float crit; + int32 const attackerWeaponSkill = GetWeaponSkillValue(attackType, victim); + int32 const victimDefenseSkill = victim->GetDefenseSkillValue(this); + int32 const skillDiff = victimDefenseSkill - attackerWeaponSkill; + float chance = 0.0f; + float skillBonus = 0.0f; if (GetTypeId() == TYPEID_PLAYER) { switch (attackType) { case BASE_ATTACK: - crit = GetFloatValue(PLAYER_CRIT_PERCENTAGE); + chance = GetFloatValue(PLAYER_CRIT_PERCENTAGE); break; case OFF_ATTACK: - crit = GetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE); + chance = GetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE); break; case RANGED_ATTACK: - crit = GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE); + chance = GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE); break; // Just for good manner default: - crit = 0.0f; + chance = 0.0f; break; } } else { - crit = 5.0f; - crit += GetTotalAuraModifier(SPELL_AURA_MOD_WEAPON_CRIT_PERCENT); - crit += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PCT); + if (!(ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRIT)) + { + chance = 5.0f; + chance += GetTotalAuraModifier(SPELL_AURA_MOD_WEAPON_CRIT_PERCENT); + chance += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PCT); + } } // flat aura mods if (attackType == RANGED_ATTACK) - crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE); + chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE); else - crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE); + chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE); AuraEffectList const& critChanceForCaster = victim->GetAuraEffectsByType(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER); for (AuraEffect const* aurEff : critChanceForCaster) @@ -2852,31 +2951,32 @@ float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit* victi if (aurEff->GetCasterGUID() != GetGUID()) continue; - crit += aurEff->GetAmount(); + chance += aurEff->GetAmount(); } - crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); + chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); // reduce crit chance from Rating for players if (attackType != RANGED_ATTACK) - { - ApplyResilience(victim, &crit, NULL, false, CR_CRIT_TAKEN_MELEE); - // Glyph of barkskin - if (victim->HasAura(63057) && victim->HasAura(22812)) - crit -= 25.0f; - } + ApplyResilience(victim, &chance, nullptr, false, CR_CRIT_TAKEN_MELEE); else - ApplyResilience(victim, &crit, NULL, false, CR_CRIT_TAKEN_RANGED); + ApplyResilience(victim, &chance, nullptr, false, CR_CRIT_TAKEN_RANGED); - // Apply crit chance from defence skill - crit += (int32(GetMaxSkillValueForLevel(victim)) - int32(victim->GetDefenseSkillValue(this))) * 0.04f; + // Apply crit chance from defense skill + if (victim->GetTypeId() == TYPEID_PLAYER) + skillBonus = -skillDiff * 0.04f; + else + { + skillBonus = -skillDiff * 0.12f; + if (skillDiff >= 15) + skillBonus -= 3.0f; + } - if (crit < 0.0f) - crit = 0.0f; - return crit; + chance += skillBonus; + return std::max(chance, 0.0f); } -uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target) const +uint32 Unit::GetWeaponSkillValue(WeaponAttackType attType, Unit const* target) const { uint32 value = 0; if (Player const* player = ToPlayer()) @@ -2903,15 +3003,22 @@ uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target) value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL)); switch (attType) { - case BASE_ATTACK: value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL_MAINHAND)); break; - case OFF_ATTACK: value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL_OFFHAND)); break; - case RANGED_ATTACK: value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL_RANGED)); break; - default: break; + case BASE_ATTACK: + value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL_MAINHAND)); + break; + case OFF_ATTACK: + value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL_OFFHAND)); + break; + case RANGED_ATTACK: + value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL_RANGED)); + break; + default: + break; } } else value = GetUnitMeleeSkill(target); - return value; + return value; } void Unit::_DeleteRemovedAuras() @@ -3193,10 +3300,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 @@ -3277,7 +3387,7 @@ void Unit::DeMorph() SetDisplayId(GetNativeDisplayId()); } -Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 effMask, Unit* caster, int32* baseAmount /*= NULL*/, Item* castItem /*= NULL*/, ObjectGuid casterGUID /*= 0*/) +Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 effMask, Unit* caster, int32* baseAmount /*= nullptr*/, Item* castItem /*= nullptr*/, ObjectGuid casterGUID /*= ObjectGuid::Empty*/, bool resetPeriodicTimer /*= true*/) { ASSERT(casterGUID || caster); @@ -3300,7 +3410,7 @@ Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 // extremely rare case // let's just recreate aura if (effMask != foundAura->GetEffectMask()) - return NULL; + return nullptr; // update basepoints with new values - effect amount will be recalculated in ModStackAmount for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) @@ -3326,12 +3436,12 @@ Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 } // try to increase stack amount - foundAura->ModStackAmount(1); + foundAura->ModStackAmount(1, AURA_REMOVE_BY_DEFAULT, resetPeriodicTimer); return foundAura; } } - return NULL; + return nullptr; } void Unit::_AddAura(UnitAura* aura, Unit* caster) @@ -3939,7 +4049,7 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, U if (aura->IsSingleTarget()) aura->UnregisterSingleTarget(); - if (Aura* newAura = Aura::TryRefreshStackOrCreate(aura->GetSpellInfo(), effMask, stealer, NULL, &baseDamage[0], NULL, aura->GetCasterGUID())) + if (Aura* newAura = Aura::TryRefreshStackOrCreate(aura->GetSpellInfo(), effMask, stealer, nullptr, &baseDamage[0], nullptr, aura->GetCasterGUID())) { // created aura must not be single target aura,, so stealer won't loose it on recast if (newAura->IsSingleTarget()) @@ -4182,17 +4292,13 @@ void Unit::RemoveArenaAuras() { // in join, remove positive buffs, on end, remove negative // used to remove positive visible auras in arenas - for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();) + RemoveAppliedAuras([](AuraApplication const* aurApp) { - AuraApplication const* aurApp = iter->second; Aura const* aura = aurApp->GetBase(); - if (!aura->GetSpellInfo()->HasAttribute(SPELL_ATTR4_UNK21) // don't remove stances, shadowform, pally/hunter auras - && !aura->IsPassive() // don't remove passive auras - && (aurApp->IsPositive() || !aura->GetSpellInfo()->HasAttribute(SPELL_ATTR3_DEATH_PERSISTENT))) // not negative death persistent auras - RemoveAura(iter); - else - ++iter; - } + return !aura->GetSpellInfo()->HasAttribute(SPELL_ATTR4_UNK21) // don't remove stances, shadowform, pally/hunter auras + && !aura->IsPassive() // don't remove passive auras + && (aurApp->IsPositive() || !aura->GetSpellInfo()->HasAttribute(SPELL_ATTR3_DEATH_PERSISTENT)); // not negative death persistent auras + }); } void Unit::RemoveAurasOnEvade() @@ -4469,8 +4575,8 @@ void Unit::GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelCharges // The charges / stack amounts don't count towards the total number of auras that can be dispelled. // Ie: A dispel on a target with 5 stacks of Winters Chill and a Polymorph has 1 / (1 + 1) -> 50% chance to dispell // Polymorph instead of 1 / (5 + 1) -> 16%. - bool dispel_charges = aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES); - uint8 charges = dispel_charges ? aura->GetCharges() : aura->GetStackAmount(); + bool dispelCharges = aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES); + uint8 charges = dispelCharges ? aura->GetCharges() : aura->GetStackAmount(); if (charges > 0) dispelList.push_back(std::make_pair(aura, charges)); } @@ -5140,15 +5246,16 @@ void Unit::SendSpellNonMeleeDamageLog(Unit* target, uint32 SpellID, uint32 Damag SendSpellNonMeleeDamageLog(&log); } -void Unit::ProcDamageAndSpell(Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procExtra, uint32 amount, WeaponAttackType attType, SpellInfo const* procSpell, SpellInfo const* procAura) +void Unit::ProcSkillsAndAuras(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo) { - // Not much to do if no flags are set. - if (procAttacker) - ProcDamageAndSpellFor(false, victim, procAttacker, procExtra, attType, procSpell, amount, procAura); - // Now go on with a victim's events'n'auras - // Not much to do if no flags are set or there is no victim - if (victim && victim->IsAlive() && procVictim) - victim->ProcDamageAndSpellFor(true, this, procVictim, procExtra, attType, procSpell, amount, procAura); + WeaponAttackType attType = damageInfo ? damageInfo->GetAttackType() : BASE_ATTACK; + if (typeMaskActor) + ProcSkillsAndReactives(false, actionTarget, typeMaskActor, hitMask, attType); + + if (typeMaskActionTarget && actionTarget) + actionTarget->ProcSkillsAndReactives(true, this, typeMaskActionTarget, hitMask, attType); + + TriggerAurasProcOnEvent(actionTarget, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); } void Unit::SendPeriodicAuraLog(SpellPeriodicAuraLogInfo* pInfo) @@ -5235,8 +5342,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(); @@ -5310,2970 +5417,6 @@ void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit* target, uint8 /*SwingType SendAttackStateUpdate(&dmgInfo); } -//victim may be NULL -bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, Milliseconds& cooldown) -{ - SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo(); - uint32 effIndex = triggeredByAura->GetEffIndex(); - int32 triggerAmount = triggeredByAura->GetAmount(); - - Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER - ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; - - uint32 triggered_spell_id = 0; - Unit* target = victim; - int32 basepoints0 = 0; - ObjectGuid originalCaster; - - switch (dummySpell->SpellFamilyName) - { - case SPELLFAMILY_GENERIC: - { - switch (dummySpell->Id) - { - // Unstable Power - case 24658: - { - if (!procSpell || procSpell->Id == 24659) - return false; - // Need remove one 24659 aura - RemoveAuraFromStack(24659); - return true; - } - // Restless Strength - case 24661: - { - // Need remove one 24662 aura - RemoveAuraFromStack(24662); - return true; - } - // Mark of Malice - case 33493: - { - // Cast finish spell at last charge - if (triggeredByAura->GetBase()->GetCharges() > 1) - return false; - - target = this; - triggered_spell_id = 33494; - break; - } - // Twisted Reflection (boss spell) - case 21063: - triggered_spell_id = 21064; - break; - // Vampiric Aura (boss spell) - case 38196: - { - basepoints0 = 3 * damage; // 300% - if (basepoints0 < 0) - return false; - - triggered_spell_id = 31285; - target = this; - break; - } - // Aura of Madness (Darkmoon Card: Madness trinket) - //===================================================== - // 39511 Sociopath: +35 strength (Paladin, Rogue, Druid, Warrior) - // 40997 Delusional: +70 attack power (Rogue, Hunter, Paladin, Warrior, Druid) - // 40998 Kleptomania: +35 agility (Warrior, Rogue, Paladin, Hunter, Druid) - // 40999 Megalomania: +41 damage/healing (Druid, Shaman, Priest, Warlock, Mage, Paladin) - // 41002 Paranoia: +35 spell/melee/ranged crit strike rating (All classes) - // 41005 Manic: +35 haste (spell, melee and ranged) (All classes) - // 41009 Narcissism: +35 intellect (Druid, Shaman, Priest, Warlock, Mage, Paladin, Hunter) - // 41011 Martyr Complex: +35 stamina (All classes) - // 41406 Dementia: Every 5 seconds either gives you +5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin) - // 41409 Dementia: Every 5 seconds either gives you -5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin) - case 39446: - { - if (GetTypeId() != TYPEID_PLAYER || !IsAlive()) - return false; - - // Select class defined buff - switch (getClass()) - { - case CLASS_PALADIN: // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409 - case CLASS_DRUID: // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409 - triggered_spell_id = RAND(39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409); - break; - case CLASS_ROGUE: // 39511, 40997, 40998, 41002, 41005, 41011 - case CLASS_WARRIOR: // 39511, 40997, 40998, 41002, 41005, 41011 - case CLASS_DEATH_KNIGHT: - triggered_spell_id = RAND(39511, 40997, 40998, 41002, 41005, 41011); - break; - case CLASS_PRIEST: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 - case CLASS_SHAMAN: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 - case CLASS_MAGE: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 - case CLASS_WARLOCK: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 - triggered_spell_id = RAND(40999, 41002, 41005, 41009, 41011, 41406, 41409); - break; - case CLASS_HUNTER: // 40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409 - triggered_spell_id = RAND(40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409); - break; - default: - return false; - } - - target = this; - if (roll_chance_i(10)) - ToPlayer()->Say("This is Madness!", LANG_UNIVERSAL); /// @todo It should be moved to database, shouldn't it? - break; - } - // Sunwell Exalted Caster Neck (??? neck) - // cast ??? Light's Wrath if Exalted by Aldor - // cast ??? Arcane Bolt if Exalted by Scryers - case 46569: - return false; // old unused version - // Sunwell Exalted Caster Neck (Shattered Sun Pendant of Acumen neck) - // cast 45479 Light's Wrath if Exalted by Aldor - // cast 45429 Arcane Bolt if Exalted by Scryers - case 45481: - { - Player* player = ToPlayer(); - if (!player) - return false; - - // Get Aldor reputation rank - if (player->GetReputationRank(932) == REP_EXALTED) - { - target = this; - triggered_spell_id = 45479; - break; - } - // Get Scryers reputation rank - if (player->GetReputationRank(934) == REP_EXALTED) - { - // triggered at positive/self casts also, current attack target used then - if (target && IsFriendlyTo(target)) - { - target = GetVictim(); - if (!target) - { - target = player->GetSelectedUnit(); - if (!target) - return false; - } - if (IsFriendlyTo(target)) - return false; - } - - triggered_spell_id = 45429; - break; - } - return false; - } - // Sunwell Exalted Melee Neck (Shattered Sun Pendant of Might neck) - // cast 45480 Light's Strength if Exalted by Aldor - // cast 45428 Arcane Strike if Exalted by Scryers - case 45482: - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - // Get Aldor reputation rank - if (ToPlayer()->GetReputationRank(932) == REP_EXALTED) - { - target = this; - triggered_spell_id = 45480; - break; - } - // Get Scryers reputation rank - if (ToPlayer()->GetReputationRank(934) == REP_EXALTED) - { - triggered_spell_id = 45428; - break; - } - return false; - } - // Sunwell Exalted Tank Neck (Shattered Sun Pendant of Resolve neck) - // cast 45431 Arcane Insight if Exalted by Aldor - // cast 45432 Light's Ward if Exalted by Scryers - case 45483: - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - // Get Aldor reputation rank - if (ToPlayer()->GetReputationRank(932) == REP_EXALTED) - { - target = this; - triggered_spell_id = 45432; - break; - } - // Get Scryers reputation rank - if (ToPlayer()->GetReputationRank(934) == REP_EXALTED) - { - target = this; - triggered_spell_id = 45431; - break; - } - return false; - } - // Sunwell Exalted Healer Neck (Shattered Sun Pendant of Restoration neck) - // cast 45478 Light's Salvation if Exalted by Aldor - // cast 45430 Arcane Surge if Exalted by Scryers - case 45484: - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - // Get Aldor reputation rank - if (ToPlayer()->GetReputationRank(932) == REP_EXALTED) - { - target = this; - triggered_spell_id = 45478; - break; - } - // Get Scryers reputation rank - if (ToPlayer()->GetReputationRank(934) == REP_EXALTED) - { - triggered_spell_id = 45430; - break; - } - return false; - } - // Kill command - case 58914: - { - // Remove aura stack from pet - RemoveAuraFromStack(58914); - Unit* owner = GetOwner(); - if (!owner) - return true; - // reduce the owner's aura stack - owner->RemoveAuraFromStack(34027); - return true; - } - // Vampiric Touch (generic, used by some boss) - case 52723: - case 60501: - { - triggered_spell_id = 52724; - basepoints0 = damage / 2; - target = this; - break; - } - // Shadowfiend Death (Gain mana if pet dies with Glyph of Shadowfiend) - case 57989: - { - Unit* owner = GetOwner(); - if (!owner || owner->GetTypeId() != TYPEID_PLAYER) - return false; - // Glyph of Shadowfiend (need cast as self cast for owner, no hidden cooldown) - owner->CastSpell(owner, 58227, true, castItem, triggeredByAura); - return true; - } - // Divine purpose - case 31871: - case 31872: - { - // Roll chane - if (!victim || !victim->IsAlive() || !roll_chance_i(triggerAmount)) - return false; - - // Remove any stun effect on target - victim->RemoveAurasWithMechanic(1<<MECHANIC_STUN, AURA_REMOVE_BY_ENEMY_SPELL); - return true; - } - // Glyph of Scourge Strike - case 58642: - { - triggered_spell_id = 69961; // Glyph of Scourge Strike - break; - } - // Glyph of Life Tap - case 63320: - { - triggered_spell_id = 63321; // Life Tap - break; - } - // Purified Shard of the Scale - Onyxia 10 Caster Trinket - case 69755: - { - triggered_spell_id = (procFlag & PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS) ? 69733 : 69729; - break; - } - // Shiny Shard of the Scale - Onyxia 25 Caster Trinket - case 69739: - { - triggered_spell_id = (procFlag & PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS) ? 69734 : 69730; - break; - } - case 71519: // Deathbringer's Will Normal - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - std::vector<uint32> RandomSpells; - switch (getClass()) - { - case CLASS_WARRIOR: - case CLASS_PALADIN: - case CLASS_DEATH_KNIGHT: - RandomSpells.push_back(71484); - RandomSpells.push_back(71491); - RandomSpells.push_back(71492); - break; - case CLASS_SHAMAN: - case CLASS_ROGUE: - RandomSpells.push_back(71486); - RandomSpells.push_back(71485); - RandomSpells.push_back(71492); - break; - case CLASS_DRUID: - RandomSpells.push_back(71484); - RandomSpells.push_back(71485); - RandomSpells.push_back(71492); - break; - case CLASS_HUNTER: - RandomSpells.push_back(71486); - RandomSpells.push_back(71491); - RandomSpells.push_back(71485); - break; - default: - return false; - } - if (RandomSpells.empty()) // shouldn't happen - return false; - - uint8 rand_spell = urand(0, (RandomSpells.size() - 1)); - CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster); - break; - } - case 71562: // Deathbringer's Will Heroic - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - std::vector<uint32> RandomSpells; - switch (getClass()) - { - case CLASS_WARRIOR: - case CLASS_PALADIN: - case CLASS_DEATH_KNIGHT: - RandomSpells.push_back(71561); - RandomSpells.push_back(71559); - RandomSpells.push_back(71560); - break; - case CLASS_SHAMAN: - case CLASS_ROGUE: - RandomSpells.push_back(71558); - RandomSpells.push_back(71556); - RandomSpells.push_back(71560); - break; - case CLASS_DRUID: - RandomSpells.push_back(71561); - RandomSpells.push_back(71556); - RandomSpells.push_back(71560); - break; - case CLASS_HUNTER: - RandomSpells.push_back(71558); - RandomSpells.push_back(71559); - RandomSpells.push_back(71556); - break; - default: - return false; - } - if (RandomSpells.empty()) // shouldn't happen - return false; - - uint8 rand_spell = urand(0, (RandomSpells.size() - 1)); - CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster); - break; - } - case 65032: // Boom aura (321 Boombot) - { - if (victim->GetEntry() != 33343) // Scrapbot - return false; - - InstanceScript* instance = GetInstanceScript(); - if (!instance) - return false; - - instance->DoCastSpellOnPlayers(65037); // Achievement criteria marker - break; - } - } - break; - } - case SPELLFAMILY_MAGE: - { - // Magic Absorption - if (dummySpell->SpellIconID == 459) // only this spell has SpellIconID == 459 and dummy aura - { - if (getPowerType() != POWER_MANA) - return false; - - // mana reward - basepoints0 = CalculatePct(int32(GetMaxPower(POWER_MANA)), triggerAmount); - target = this; - triggered_spell_id = 29442; - break; - } - // Arcane Potency - if (dummySpell->SpellIconID == 2120) - { - if (!procSpell) - return false; - - target = this; - switch (dummySpell->Id) - { - case 31571: triggered_spell_id = 57529; break; - case 31572: triggered_spell_id = 57531; break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: non handled spell id: %u", dummySpell->Id); - return false; - } - break; - } - // Hot Streak - if (dummySpell->SpellIconID == 2999) - { - if (effIndex != 0) - return false; - AuraEffect* counter = triggeredByAura->GetBase()->GetEffect(EFFECT_1); - if (!counter) - return true; - - // Count spell criticals in a row in second aura - if (procEx & PROC_EX_CRITICAL_HIT) - { - counter->SetAmount(counter->GetAmount() * 2); - if (counter->GetAmount() < 100) // not enough - return true; - // Crititcal counted -> roll chance - if (roll_chance_i(triggerAmount)) - CastSpell(this, 48108, true, castItem, triggeredByAura); - } - counter->SetAmount(25); - return true; - } - // Incanter's Regalia set (add trigger chance to Mana Shield) - if (dummySpell->SpellFamilyFlags[0] & 0x8000) - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - target = this; - triggered_spell_id = 37436; - break; - } - switch (dummySpell->Id) - { - // Glyph of Polymorph - case 56375: - { - if (!target) - return false; - target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE, ObjectGuid::Empty, target->GetAura(32409)); // SW:D shall not be removed. - target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); - target->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH); - return true; - } - // Glyph of Icy Veins - case 56374: - { - RemoveAurasByType(SPELL_AURA_HASTE_SPELLS, ObjectGuid::Empty, 0, true, false); - RemoveAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); - return true; - } - // Glyph of Ice Block - case 56372: - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool - { - SpellInfo const* cdSpell = sSpellMgr->GetSpellInfo(itr->first); - if (!cdSpell || cdSpell->SpellFamilyName != SPELLFAMILY_MAGE - || !(cdSpell->SpellFamilyFlags[0] & 0x00000040)) - return false; - return true; - }, true); - break; - } - case 47020: // Enter vehicle XT-002 (Scrapbot) - { - if (GetTypeId() != TYPEID_UNIT) - return false; - - Unit* vehicleBase = GetVehicleBase(); - if (!vehicleBase) - return false; - - /// @todo Check if this amount is blizzlike - vehicleBase->ModifyHealth(int32(vehicleBase->CountPctFromMaxHealth(1))); - break; - } - } - break; - } - case SPELLFAMILY_WARRIOR: - { - switch (dummySpell->Id) - { - // Victorious - case 32216: - { - RemoveAura(dummySpell->Id); - return false; - } - // Improved Spell Reflection - case 59088: - case 59089: - { - triggered_spell_id = 59725; - target = this; - break; - } - } - // Second Wind - if (dummySpell->SpellIconID == 1697) - { - // only for spells and hit/crit (trigger start always) and not start from self cast spells (5530 Mace Stun Effect for example) - if (!procSpell || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == victim) - return false; - // Need stun or root mechanic - if (!(procSpell->GetAllEffectsMechanicMask() & ((1<<MECHANIC_ROOT)|(1<<MECHANIC_STUN)))) - return false; - - switch (dummySpell->Id) - { - case 29838: triggered_spell_id=29842; break; - case 29834: triggered_spell_id=29841; break; - case 42770: triggered_spell_id=42771; break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: non handled spell id: %u (SW)", dummySpell->Id); - return false; - } - - target = this; - break; - } - // Glyph of Blocking - if (dummySpell->Id == 58375) - { - triggered_spell_id = 58374; - break; - } - break; - } - case SPELLFAMILY_WARLOCK: - { - // Seed of Corruption - if (dummySpell->SpellFamilyFlags[1] & 0x00000010) - { - if (procSpell && procSpell->SpellFamilyFlags[1] & 0x8000) - return false; - // if damage is more than need or target die from damage deal finish spell - if (triggeredByAura->GetAmount() <= int32(damage) || GetHealth() <= damage) - { - // remember caster before aura delete - Unit* caster = triggeredByAura->GetCaster(); - - // Remove aura (before cast for prevent infinite loop handlers) - RemoveAurasDueToSpell(triggeredByAura->GetId()); - - uint32 spell = sSpellMgr->GetSpellWithRank(27285, dummySpell->GetRank()); - - // Cast finish spell (triggeredByAura already not exist!) - if (caster) - caster->CastSpell(this, spell, true, castItem); - return true; // no hidden cooldown - } - - // Damage counting - triggeredByAura->SetAmount(triggeredByAura->GetAmount() - damage); - return true; - } - // Seed of Corruption (Mobs cast) - no die req - if (dummySpell->SpellFamilyFlags.IsEqual(0, 0, 0) && dummySpell->SpellIconID == 1932) - { - // if damage is more than need deal finish spell - if (triggeredByAura->GetAmount() <= int32(damage)) - { - // remember caster before aura delete - Unit* caster = triggeredByAura->GetCaster(); - - // Remove aura (before cast for prevent infinite loop handlers) - RemoveAurasDueToSpell(triggeredByAura->GetId()); - - // Cast finish spell (triggeredByAura already not exist!) - if (caster) - caster->CastSpell(this, 32865, true, castItem); - return true; // no hidden cooldown - } - // Damage counting - triggeredByAura->SetAmount(triggeredByAura->GetAmount() - damage); - return true; - } - switch (dummySpell->Id) - { - // Nightfall - case 18094: - case 18095: - // Glyph of corruption - case 56218: - { - target = this; - triggered_spell_id = 17941; - break; - } - // Soul Leech - case 30293: - case 30295: - case 30296: - { - // Improved Soul Leech - AuraEffectList const& SoulLeechAuras = GetAuraEffectsByType(SPELL_AURA_DUMMY); - for (Unit::AuraEffectList::const_iterator i = SoulLeechAuras.begin(); i != SoulLeechAuras.end(); ++i) - { - if ((*i)->GetId() == 54117 || (*i)->GetId() == 54118) - { - if ((*i)->GetEffIndex() != 0) - continue; - basepoints0 = int32((*i)->GetAmount()); - target = GetGuardianPet(); - if (target) - { - // regen mana for pet - CastCustomSpell(target, 54607, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - } - // regen mana for caster - CastCustomSpell(this, 59117, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - // Get second aura of spell for replenishment effect on party - if (AuraEffect const* aurEff = (*i)->GetBase()->GetEffect(EFFECT_1)) - { - // Replenishment - roll chance - if (roll_chance_i(aurEff->GetAmount())) - CastSpell(this, 57669, true, castItem, triggeredByAura); - } - break; - } - } - // health - basepoints0 = CalculatePct(int32(damage), triggerAmount); - target = this; - triggered_spell_id = 30294; - break; - } - // Shadowflame (Voidheart Raiment set bonus) - case 37377: - { - triggered_spell_id = 37379; - break; - } - // Pet Healing (Corruptor Raiment or Rift Stalker Armor) - case 37381: - { - target = GetGuardianPet(); - if (!target) - return false; - - // heal amount - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 37382; - break; - } - // Shadowflame Hellfire (Voidheart Raiment set bonus) - case 39437: - { - triggered_spell_id = 37378; - break; - } - // Glyph of Succubus - case 56250: - { - if (!target) - return false; - target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE, ObjectGuid::Empty, target->GetAura(32409)); // SW:D shall not be removed. - target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); - target->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH); - return true; - } - } - break; - } - case SPELLFAMILY_PRIEST: - { - // Vampiric Touch - if (dummySpell->SpellFamilyFlags[1] & 0x00000400) - { - if (!victim || !victim->IsAlive()) - return false; - - if (effIndex != 0) - return false; - - // victim is caster of aura - if (triggeredByAura->GetCasterGUID() != victim->GetGUID()) - return false; - - // Energize 0.25% of max. mana - victim->CastSpell(victim, 57669, true, castItem, triggeredByAura); - return true; // no hidden cooldown - } - // Body and Soul - if (dummySpell->SpellIconID == 2218) - { - // Proc only from Abolish desease on self cast - if (!procSpell || procSpell->Id != 552 || victim != this || !roll_chance_i(triggerAmount)) - return false; - triggered_spell_id = 64136; - target = this; - break; - } - switch (dummySpell->Id) - { - // Vampiric Embrace - case 15286: - { - if (!victim || !victim->IsAlive() || !procSpell || procSpell->SpellFamilyFlags[1] & 0x80000) - return false; - - // heal amount - int32 total = CalculatePct(int32(damage), triggerAmount); - int32 team = total / 5; - int32 self = total - team; - CastCustomSpell(this, 15290, &team, &self, NULL, true, castItem, triggeredByAura); - return true; // no hidden cooldown - } - // Priest Tier 6 Trinket (Ashtongue Talisman of Acumen) - case 40438: - { - // Shadow Word: Pain - if (!procSpell) - return false; - else if (procSpell->SpellFamilyFlags[0] & 0x8000) - triggered_spell_id = 40441; - // Renew - else if (procSpell->SpellFamilyFlags[0] & 0x40) - triggered_spell_id = 40440; - else - return false; - - target = this; - break; - } - // Improved Shadowform - case 47570: - case 47569: - { - if (!roll_chance_i(triggerAmount)) - return false; - - RemoveMovementImpairingAuras(); - break; - } - // Glyph of Dispel Magic - case 55677: - { - // Dispel Magic shares spellfamilyflag with abolish disease - if (!procSpell || procSpell->SpellIconID != 74) - return false; - if (!target || !target->IsFriendlyTo(this)) - return false; - - basepoints0 = int32(target->CountPctFromMaxHealth(triggerAmount)); - triggered_spell_id = 56131; - break; - } - // Oracle Healing Bonus ("Garments of the Oracle" set) - case 26169: - { - // heal amount - basepoints0 = int32(CalculatePct(damage, 10)); - target = this; - triggered_spell_id = 26170; - break; - } - // Frozen Shadoweave (Shadow's Embrace set) warning! its not only priest set - case 39372: - { - if (!procSpell || (procSpell->GetSchoolMask() & (SPELL_SCHOOL_MASK_FROST | SPELL_SCHOOL_MASK_SHADOW)) == 0) - return false; - - // heal amount - basepoints0 = CalculatePct(int32(damage), triggerAmount); - target = this; - triggered_spell_id = 39373; - break; - } - // Greater Heal (Vestments of Faith (Priest Tier 3) - 4 pieces bonus) - case 28809: - { - triggered_spell_id = 28810; - break; - } - // Priest T10 Healer 2P Bonus - case 70770: - // Flash Heal - if (procSpell && procSpell->SpellFamilyFlags[0] & 0x800) - { - triggered_spell_id = 70772; - SpellInfo const* blessHealing = sSpellMgr->GetSpellInfo(triggered_spell_id); - if (!blessHealing) - return false; - basepoints0 = int32(CalculatePct(damage, triggerAmount) / (blessHealing->GetMaxDuration() / blessHealing->Effects[0].Amplitude)); - } - break; - } - break; - } - case SPELLFAMILY_DRUID: - { - switch (dummySpell->Id) - { - // Glyph of Innervate - case 54832: - { - if (!procSpell || procSpell->SpellIconID != 62) - return false; - - int32 mana_perc = triggeredByAura->GetSpellInfo()->Effects[triggeredByAura->GetEffIndex()].CalcValue(); - basepoints0 = int32(CalculatePct(GetCreatePowers(POWER_MANA), mana_perc) / 10); - triggered_spell_id = 54833; - target = this; - break; - } - // Glyph of Starfire - case 54845: - { - triggered_spell_id = 54846; - break; - } - // Glyph of Shred - case 54815: - { - if (!target) - return false; - - // try to find spell Rip on the target - if (AuraEffect const* AurEff = target->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DRUID, 0x00800000, 0x0, 0x0, GetGUID())) - { - // Rip's max duration, note: spells which modifies Rip's duration also counted like Glyph of Rip - uint32 CountMin = AurEff->GetBase()->GetMaxDuration(); - - // just Rip's max duration without other spells - uint32 CountMax = AurEff->GetSpellInfo()->GetMaxDuration(); - - // add possible auras' and Glyph of Shred's max duration - CountMax += 3 * triggerAmount * IN_MILLISECONDS; // Glyph of Shred -> +6 seconds - CountMax += HasAura(54818) ? 4 * IN_MILLISECONDS : 0; // Glyph of Rip -> +4 seconds - CountMax += HasAura(60141) ? 4 * IN_MILLISECONDS : 0; // Rip Duration/Lacerate Damage -> +4 seconds - - // if min < max -> that means caster didn't cast 3 shred yet - // so set Rip's duration and max duration - if (CountMin < CountMax) - { - AurEff->GetBase()->SetDuration(AurEff->GetBase()->GetDuration() + triggerAmount * IN_MILLISECONDS); - AurEff->GetBase()->SetMaxDuration(CountMin + triggerAmount * IN_MILLISECONDS); - return true; - } - } - // if not found Rip - return false; - } - // Glyph of Rake - case 54821: - { - if (procSpell && procSpell->SpellVisual[0] == 750 && procSpell->Effects[1].ApplyAuraName == 3) - { - if (target && target->GetTypeId() == TYPEID_UNIT) - { - triggered_spell_id = 54820; - break; - } - } - return false; - } - // Leader of the Pack - case 24932: - { - if (triggerAmount <= 0) - return false; - basepoints0 = int32(CountPctFromMaxHealth(triggerAmount)); - target = this; - triggered_spell_id = 34299; - if (triggeredByAura->GetCasterGUID() != GetGUID()) - break; - int32 basepoints1 = CalculatePct(GetMaxPower(Powers(POWER_MANA)), triggerAmount * 2); - // Improved Leader of the Pack - // Check cooldown of heal spell cooldown - if (!GetSpellHistory()->HasCooldown(34299)) - CastCustomSpell(this, 68285, &basepoints1, 0, 0, true, 0, triggeredByAura); - break; - } - // Healing Touch (Dreamwalker Raiment set) - case 28719: - { - if (procSpell) - { - // mana back - basepoints0 = int32(CalculatePct(procSpell->ManaCost, 30)); - target = this; - triggered_spell_id = 28742; - } - break; - } - // Glyph of Rejuvenation - case 54754: - { - if (!victim || !victim->HealthBelowPct(uint32(triggerAmount))) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 54755; - break; - } - // Healing Touch Refund (Idol of Longevity trinket) - case 28847: - { - target = this; - triggered_spell_id = 28848; - break; - } - // Mana Restore (Malorne Raiment set / Malorne Regalia set) - case 37288: - case 37295: - { - target = this; - triggered_spell_id = 37238; - break; - } - // Druid Tier 6 Trinket - case 40442: - { - float chance; - - if (!procSpell) - return false; - // Starfire - else if (procSpell->SpellFamilyFlags[0] & 0x4) - { - triggered_spell_id = 40445; - chance = 25.0f; - } - // Rejuvenation - else if (procSpell->SpellFamilyFlags[0] & 0x10) - { - triggered_spell_id = 40446; - chance = 25.0f; - } - // Mangle (Bear) and Mangle (Cat) - else if (procSpell->SpellFamilyFlags[1] & 0x00000440) - { - triggered_spell_id = 40452; - chance = 40.0f; - } - else - return false; - - if (!roll_chance_f(chance)) - return false; - - target = this; - break; - } - // Maim Interrupt - case 44835: - { - // Deadly Interrupt Effect - triggered_spell_id = 32747; - break; - } - // Item - Druid T10 Balance 4P Bonus - case 70723: - { - // Wrath & Starfire - if (procSpell && (procSpell->SpellFamilyFlags[0] & 0x5) && (procEx & PROC_EX_CRITICAL_HIT)) - { - triggered_spell_id = 71023; - SpellInfo const* triggeredSpell = sSpellMgr->GetSpellInfo(triggered_spell_id); - if (!triggeredSpell) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount) / (triggeredSpell->GetMaxDuration() / triggeredSpell->Effects[0].Amplitude); - // Add remaining ticks to damage done - basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_DAMAGE); - } - break; - } - // Item - Druid T10 Restoration 4P Bonus (Rejuvenation) - case 70664: - { - // Proc only from normal Rejuvenation - if (!procSpell || procSpell->SpellVisual[0] != 32) - return false; - - Player* caster = ToPlayer(); - if (!caster) - return false; - if (!caster->GetGroup() && victim == this) - return false; - - CastCustomSpell(70691, SPELLVALUE_BASE_POINT0, damage, victim, true); - return true; - } - } - break; - } - case SPELLFAMILY_ROGUE: - { - switch (dummySpell->Id) - { - case 56800: // Glyph of Backstab - { - triggered_spell_id = 63975; - break; - } - case 32748: // Deadly Throw Interrupt - { - // Prevent cast Deadly Throw Interrupt on self from last effect (apply dummy) of Deadly Throw - if (this == victim) - return false; - - triggered_spell_id = 32747; - break; - } - } - - switch (dummySpell->SpellIconID) - { - case 2116: // Quick Recovery - { - if (!procSpell) - return false; - - // energy cost save - basepoints0 = CalculatePct(int32(procSpell->ManaCost), triggerAmount); - if (basepoints0 <= 0) - return false; - - target = this; - triggered_spell_id = 31663; - break; - } - case 2909: // Cut to the Chase - { - // "refresh your Slice and Dice duration to its 5 combo point maximum" - // lookup Slice and Dice - if (AuraEffect const* aur = GetAuraEffect(SPELL_AURA_MOD_MELEE_HASTE, SPELLFAMILY_ROGUE, 0x40000, 0, 0)) - { - aur->GetBase()->SetDuration(aur->GetSpellInfo()->GetMaxDuration(), true); - return true; - } - return false; - } - case 2963: // Deadly Brew - { - triggered_spell_id = 3409; - break; - } - } - break; - } - case SPELLFAMILY_HUNTER: - { - switch (dummySpell->SpellIconID) - { - case 2236: // Thrill of the Hunt - { - if (!procSpell) - return false; - - Spell* spell = ToPlayer()->m_spellModTakingSpell; - - // Disable charge drop because of Lock and Load - ToPlayer()->SetSpellModTakingSpell(spell, false); - - // Explosive Shot - if (procSpell->SpellFamilyFlags[2] & 0x200) - { - if (!victim) - return false; - if (AuraEffect const* pEff = victim->GetAuraEffect(SPELL_AURA_PERIODIC_DUMMY, SPELLFAMILY_HUNTER, 0x0, 0x80000000, 0x0, GetGUID())) - basepoints0 = pEff->GetSpellInfo()->CalcPowerCost(this, SpellSchoolMask(pEff->GetSpellInfo()->SchoolMask)) * 4/10/3; - } - else - basepoints0 = procSpell->CalcPowerCost(this, SpellSchoolMask(procSpell->SchoolMask)) * 4/10; - - ToPlayer()->SetSpellModTakingSpell(spell, true); - - if (basepoints0 <= 0) - return false; - - target = this; - triggered_spell_id = 34720; - break; - } - case 3406: // Hunting Party - { - triggered_spell_id = 57669; - target = this; - break; - } - case 3560: // Rapid Recuperation - { - // This effect only from Rapid Killing (mana regen) - if (!procSpell || !(procSpell->SpellFamilyFlags[1] & 0x01000000)) - return false; - - target = this; - - switch (dummySpell->Id) - { - case 53228: // Rank 1 - triggered_spell_id = 56654; - break; - case 53232: // Rank 2 - triggered_spell_id = 58882; - break; - } - break; - } - } - - switch (dummySpell->Id) - { - case 57870: // Glyph of Mend Pet - { - victim->CastSpell(victim, 57894, true, NULL, NULL, GetGUID()); - return true; - } - } - break; - } - case SPELLFAMILY_PALADIN: - { - // Judgements of the Wise - if (dummySpell->SpellIconID == 3017) - { - target = this; - triggered_spell_id = 31930; - // replenishment - CastSpell(this, 57669, true, castItem, triggeredByAura); - break; - } - // Righteous Vengeance - if (dummySpell->SpellIconID == 3025) - { - // 4 damage tick - basepoints0 = triggerAmount * damage / 400; - triggered_spell_id = 61840; - // Add remaining ticks to damage done - basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_DAMAGE); - break; - } - // Sheath of Light - if (dummySpell->SpellIconID == 3030) - { - // 4 healing tick - basepoints0 = triggerAmount * damage / 400; - triggered_spell_id = 54203; - // Add remaining ticks to healing done - basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_HEAL); - break; - } - switch (dummySpell->Id) - { - // Sacred Shield - case 53601: - { - if (procFlag & PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS) - return false; - - if (damage > 0) - triggered_spell_id = 58597; - - // Item - Paladin T8 Holy 4P Bonus - if (Unit* caster = triggeredByAura->GetCaster()) - if (AuraEffect const* aurEff = caster->GetAuraEffect(64895, 0)) - cooldown = Milliseconds(aurEff->GetAmount()); - - target = this; - break; - } - // Heart of the Crusader - case 20335: // rank 1 - triggered_spell_id = 21183; - break; - case 20336: // rank 2 - triggered_spell_id = 54498; - break; - case 20337: // rank 3 - triggered_spell_id = 54499; - break; - // Judgement of Light - case 20185: - { - if (!victim) - return false; - - // 2% of maximum health - basepoints0 = int32(victim->CountPctFromMaxHealth(2)); - victim->CastCustomSpell(victim, 20267, &basepoints0, 0, 0, true, 0, triggeredByAura); - return true; - } - // Judgement of Wisdom - case 20186: - { - if (victim && victim->IsAlive() && victim->getPowerType() == POWER_MANA) - { - // 2% of base mana - basepoints0 = int32(CalculatePct(victim->GetCreateMana(), 2)); - victim->CastCustomSpell(victim, 20268, &basepoints0, NULL, NULL, true, 0, triggeredByAura); - } - return true; - } - // Holy Power (Redemption Armor set) - case 28789: - { - if (!victim) - return false; - - // Set class defined buff - switch (victim->getClass()) - { - case CLASS_PALADIN: - case CLASS_PRIEST: - case CLASS_SHAMAN: - case CLASS_DRUID: - triggered_spell_id = 28795; // Increases the friendly target's mana regeneration by $s1 per 5 sec. for $d. - break; - case CLASS_MAGE: - case CLASS_WARLOCK: - triggered_spell_id = 28793; // Increases the friendly target's spell damage and healing by up to $s1 for $d. - break; - case CLASS_HUNTER: - case CLASS_ROGUE: - triggered_spell_id = 28791; // Increases the friendly target's attack power by $s1 for $d. - break; - case CLASS_WARRIOR: - triggered_spell_id = 28790; // Increases the friendly target's armor - break; - default: - return false; - } - break; - } - // Seal of Vengeance (damage calc on apply aura) - case 31801: - { - if (effIndex != 0) // effect 1, 2 used by seal unleashing code - return false; - - // At melee attack or Hammer of the Righteous spell damage considered as melee attack - bool stacker = procSpell ? procSpell->Id == 53595 : true; - // spells with SPELL_DAMAGE_CLASS_MELEE excluding Judgements - bool damager = procSpell ? (procSpell->EquippedItemClass != -1 || (procSpell->SpellIconID == 243 && procSpell->SpellVisual[0] == 39)) : false; - - if (!stacker && !damager) - return false; - - triggered_spell_id = 31803; - - // On target with 5 stacks of Holy Vengeance direct damage is done - if (Aura* aur = victim->GetAura(triggered_spell_id, GetGUID())) - { - if (aur->GetStackAmount() == 5) - { - if (stacker) - aur->RefreshDuration(); - CastSpell(victim, 42463, true); - return true; - } - } - - if (!stacker) - return false; - break; - } - // Seal of Corruption - case 53736: - { - if (effIndex != 0) // effect 1, 2 used by seal unleashing code - return false; - - // At melee attack or Hammer of the Righteous spell damage considered as melee attack - bool stacker = procSpell ? procSpell->Id == 53595 : true; - // spells with SPELL_DAMAGE_CLASS_MELEE excluding Judgements - bool damager = procSpell ? (procSpell->EquippedItemClass != -1 || (procSpell->SpellIconID == 243 && procSpell->SpellVisual[0] == 39)) : false; - - if (!stacker && !damager) - return false; - - triggered_spell_id = 53742; - - // On target with 5 stacks of Blood Corruption direct damage is done - if (Aura* aur = victim->GetAura(triggered_spell_id, GetGUID())) - { - if (aur->GetStackAmount() == 5) - { - if (stacker) - aur->RefreshDuration(); - CastSpell(victim, 53739, true); - return true; - } - } - - if (!stacker) - return false; - break; - } - // Spiritual Attunement - case 31785: - case 33776: - { - // if healed by another unit (victim) - if (this == victim) - return false; - - // heal amount - basepoints0 = int32(CalculatePct(std::min(damage, GetMaxHealth() - GetHealth()), triggerAmount)); - target = this; - - if (basepoints0) - triggered_spell_id = 31786; - break; - } - // Paladin Tier 6 Trinket (Ashtongue Talisman of Zeal) - case 40470: - { - if (!procSpell) - return false; - - float chance; - - // Flash of light/Holy light - if (procSpell->SpellFamilyFlags[0] & 0xC0000000) - { - triggered_spell_id = 40471; - chance = 15.0f; - } - // Judgement (any) - else if (procSpell->GetSpellSpecific() == SPELL_SPECIFIC_JUDGEMENT) - { - triggered_spell_id = 40472; - chance = 50.0f; - } - else - return false; - - if (!roll_chance_f(chance)) - return false; - - break; - } - // Glyph of Holy Light - case 54937: - { - triggered_spell_id = 54968; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - break; - } - // Item - Paladin T8 Holy 2P Bonus - case 64890: - { - triggered_spell_id = 64891; - basepoints0 = triggerAmount * damage / 300; - break; - } - case 71406: // Tiny Abomination in a Jar - case 71545: // Tiny Abomination in a Jar (Heroic) - { - if (!victim || !victim->IsAlive()) - return false; - - CastSpell(this, 71432, true, NULL, triggeredByAura); - - Aura const* dummy = GetAura(71432); - if (!dummy || dummy->GetStackAmount() < (dummySpell->Id == 71406 ? 8 : 7)) - return false; - - RemoveAurasDueToSpell(71432); - triggered_spell_id = 71433; // default main hand attack - // roll if offhand - if (Player const* player = ToPlayer()) - if (player->GetWeaponForAttack(OFF_ATTACK, true) && urand(0, 1)) - triggered_spell_id = 71434; - target = victim; - break; - } - // Item - Icecrown 25 Normal Dagger Proc - case 71880: - { - switch (getPowerType()) - { - case POWER_MANA: - triggered_spell_id = 71881; - break; - case POWER_RAGE: - triggered_spell_id = 71883; - break; - case POWER_ENERGY: - triggered_spell_id = 71882; - break; - case POWER_RUNIC_POWER: - triggered_spell_id = 71884; - break; - default: - return false; - } - break; - } - // Item - Icecrown 25 Heroic Dagger Proc - case 71892: - { - switch (getPowerType()) - { - case POWER_MANA: - triggered_spell_id = 71888; - break; - case POWER_RAGE: - triggered_spell_id = 71886; - break; - case POWER_ENERGY: - triggered_spell_id = 71887; - break; - case POWER_RUNIC_POWER: - triggered_spell_id = 71885; - break; - default: - return false; - } - break; - } - } - break; - } - case SPELLFAMILY_SHAMAN: - { - switch (dummySpell->Id) - { - // Tidal Force - case 55198: - { - // Remove aura stack from caster - RemoveAuraFromStack(55166); - // drop charges - return false; - } - // Totemic Power (The Earthshatterer set) - case 28823: - { - if (!victim) - return false; - - // Set class defined buff - switch (victim->getClass()) - { - case CLASS_PALADIN: - case CLASS_PRIEST: - case CLASS_SHAMAN: - case CLASS_DRUID: - triggered_spell_id = 28824; // Increases the friendly target's mana regeneration by $s1 per 5 sec. for $d. - break; - case CLASS_MAGE: - case CLASS_WARLOCK: - triggered_spell_id = 28825; // Increases the friendly target's spell damage and healing by up to $s1 for $d. - break; - case CLASS_HUNTER: - case CLASS_ROGUE: - triggered_spell_id = 28826; // Increases the friendly target's attack power by $s1 for $d. - break; - case CLASS_WARRIOR: - triggered_spell_id = 28827; // Increases the friendly target's armor - break; - default: - return false; - } - break; - } - // Lesser Healing Wave (Totem of Flowing Water Relic) - case 28849: - { - target = this; - triggered_spell_id = 28850; - break; - } - // Windfury Weapon (Passive) 1-8 Ranks - case 33757: - { - Player* player = ToPlayer(); - if (!player || !castItem || !castItem->IsEquipped() || !victim || !victim->IsAlive()) - return false; - - if (triggeredByAura->GetBase() && castItem->GetGUID() != triggeredByAura->GetBase()->GetCastItemGUID()) - return false; - - WeaponAttackType attType = WeaponAttackType(player->GetAttackBySlot(castItem->GetSlot())); - if ((attType != BASE_ATTACK && attType != OFF_ATTACK) - || (attType == BASE_ATTACK && procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) - || (attType == OFF_ATTACK && procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK)) - return false; - - // Now compute real proc chance... - uint32 chance = 20; - player->ApplySpellMod(dummySpell->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance); - - Item* addWeapon = player->GetWeaponForAttack(attType == BASE_ATTACK ? OFF_ATTACK : BASE_ATTACK, true); - uint32 enchant_id_add = addWeapon ? addWeapon->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT)) : 0; - SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id_add); - if (pEnchant && pEnchant->spellid[0] == dummySpell->Id) - chance += 14; - - if (!roll_chance_i(chance)) - return false; - - // Now amount of extra power stored in 1 effect of Enchant spell - // Get it by item enchant id - uint32 spellId; - switch (castItem->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT))) - { - case 283: spellId = 8232; break; // 1 Rank - case 284: spellId = 8235; break; // 2 Rank - case 525: spellId = 10486; break; // 3 Rank - case 1669:spellId = 16362; break; // 4 Rank - case 2636:spellId = 25505; break; // 5 Rank - case 3785:spellId = 58801; break; // 6 Rank - case 3786:spellId = 58803; break; // 7 Rank - case 3787:spellId = 58804; break; // 8 Rank - default: - { - TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: non handled item enchantment (rank?) %u for spell id: %u (Windfury)", - castItem->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT)), dummySpell->Id); - return false; - } - } - - SpellInfo const* windfurySpellInfo = sSpellMgr->GetSpellInfo(spellId); - if (!windfurySpellInfo) - { - TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: non-existing spell id: %u (Windfury)", spellId); - return false; - } - - int32 extra_attack_power = CalculateSpellDamage(victim, windfurySpellInfo, 1); - - // Value gained from additional AP - basepoints0 = int32(extra_attack_power / 14.0f * GetAttackTime(attType) / 1000); - - if (procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK) - triggered_spell_id = 25504; - - if (procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) - triggered_spell_id = 33750; - - // apply cooldown before cast to prevent processing itself - triggeredByAura->GetBase()->AddProcCooldown(std::chrono::steady_clock::now() + cooldown); - cooldown = Milliseconds::zero(); - - // Attack Twice - for (uint32 i = 0; i < 2; ++i) - CastCustomSpell(victim, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - - return true; - } - // Shaman Tier 6 Trinket - case 40463: - { - if (!procSpell) - return false; - - float chance; - if (procSpell->SpellFamilyFlags[0] & 0x1) - { - triggered_spell_id = 40465; // Lightning Bolt - chance = 15.0f; - } - else if (procSpell->SpellFamilyFlags[0] & 0x80) - { - triggered_spell_id = 40465; // Lesser Healing Wave - chance = 10.0f; - } - else if (procSpell->SpellFamilyFlags[1] & 0x00000010) - { - triggered_spell_id = 40466; // Stormstrike - chance = 50.0f; - } - else - return false; - - if (!roll_chance_f(chance)) - return false; - - target = this; - break; - } - // Glyph of Healing Wave - case 55440: - { - // Not proc from self heals - if (this == victim) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - target = this; - triggered_spell_id = 55533; - break; - } - // Spirit Hunt - case 58877: - { - // Cast on owner - target = GetOwner(); - if (!target) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 58879; - // Cast on spirit wolf - CastCustomSpell(this, triggered_spell_id, &basepoints0, NULL, NULL, true, NULL, triggeredByAura); - break; - } - // Shaman T8 Elemental 4P Bonus - case 64928: - { - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 64930; // Electrified - break; - } - // Shaman T9 Elemental 4P Bonus - case 67228: - { - // Lava Burst - if (procSpell && procSpell->SpellFamilyFlags[1] & 0x1000) - { - triggered_spell_id = 71824; - SpellInfo const* triggeredSpell = sSpellMgr->GetSpellInfo(triggered_spell_id); - if (!triggeredSpell) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount) / (triggeredSpell->GetMaxDuration() / triggeredSpell->Effects[0].Amplitude); - } - break; - } - // Item - Shaman T10 Restoration 4P Bonus - case 70808: - { - // Chain Heal - if (procSpell && (procSpell->SpellFamilyFlags[0] & 0x100) && (procEx & PROC_EX_CRITICAL_HIT)) - { - triggered_spell_id = 70809; - SpellInfo const* triggeredSpell = sSpellMgr->GetSpellInfo(triggered_spell_id); - if (!triggeredSpell) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount) / (triggeredSpell->GetMaxDuration() / triggeredSpell->Effects[0].Amplitude); - // Add remaining ticks to healing done - basepoints0 += GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_HEAL); - } - break; - } - // Item - Shaman T10 Elemental 4P Bonus - case 70817: - { - if (!target) - return false; - // try to find spell Flame Shock on the target - if (AuraEffect const* aurEff = target->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_SHAMAN, 0x10000000, 0x0, 0x0, GetGUID())) - { - Aura* flameShock = aurEff->GetBase(); - int32 maxDuration = flameShock->GetMaxDuration(); - int32 newDuration = flameShock->GetDuration() + 2 * aurEff->GetAmplitude(); - - flameShock->SetDuration(newDuration); - // is it blizzlike to change max duration for FS? - if (newDuration > maxDuration) - flameShock->SetMaxDuration(newDuration); - - return true; - } - // if not found Flame Shock - return false; - } - case 63280: // Glyph of Totem of Wrath - { - if (!procSpell || procSpell->SpellIconID != 2019) - return false; - - if (Creature* totem = GetMap()->GetCreature(m_SummonSlot[1])) // Fire totem summon slot - { - if (SpellInfo const* totemSpell = sSpellMgr->GetSpellInfo(totem->m_spells[0])) - { - int32 bp0 = CalculatePct(totemSpell->Effects[EFFECT_0].CalcValue(), triggerAmount); - int32 bp1 = CalculatePct(totemSpell->Effects[EFFECT_1].CalcValue(), triggerAmount); - CastCustomSpell(this, 63283, &bp0, &bp1, NULL, true); - return true; - } - } - return false; - } - break; - } - // Frozen Power - if (dummySpell->SpellIconID == 3780) - { - if (!target) - return false; - if (GetDistance(target) < 15.0f) - return false; - float chance = (float)triggerAmount; - if (!roll_chance_f(chance)) - return false; - - triggered_spell_id = 63685; - break; - } - // Ancestral Awakening - if (dummySpell->SpellIconID == 3065) - { - triggered_spell_id = 52759; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - target = this; - break; - } - // Flametongue Weapon (Passive) - if (dummySpell->SpellFamilyFlags[0] & 0x200000) - { - if (GetTypeId() != TYPEID_PLAYER || !victim || !victim->IsAlive() || !castItem || !castItem->IsEquipped()) - return false; - - WeaponAttackType attType = WeaponAttackType(Player::GetAttackBySlot(castItem->GetSlot())); - if ((attType != BASE_ATTACK && attType != OFF_ATTACK) - || (attType == BASE_ATTACK && procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) - || (attType == OFF_ATTACK && procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK)) - return false; - - float fire_onhit = float(CalculatePct(dummySpell->Effects[EFFECT_0]. CalcValue(), 1.0f)); - - float add_spellpower = (float)(SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_FIRE) - + victim->SpellBaseDamageBonusTaken(SPELL_SCHOOL_MASK_FIRE)); - - // 1.3speed = 5%, 2.6speed = 10%, 4.0 speed = 15%, so, 1.0speed = 3.84% - ApplyPct(add_spellpower, 3.84f); - - // Enchant on Off-Hand and ready? - if (castItem->GetSlot() == EQUIPMENT_SLOT_OFFHAND && procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) - { - float BaseWeaponSpeed = GetAttackTime(OFF_ATTACK) / 1000.0f; - - // Value1: add the tooltip damage by swingspeed + Value2: add spelldmg by swingspeed - basepoints0 = int32((fire_onhit * BaseWeaponSpeed) + (add_spellpower * BaseWeaponSpeed)); - triggered_spell_id = 10444; - } - - // Enchant on Main-Hand and ready? - else if (castItem->GetSlot() == EQUIPMENT_SLOT_MAINHAND && procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK) - { - float BaseWeaponSpeed = GetAttackTime(BASE_ATTACK) / 1000.0f; - - // Value1: add the tooltip damage by swingspeed + Value2: add spelldmg by swingspeed - basepoints0 = int32((fire_onhit * BaseWeaponSpeed) + (add_spellpower * BaseWeaponSpeed)); - triggered_spell_id = 10444; - } - - // If not ready, we should return, shouldn't we?! - else - return false; - - CastCustomSpell(victim, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - return true; - } - // Improved Water Shield - if (dummySpell->SpellIconID == 2287) - { - // Default chance for Healing Wave and Riptide - float chance = (float)triggeredByAura->GetAmount(); - - if (!procSpell) - return false; //This is the same than putting chance *= 0.0f; - else if (procSpell->SpellFamilyFlags[0] & 0x80) - // Lesser Healing Wave - 0.6 of default - chance *= 0.6f; - else if (procSpell->SpellFamilyFlags[0] & 0x100) - // Chain heal - 0.3 of default - chance *= 0.3f; - - if (!roll_chance_f(chance)) - return false; - - // Water Shield - if (AuraEffect const* aurEff = GetAuraEffect(SPELL_AURA_PROC_TRIGGER_SPELL, SPELLFAMILY_SHAMAN, 0, 0x00000020, 0)) - { - uint32 spell = aurEff->GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell; - CastSpell(this, spell, true, castItem, triggeredByAura); - return true; - } - return false; - } - // Lightning Overload - if (dummySpell->SpellIconID == 2018) // only this spell has SpellFamily Shaman SpellIconID == 2018 and dummy aura - { - if (!procSpell || GetTypeId() != TYPEID_PLAYER || !victim) - return false; - - uint32 spellId = 0; - // Every Lightning Bolt and Chain Lightning spell have duplicate vs half damage and zero cost - switch (procSpell->Id) - { - // Lightning Bolt - case 403: spellId = 45284; break; // Rank 1 - case 529: spellId = 45286; break; // Rank 2 - case 548: spellId = 45287; break; // Rank 3 - case 915: spellId = 45288; break; // Rank 4 - case 943: spellId = 45289; break; // Rank 5 - case 6041: spellId = 45290; break; // Rank 6 - case 10391: spellId = 45291; break; // Rank 7 - case 10392: spellId = 45292; break; // Rank 8 - case 15207: spellId = 45293; break; // Rank 9 - case 15208: spellId = 45294; break; // Rank 10 - case 25448: spellId = 45295; break; // Rank 11 - case 25449: spellId = 45296; break; // Rank 12 - case 49237: spellId = 49239; break; // Rank 13 - case 49238: spellId = 49240; break; // Rank 14 - // Chain Lightning - case 421: spellId = 45297; break; // Rank 1 - case 930: spellId = 45298; break; // Rank 2 - case 2860: spellId = 45299; break; // Rank 3 - case 10605: spellId = 45300; break; // Rank 4 - case 25439: spellId = 45301; break; // Rank 5 - case 25442: spellId = 45302; break; // Rank 6 - case 49270: spellId = 49268; break; // Rank 7 - case 49271: spellId = 49269; break; // Rank 8 - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: non handled spell id: %u (LO)", procSpell->Id); - return false; - } - - // Chain Lightning - if (procSpell->SpellFamilyFlags[0] & 0x2) - { - // Chain lightning has [LightOverload_Proc_Chance] / [Max_Number_of_Targets] chance to proc of each individual target hit. - // A maxed LO would have a 33% / 3 = 11% chance to proc of each target. - // LO chance was already "accounted" at the proc chance roll, now need to divide the chance by [Max_Number_of_Targets] - float chance = 100.0f / procSpell->Effects[effIndex].ChainTarget; - if (!roll_chance_f(chance)) - return false; - } - - CastSpell(victim, spellId, true, castItem, triggeredByAura); - return true; - } - // Static Shock - if (dummySpell->SpellIconID == 3059) - { - // Lightning Shield - if (AuraEffect const* aurEff = GetAuraEffect(SPELL_AURA_PROC_TRIGGER_SPELL, SPELLFAMILY_SHAMAN, 0x400, 0, 0)) - { - uint32 spell = sSpellMgr->GetSpellWithRank(26364, aurEff->GetSpellInfo()->GetRank()); - - // custom cooldown processing case - if (GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(spell)) - GetSpellHistory()->ResetCooldown(spell); - - CastSpell(target, spell, true, castItem, triggeredByAura); - aurEff->GetBase()->DropCharge(); - return true; - } - return false; - } - break; - } - case SPELLFAMILY_DEATHKNIGHT: - { - // Blood-Caked Strike - Blood-Caked Blade - if (dummySpell->SpellIconID == 138) - { - if (!target || !target->IsAlive()) - return false; - - triggered_spell_id = dummySpell->Effects[effIndex].TriggerSpell; - break; - } - // Improved Blood Presence - if (dummySpell->SpellIconID == 2636) - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - break; - } - // Butchery - if (dummySpell->SpellIconID == 2664) - { - basepoints0 = triggerAmount; - triggered_spell_id = 50163; - target = this; - break; - } - // Dancing Rune Weapon - if (dummySpell->Id == 49028) - { - // 1 dummy aura for dismiss rune blade - if (effIndex != 1) - return false; - - Unit* pPet = NULL; - for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) // Find Rune Weapon - if ((*itr)->GetEntry() == 27893) - { - pPet = *itr; - break; - } - - if (pPet && pPet->GetVictim() && damage && procSpell) - { - uint32 procDmg = damage / 2; - pPet->SendSpellNonMeleeDamageLog(pPet->GetVictim(), procSpell->Id, procDmg, procSpell->GetSchoolMask(), 0, 0, false, 0, false); - pPet->DealDamage(pPet->GetVictim(), procDmg, NULL, SPELL_DIRECT_DAMAGE, procSpell->GetSchoolMask(), procSpell, true); - break; - } - else - return false; - } - // Mark of Blood - if (dummySpell->Id == 49005) - { - /// @todo need more info (cooldowns/PPM) - triggered_spell_id = 61607; - break; - } - // Unholy Blight - if (dummySpell->Id == 49194) - { - triggered_spell_id = 50536; - SpellInfo const* unholyBlight = sSpellMgr->GetSpellInfo(triggered_spell_id); - if (!unholyBlight) - return false; - - basepoints0 = CalculatePct(int32(damage), triggerAmount); - - //Glyph of Unholy Blight - if (AuraEffect* glyph=GetAuraEffect(63332, 0)) - AddPct(basepoints0, glyph->GetAmount()); - - basepoints0 = basepoints0 / (unholyBlight->GetMaxDuration() / unholyBlight->Effects[0].Amplitude); - basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_DAMAGE); - break; - } - // Vendetta - if (dummySpell->SpellFamilyFlags[0] & 0x10000) - { - basepoints0 = int32(CountPctFromMaxHealth(triggerAmount)); - triggered_spell_id = 50181; - target = this; - break; - } - // Necrosis - if (dummySpell->SpellIconID == 2709) - { - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 51460; - break; - } - // Threat of Thassarian - if (dummySpell->SpellIconID == 2023) - { - // Must Dual Wield - if (!procSpell || !haveOffhandWeapon()) - return false; - // Chance as basepoints for dummy aura - if (!roll_chance_i(triggerAmount)) - return false; - - switch (procSpell->Id) - { - // Obliterate - case 49020: triggered_spell_id = 66198; break; // Rank 1 - case 51423: triggered_spell_id = 66972; break; // Rank 2 - case 51424: triggered_spell_id = 66973; break; // Rank 3 - case 51425: triggered_spell_id = 66974; break; // Rank 4 - - // Frost Strike - case 49143: triggered_spell_id = 66196; break; // Rank 1 - case 51416: triggered_spell_id = 66958; break; // Rank 2 - case 51417: triggered_spell_id = 66959; break; // Rank 3 - case 51418: triggered_spell_id = 66960; break; // Rank 4 - case 51419: triggered_spell_id = 66961; break; // Rank 5 - case 55268: triggered_spell_id = 66962; break; // Rank 6 - - // Plague Strike - case 45462: triggered_spell_id = 66216; break; // Rank 1 - case 49917: triggered_spell_id = 66988; break; // Rank 2 - case 49918: triggered_spell_id = 66989; break; // Rank 3 - case 49919: triggered_spell_id = 66990; break; // Rank 4 - case 49920: triggered_spell_id = 66991; break; // Rank 5 - case 49921: triggered_spell_id = 66992; break; // Rank 6 - - // Death Strike - case 49998: triggered_spell_id = 66188; break; // Rank 1 - case 49999: triggered_spell_id = 66950; break; // Rank 2 - case 45463: triggered_spell_id = 66951; break; // Rank 3 - case 49923: triggered_spell_id = 66952; break; // Rank 4 - case 49924: triggered_spell_id = 66953; break; // Rank 5 - - // Rune Strike - case 56815: triggered_spell_id = 66217; break; // Rank 1 - - // Blood Strike - case 45902: triggered_spell_id = 66215; break; // Rank 1 - case 49926: triggered_spell_id = 66975; break; // Rank 2 - case 49927: triggered_spell_id = 66976; break; // Rank 3 - case 49928: triggered_spell_id = 66977; break; // Rank 4 - case 49929: triggered_spell_id = 66978; break; // Rank 5 - case 49930: triggered_spell_id = 66979; break; // Rank 6 - default: - return false; - } - break; - } - // Runic Power Back on Snare/Root - if (dummySpell->Id == 61257) - { - // only for spells and hit/crit (trigger start always) and not start from self cast spells - if (!procSpell || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == victim) - return false; - // Need snare or root mechanic - if (!(procSpell->GetAllEffectsMechanicMask() & ((1<<MECHANIC_ROOT)|(1<<MECHANIC_SNARE)))) - return false; - triggered_spell_id = 61258; - target = this; - break; - } - // Wandering Plague - if (dummySpell->SpellIconID == 1614) - { - if (!roll_chance_f(GetUnitCriticalChance(BASE_ATTACK, victim))) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 50526; - break; - } - // Sudden Doom - if (dummySpell->SpellIconID == 1939 && GetTypeId() == TYPEID_PLAYER) - { - SpellChainNode const* chain = NULL; - // get highest rank of the Death Coil spell - PlayerSpellMap const& sp_list = ToPlayer()->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) - { - // check if shown in spell book - if (!itr->second->active || itr->second->disabled || itr->second->state == PLAYERSPELL_REMOVED) - continue; - - SpellInfo const* spellProto = sSpellMgr->GetSpellInfo(itr->first); - if (!spellProto) - continue; - - if (spellProto->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT - && spellProto->SpellFamilyFlags[0] & 0x2000) - { - SpellChainNode const* newChain = sSpellMgr->GetSpellChainNode(itr->first); - - // No chain entry or entry lower than found entry - if (!chain || !newChain || (chain->rank < newChain->rank)) - { - triggered_spell_id = itr->first; - chain = newChain; - } - else - continue; - // Found spell is last in chain - do not need to look more - // Optimisation for most common case - if (chain && chain->last->Id == itr->first) - break; - } - } - } - break; - } - case SPELLFAMILY_POTION: - { - // alchemist's stone - if (procSpell && dummySpell->Id == 17619) - { - if (procSpell->SpellFamilyName == SPELLFAMILY_POTION) - { - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; i++) - { - if (procSpell->Effects[i].Effect == SPELL_EFFECT_HEAL) - { - triggered_spell_id = 21399; - } - else if (procSpell->Effects[i].Effect == SPELL_EFFECT_ENERGIZE) - { - triggered_spell_id = 21400; - } - else - continue; - - basepoints0 = int32(CalculateSpellDamage(this, procSpell, i) * 0.4f); - CastCustomSpell(this, triggered_spell_id, &basepoints0, NULL, NULL, true, NULL, triggeredByAura); - } - return true; - } - } - break; - } - case SPELLFAMILY_PET: - { - switch (dummySpell->SpellIconID) - { - // Guard Dog - case 201: - { - if (!victim || !procSpell) - return false; - - triggered_spell_id = 54445; - target = this; - float addThreat = float(CalculatePct(procSpell->Effects[0].CalcValue(this), triggerAmount)); - victim->AddThreat(this, addThreat); - break; - } - // Silverback - case 1582: - triggered_spell_id = dummySpell->Id == 62765 ? 62801 : 62800; - target = this; - break; - } - break; - } - default: - break; - } - - // if not handled by custom case, get triggered spell from dummySpell proto - if (!triggered_spell_id) - triggered_spell_id = dummySpell->Effects[triggeredByAura->GetEffIndex()].TriggerSpell; - - // processed charge only counting case - if (!triggered_spell_id) - return true; - - SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id); - if (!triggerEntry) - { - TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: Spell %u has non-existing triggered spell %u", dummySpell->Id, triggered_spell_id); - return false; - } - - if (basepoints0) - CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura, originalCaster); - else - CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura, originalCaster); - - return true; -} - -// Used in case when access to whole aura is needed -// All procs should be handled like this... -bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 /*procFlag*/, uint32 procEx, bool* handled) -{ - SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo(); - - switch (dummySpell->SpellFamilyName) - { - case SPELLFAMILY_GENERIC: - switch (dummySpell->Id) - { - // Nevermelting Ice Crystal - case 71564: - RemoveAuraFromStack(71564); - *handled = true; - break; - // Gaseous Bloat - case 70672: - case 72455: - case 72832: - case 72833: - { - *handled = true; - uint32 stack = triggeredByAura->GetStackAmount(); - int32 const mod = (GetMap()->GetSpawnMode() & 1) ? 1500 : 1250; - int32 dmg = 0; - for (uint8 i = 1; i <= stack; ++i) - dmg += mod * i; - if (Unit* caster = triggeredByAura->GetCaster()) - caster->CastCustomSpell(70701, SPELLVALUE_BASE_POINT0, dmg); - break; - } - // Ball of Flames Proc - case 71756: - case 72782: - case 72783: - case 72784: - RemoveAuraFromStack(dummySpell->Id); - *handled = true; - break; - // Discerning Eye of the Beast - case 59915: - { - CastSpell(this, 59914, true); // 59914 already has correct basepoints in DBC, no need for custom bp - *handled = true; - break; - } - // Swift Hand of Justice - case 59906: - { - int32 bp0 = CalculatePct(GetMaxHealth(), dummySpell->Effects[EFFECT_0]. CalcValue()); - CastCustomSpell(this, 59913, &bp0, NULL, NULL, true); - *handled = true; - break; - } - } - - break; - case SPELLFAMILY_PALADIN: - { - // Infusion of Light - if (procSpell && dummySpell->SpellIconID == 3021) - { - // Flash of Light HoT on Flash of Light when Sacred Shield active - if (procSpell->SpellFamilyFlags[0] & 0x40000000 && procSpell->SpellIconID == 242) - { - *handled = true; - if (victim && victim->HasAura(53601)) - { - int32 bp0 = CalculatePct(int32(damage / 12), dummySpell->Effects[EFFECT_2].CalcValue()); - // Item - Paladin T9 Holy 4P Bonus - if (AuraEffect const* aurEff = GetAuraEffect(67191, 0)) - AddPct(bp0, aurEff->GetAmount()); - CastCustomSpell(victim, 66922, &bp0, NULL, NULL, true); - return true; - } - } - // but should not proc on non-critical Holy Shocks - else if ((procSpell->SpellFamilyFlags[0] & 0x200000 || procSpell->SpellFamilyFlags[1] & 0x10000) && !(procEx & PROC_EX_CRITICAL_HIT)) - *handled = true; - break; - } - // Judgements of the Just - else if (dummySpell->SpellIconID == 3015) - { - *handled = true; - CastSpell(victim, 68055, true); - return true; - } - // Glyph of Divinity - else if (dummySpell->Id == 54939) - { - if (!procSpell) - return false; - *handled = true; - // Check if we are the target and prevent mana gain - if (victim && triggeredByAura->GetCasterGUID() == victim->GetGUID()) - return false; - // Lookup base amount mana restore - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; i++) - { - if (procSpell->Effects[i].Effect == SPELL_EFFECT_ENERGIZE) - { - // value multiplied by 2 because you should get twice amount - int32 mana = procSpell->Effects[i].CalcValue() * 2; - CastCustomSpell(this, 54986, 0, &mana, NULL, true); - } - } - return true; - } - break; - } - case SPELLFAMILY_MAGE: - { - // Combustion - switch (dummySpell->Id) - { - case 11129: - { - *handled = true; - Unit* caster = triggeredByAura->GetCaster(); - if (!caster || !damage) - return false; - - // last charge and crit - if (triggeredByAura->GetCharges() <= 1 && (procEx & PROC_EX_CRITICAL_HIT)) - return true; // charge counting (will removed) - - CastSpell(this, 28682, true); - - return (procEx & PROC_EX_CRITICAL_HIT) != 0; - } - // Empowered Fire - case 31656: - case 31657: - case 31658: - { - *handled = true; - - SpellInfo const* spInfo = sSpellMgr->GetSpellInfo(67545); - if (!spInfo) - return false; - - int32 bp0 = int32(CalculatePct(GetCreateMana(), spInfo->Effects[0].CalcValue())); - CastCustomSpell(this, 67545, &bp0, NULL, NULL, true, NULL, triggeredByAura->GetEffect(EFFECT_0), GetGUID()); - return true; - } - case 44401: //Missile Barrage - case 48108: //Hot Streak - case 57761: //Fireball! - { - *handled = true; - - // Prevent double proc for Arcane missiles - if (this == victim) - return false; - - if (HasAura(70752)) // Item - Mage T10 2P Bonus - CastSpell((Unit*)nullptr, 70753, true); - - // Proc chance is unknown, we'll just use dummy aura amount - if (AuraEffect const* aurEff = GetAuraEffect(64869, EFFECT_0)) // Item - Mage T8 4P Bonus - if (roll_chance_i(aurEff->GetAmount())) // do not proc charges - return false; - - // @workaround: We'll take care of removing the aura on next update tick - // This is needed for 44401 to affect Arcane Missiles, else the spellmod will not be applied - // it only works because EventProcessor will always update first scheduled event, - // as cast is already in progress the SpellEvent for Arcane Missiles is already in queue. - triggeredByAura->DropChargeDelayed(1); - return false; - } - } - break; - } - case SPELLFAMILY_DEATHKNIGHT: - { - // Blood of the North - // Reaping - // Death Rune Mastery - /// @todo move those to spell scripts - if (dummySpell->SpellIconID == 3041 || (dummySpell->SpellIconID == 22 && dummySpell->Id != 62459) || dummySpell->SpellIconID == 2622) - { - *handled = true; - // Convert recently used Blood Rune to Death Rune - if (Player* player = ToPlayer()) - { - if (player->getClass() != CLASS_DEATH_KNIGHT) - return false; - - RuneType rune = ToPlayer()->GetLastUsedRune(); - // can't proc from death rune use - if (rune == RUNE_DEATH) - return false; - AuraEffect* aurEff = triggeredByAura->GetEffect(EFFECT_0); - if (!aurEff) - return false; - - // Reset amplitude - set death rune remove timer to 30s - aurEff->ResetPeriodic(true); - uint32 runesLeft; - - if (dummySpell->SpellIconID == 2622) - runesLeft = 2; - else - runesLeft = 1; - - for (uint8 i = 0; i < MAX_RUNES && runesLeft; ++i) - { - if (dummySpell->SpellIconID == 2622) - { - if (player->GetCurrentRune(i) == RUNE_DEATH || - player->GetBaseRune(i) == RUNE_BLOOD) - continue; - } - else - { - if (player->GetCurrentRune(i) == RUNE_DEATH || - player->GetBaseRune(i) != RUNE_BLOOD) - continue; - } - if (player->GetRuneCooldown(i) != (player->GetRuneBaseCooldown(i) - player->GetLastRuneGraceTimer(i))) - continue; - - --runesLeft; - // Mark aura as used - player->AddRuneByAuraEffect(i, RUNE_DEATH, aurEff); - } - return true; - } - return false; - } - - switch (dummySpell->Id) - { - // Bone Shield cooldown - case 49222: - { - *handled = true; - return true; - } - // Hungering Cold aura drop - case 51209: - *handled = true; - // Drop only in not disease case - if (procSpell && procSpell->Dispel == DISPEL_DISEASE) - return false; - return true; - } - break; - } - case SPELLFAMILY_WARRIOR: - { - switch (dummySpell->Id) - { - // Item - Warrior T10 Protection 4P Bonus - case 70844: - { - int32 basepoints0 = CalculatePct(GetMaxHealth(), dummySpell->Effects[EFFECT_1]. CalcValue()); - CastCustomSpell(this, 70845, &basepoints0, NULL, NULL, true); - break; - } - // Recklessness - case 1719: - { - //! Possible hack alert - //! Don't drop charges on proc, they will be dropped on SpellMod removal - //! Before this change, it was dropping two charges per attack, one in ProcDamageAndSpellFor, and one in RemoveSpellMods. - //! The reason of this behaviour is Recklessness having three auras, 2 of them can not proc (isTriggeredAura array) but the other one can, making the whole spell proc. - *handled = true; - break; - } - default: - break; - } - break; - } - } - return false; -} - -bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx) -{ - // Get triggered aura spell info - SpellInfo const* auraSpellInfo = triggeredByAura->GetSpellInfo(); - - // Basepoints of trigger aura - int32 triggerAmount = triggeredByAura->GetAmount(); - - // Set trigger spell id, target, custom basepoints - uint32 trigger_spell_id = auraSpellInfo->Effects[triggeredByAura->GetEffIndex()].TriggerSpell; - - Unit* target = NULL; - int32 basepoints0 = 0; - - if (triggeredByAura->GetAuraType() == SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE) - basepoints0 = triggerAmount; - - Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER - ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; - - // Try handle unknown trigger spells - // triggered spells exists only in serverside spell_dbc - /// @todo: reverify and move these spells to spellscripts - { - switch (auraSpellInfo->SpellFamilyName) - { - case SPELLFAMILY_WARLOCK: - { - // Drain Soul - if (auraSpellInfo->SpellFamilyFlags[0] & 0x4000) - { - // Improved Drain Soul - Unit::AuraEffectList const& mAddFlatModifier = GetAuraEffectsByType(SPELL_AURA_DUMMY); - for (Unit::AuraEffectList::const_iterator i = mAddFlatModifier.begin(); i != mAddFlatModifier.end(); ++i) - { - if ((*i)->GetMiscValue() == SPELLMOD_CHANCE_OF_SUCCESS && (*i)->GetSpellInfo()->SpellIconID == 113) - { - int32 value2 = CalculateSpellDamage(this, (*i)->GetSpellInfo(), 2); - basepoints0 = int32(CalculatePct(GetMaxPower(POWER_MANA), value2)); - // Drain Soul - CastCustomSpell(this, 18371, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - break; - } - } - // Not remove charge (aura removed on death in any cases) - // Need for correct work Drain Soul SPELL_AURA_CHANNEL_DEATH_ITEM aura - return false; - } - } - default: - break; - } - } - - // All ok. Check current trigger spell - SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(trigger_spell_id); - if (triggerEntry == NULL) - { - // Don't cast unknown spell - TC_LOG_ERROR("entities.unit.handleproctriggerspell", "Unit::HandleProcTriggerSpell: Spell %u (effIndex: %u) has unknown TriggerSpell %u. Unhandled custom case?", auraSpellInfo->Id, triggeredByAura->GetEffIndex(), trigger_spell_id); - return false; - } - - // not allow proc extra attack spell at extra attack - if (m_extraAttacks && triggerEntry->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) - return false; - - // Custom requirements (not listed in procEx) Warning! damage dealing after this - // Custom triggered spells - switch (auraSpellInfo->Id) - { - // Deep Wounds - case 12834: - case 12849: - case 12867: - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - float averageDmg = 0; - // now compute approximate weapon damage by formula from wowwiki.com - if (procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) - averageDmg = (GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE) + GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE)) / 2.f; - else - averageDmg = (GetFloatValue(UNIT_FIELD_MINDAMAGE) + GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2.f; - - basepoints0 = int32(averageDmg); - break; - } - // Persistent Shield (Scarab Brooch trinket) - // This spell originally trigger 13567 - Dummy Trigger (vs dummy efect) - case 26467: - { - basepoints0 = int32(CalculatePct(damage, 15)); - target = victim; - trigger_spell_id = 26470; - break; - } - // Unyielding Knights (item exploit 29108\29109) - case 38164: - { - if (!victim || victim->GetEntry() != 19457) // Proc only if your target is Grillok - return false; - break; - } - // Deflection - case 52420: - { - if (!HealthBelowPct(35)) - return false; - break; - } - - // Cheat Death - case 28845: - { - // When your health drops below 20% - if (HealthBelowPctDamaged(20, damage) || HealthBelowPct(20)) - return false; - break; - } - // Deadly Swiftness (Rank 1) - case 31255: - { - // whenever you deal damage to a target who is below 20% health. - if (!victim || !victim->IsAlive() || victim->HealthAbovePct(20)) - return false; - - target = this; - trigger_spell_id = 22588; - } - // Greater Heal Refund (Avatar Raiment set) - case 37594: - { - if (!victim || !victim->IsAlive()) - return false; - - // Doesn't proc if target already has full health - if (victim->IsFullHealth()) - return false; - // If your Greater Heal brings the target to full health, you gain $37595s1 mana. - if (victim->GetHealth() + damage < victim->GetMaxHealth()) - return false; - break; - } - // Bonus Healing (Crystal Spire of Karabor mace) - case 40971: - { - // If your target is below $s1% health - if (!victim || !victim->IsAlive() || victim->HealthAbovePct(triggerAmount)) - return false; - break; - } - // Rapid Recuperation - case 53228: - case 53232: - { - // This effect only from Rapid Fire (ability cast) - if (!procSpell || !(procSpell->SpellFamilyFlags[0] & 0x20)) - return false; - break; - } - // Decimation - case 63156: - case 63158: - // Can proc only if target has hp below 35% - if (!victim || !victim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, procSpell, this)) - return false; - break; - // Deathbringer Saurfang - Blood Beast's Blood Link - case 72176: - basepoints0 = 3; - break; - case 15337: // Improved Spirit Tap (Rank 1) - case 15338: // Improved Spirit Tap (Rank 2) - { - if (!procSpell) - return false; - - if (procSpell->SpellFamilyFlags[0] & 0x800000) - if ((procSpell->Id != 58381) || !roll_chance_i(50)) - return false; - - target = victim; - break; - } - // Professor Putricide - Ooze Spell Tank Protection - case 71770: - if (victim) - victim->CastSpell(victim, trigger_spell_id, true); // EffectImplicitTarget is self - return true; - case 45057: // Evasive Maneuvers (Commendation of Kael`thas trinket) - case 71634: // Item - Icecrown 25 Normal Tank Trinket 1 - case 71640: // Item - Icecrown 25 Heroic Tank Trinket 1 - case 75475: // Item - Chamber of Aspects 25 Normal Tank Trinket - case 75481: // Item - Chamber of Aspects 25 Heroic Tank Trinket - { - // Procs only if damage takes health below $s1% - if (!HealthBelowPctDamaged(triggerAmount, damage)) - return false; - break; - } - default: - break; - } - - // Blade Barrier - if (auraSpellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && auraSpellInfo->SpellIconID == 85 && procSpell) - { - Player* player = ToPlayer(); - if (!player || player->getClass() != CLASS_DEATH_KNIGHT) - return false; - - if (!player->IsBaseRuneSlotsOnCooldown(RUNE_BLOOD)) - return false; - } - - // Rime - else if (auraSpellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && auraSpellInfo->SpellIconID == 56) - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - // Howling Blast - GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool - { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); - return spellInfo && spellInfo->GetCategory() == 1248; - }, true); - } - - // Custom basepoints/target for exist spell - // dummy basepoints or other customs - switch (trigger_spell_id) - { - // Auras which should proc on area aura source (caster in this case): - // Turn the Tables - case 52914: - case 52915: - case 52910: - { - target = triggeredByAura->GetBase()->GetCaster(); - if (!target) - return false; - - target->CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura); - return true; - } - // Cast positive spell on enemy target - case 7099: // Curse of Mending - case 39703: // Curse of Mending - case 29494: // Temptation - case 20233: // Improved Lay on Hands (cast on target) - { - target = victim; - break; - } - // Combo points add triggers (need add combopoint only for main target, and after possible combopoints reset) - case 15250: // Rogue Setup - { - // applied only for main target - if (!victim || (GetTypeId() == TYPEID_PLAYER && victim != ToPlayer()->GetSelectedUnit())) - return false; - break; // continue normal case - } - // Finish movies that add combo - case 14189: // Seal Fate (Netherblade set) - case 14157: // Ruthlessness - { - if (!victim || victim == this) - return false; - // Need add combopoint AFTER finish movie (or they dropped in finish phase) - break; - } - // Item - Druid T10 Balance 2P Bonus - case 16870: - { - if (HasAura(70718)) - CastSpell(this, 70721, true); - break; - } - // Shamanistic Rage triggered spell - case 30824: - { - basepoints0 = int32(CalculatePct(GetTotalAttackPowerValue(BASE_ATTACK), triggerAmount)); - break; - } - // Enlightenment (trigger only from mana cost spells) - case 35095: - { - if (!procSpell || procSpell->PowerType != POWER_MANA || (procSpell->ManaCost == 0 && procSpell->ManaCostPercentage == 0 && procSpell->ManaCostPerlevel == 0)) - return false; - break; - } - // Demonic Pact - case 48090: - { - // Get talent aura from owner - if (IsPet()) - if (Unit* owner = GetOwner()) - { - if (AuraEffect* aurEff = owner->GetDummyAuraEffect(SPELLFAMILY_WARLOCK, 3220, 0)) - { - basepoints0 = int32((aurEff->GetAmount() * owner->SpellBaseDamageBonusDone(SpellSchoolMask(SPELL_SCHOOL_MASK_MAGIC)) + 100.0f) / 100.0f); /// @todo Is it right? - CastCustomSpell(this, trigger_spell_id, &basepoints0, &basepoints0, NULL, true, castItem, triggeredByAura); - return true; - } - } - break; - } - case 46916: // Slam! (Bloodsurge proc) - case 52437: // Sudden Death - { - // Item - Warrior T10 Melee 4P Bonus - if (AuraEffect const* aurEff = GetAuraEffect(70847, 0)) - { - if (!roll_chance_i(aurEff->GetAmount())) - break; - CastSpell(this, 70849, true, castItem, triggeredByAura); // Extra Charge! - CastSpell(this, 71072, true, castItem, triggeredByAura); // Slam GCD Reduced - CastSpell(this, 71069, true, castItem, triggeredByAura); // Execute GCD Reduced - } - break; - } - // Sword and Board - case 50227: - { - // Remove cooldown on Shield Slam - GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool - { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); - return spellInfo && spellInfo->GetCategory() == 1209; - }, true); - break; - } - // Maelstrom Weapon - case 53817: - { - // has rank dependant proc chance, ignore too often cases - // PPM = 2.5 * (rank of talent), - uint32 rank = auraSpellInfo->GetRank(); - // 5 rank -> 100% 4 rank -> 80% and etc from full rate - if (!roll_chance_i(20*rank)) - return false; - // Item - Shaman T10 Enhancement 4P Bonus - if (AuraEffect const* aurEff = GetAuraEffect(70832, 0)) - if (Aura const* maelstrom = GetAura(53817)) - if ((maelstrom->GetStackAmount() == maelstrom->GetSpellInfo()->StackAmount) && roll_chance_i(aurEff->GetAmount())) - CastSpell(this, 70831, true, castItem, triggeredByAura); - break; - } - // Astral Shift - case 52179: - { - if (!procSpell || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == victim) - return false; - - // Need stun, fear or silence mechanic - if (!(procSpell->GetAllEffectsMechanicMask() & ((1<<MECHANIC_SILENCE)|(1<<MECHANIC_STUN)|(1<<MECHANIC_FEAR)))) - return false; - break; - } - // Burning Determination - case 54748: - { - if (!procSpell) - return false; - // Need Interrupt or Silenced mechanic - if (!(procSpell->GetAllEffectsMechanicMask() & ((1<<MECHANIC_INTERRUPT)|(1<<MECHANIC_SILENCE)))) - return false; - break; - } - // Glyph of Death's Embrace - case 58679: - { - // Proc only from healing part of Death Coil. Check is essential as all Death Coil spells have 0x2000 mask in SpellFamilyFlags - if (!procSpell || !(procSpell->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && procSpell->SpellFamilyFlags[0] == 0x80002000)) - return false; - break; - } - // Glyph of Death Grip - case 58628: - { - // remove cooldown of Death Grip - GetSpellHistory()->ResetCooldown(49576, true); - return true; - } - // Savage Defense - case 62606: - { - basepoints0 = CalculatePct(triggerAmount, GetTotalAttackPowerValue(BASE_ATTACK)); - break; - } - // Body and Soul - case 64128: - case 65081: - { - // Proc only from PW:S cast - if (!procSpell || !(procSpell->SpellFamilyFlags[0] & 0x00000001)) - return false; - break; - } - // Culling the Herd - case 70893: - { - if (!procSpell) - return false; - // check if we're doing a critical hit - if (!(procSpell->SpellFamilyFlags[1] & 0x10000000) && (procEx != PROC_EX_CRITICAL_HIT)) - return false; - // check if we're procced by Claw, Bite or Smack (need to use the spell icon ID to detect it) - if (!(procSpell->SpellIconID == 262 || procSpell->SpellIconID == 1680 || procSpell->SpellIconID == 473)) - return false; - break; - } - } - - // extra attack should hit same target - if (triggerEntry->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) - target = victim; - - // try detect target manually if not set - if (target == NULL) - target = !(procFlag & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)) && triggerEntry->IsPositive() ? this : victim; - - if (basepoints0) - CastCustomSpell(target, trigger_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - else - CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura); - - return true; -} - -bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* procSpell) -{ - int32 scriptId = triggeredByAura->GetMiscValue(); - - if (!victim || !victim->IsAlive()) - return false; - - Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER - ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; - - uint32 triggered_spell_id = 0; - - switch (scriptId) - { - case 836: // Improved Blizzard (Rank 1) - { - if (!procSpell || procSpell->SpellVisual[0] != 9487) - return false; - triggered_spell_id = 12484; - break; - } - case 988: // Improved Blizzard (Rank 2) - { - if (!procSpell || procSpell->SpellVisual[0] != 9487) - return false; - triggered_spell_id = 12485; - break; - } - case 989: // Improved Blizzard (Rank 3) - { - if (!procSpell || procSpell->SpellVisual[0] != 9487) - return false; - triggered_spell_id = 12486; - break; - } - case 4533: // Dreamwalker Raiment 2 pieces bonus - { - // Chance 50% - if (!roll_chance_i(50)) - return false; - - switch (victim->getPowerType()) - { - case POWER_MANA: triggered_spell_id = 28722; break; - case POWER_RAGE: triggered_spell_id = 28723; break; - case POWER_ENERGY: triggered_spell_id = 28724; break; - default: - return false; - } - break; - } - case 4537: // Dreamwalker Raiment 6 pieces bonus - triggered_spell_id = 28750; // Blessing of the Claw - break; - case 5497: // Improved Mana Gems - triggered_spell_id = 37445; // Mana Surge - break; - case 7010: // Revitalize - can proc on full hp target - case 7011: - case 7012: - { - if (!roll_chance_i(triggeredByAura->GetAmount())) - return false; - switch (victim->getPowerType()) - { - case POWER_MANA: triggered_spell_id = 48542; break; - case POWER_RAGE: triggered_spell_id = 48541; break; - case POWER_ENERGY: triggered_spell_id = 48540; break; - case POWER_RUNIC_POWER: triggered_spell_id = 48543; break; - default: - break; - } - break; - } - default: - break; - } - - // not processed - if (!triggered_spell_id) - return false; - - // standard non-dummy case - SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id); - - if (!triggerEntry) - { - TC_LOG_ERROR("entities.unit", "Unit::HandleOverrideClassScriptAuraProc: Spell %u triggering for class script id %u", triggered_spell_id, scriptId); - return false; - } - - CastSpell(victim, triggered_spell_id, true, castItem, triggeredByAura); - return true; -} - void Unit::setPowerType(Powers new_powertype) { if (getPowerType() == new_powertype) @@ -8335,6 +5478,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; } @@ -8390,8 +5535,7 @@ ReputationRank Unit::GetReactionTo(Unit const* target) const } // check FFA_PVP - if (GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_FFA_PVP - && target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_FFA_PVP) + if (IsFFAPvP() && target->IsFFAPvP()) return REP_HOSTILE; if (selfPlayerOwner) @@ -8623,7 +5767,7 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) } // delay offhand weapon attack to next attack time - if (haveOffhandWeapon()) + if (haveOffhandWeapon() && GetTypeId() != TYPEID_PLAYER) resetAttackTimer(OFF_ATTACK); if (meleeAttack) @@ -8769,17 +5913,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; } } } @@ -8789,9 +5930,9 @@ uint32 Unit::BuildAuraStateUpdateForTarget(Unit* target) const { uint32 auraStates = GetUInt32Value(UNIT_FIELD_AURASTATE) &~(PER_CASTER_AURA_STATE_MASK); for (AuraStateAurasMap::const_iterator itr = m_auraStateAuras.begin(); itr != m_auraStateAuras.end(); ++itr) - if ((1<<(itr->first-1)) & PER_CASTER_AURA_STATE_MASK) + if ((1 << (itr->first - 1)) & PER_CASTER_AURA_STATE_MASK) if (itr->second->GetBase()->GetCasterGUID() == target->GetGUID()) - auraStates |= (1<<(itr->first-1)); + auraStates |= (1 << (itr->first - 1)); return auraStates; } @@ -8803,13 +5944,14 @@ bool Unit::HasAuraState(AuraStateType flag, SpellInfo const* spellProto, Unit co if (spellProto) { AuraEffectList const& stateAuras = Caster->GetAuraEffectsByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE); - for (AuraEffectList::const_iterator j = stateAuras.begin(); j != stateAuras.end(); ++j) - if ((*j)->IsAffectedOnSpell(spellProto)) + for (AuraEffect const* aurEff : stateAuras) + if (aurEff->IsAffectedOnSpell(spellProto)) return true; } + // Check per caster aura state // If aura with aurastate by caster not found return false - if ((1<<(flag-1)) & PER_CASTER_AURA_STATE_MASK) + if ((1 << (flag - 1)) & PER_CASTER_AURA_STATE_MASK) { AuraStateAurasMapBounds range = m_auraStateAuras.equal_range(flag); for (AuraStateAurasMap::const_iterator itr = range.first; itr != range.second; ++itr) @@ -8819,7 +5961,7 @@ bool Unit::HasAuraState(AuraStateType flag, SpellInfo const* spellProto, Unit co } } - return HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)); + return HasFlag(UNIT_FIELD_AURASTATE, 1 << (flag - 1)); } void Unit::SetOwnerGUID(ObjectGuid owner) @@ -9193,9 +6335,11 @@ void Unit::SetCharm(Unit* charm, bool apply) } } -int32 Unit::DealHeal(Unit* victim, uint32 addhealth) +void Unit::DealHeal(HealInfo& healInfo) { int32 gain = 0; + Unit* victim = healInfo.GetTarget(); + uint32 addhealth = healInfo.GetHeal(); if (victim->IsAIEnabled) victim->GetAI()->HealReceived(this, addhealth); @@ -9232,7 +6376,8 @@ int32 Unit::DealHeal(Unit* victim, uint32 addhealth) player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED, addhealth); } - return gain; + if (gain) + healInfo.SetEffectiveHeal(gain > 0 ? static_cast<uint32>(gain) : 0UL); } bool Unit::IsMagnet() const @@ -9445,30 +6590,29 @@ void Unit::UnsummonAllTotems() } } -void Unit::SendHealSpellLog(Unit* victim, uint32 SpellID, uint32 Damage, uint32 OverHeal, uint32 Absorb, bool critical) +void Unit::SendHealSpellLog(HealInfo& healInfo, bool critical /*= false*/) { // we guess size WorldPacket data(SMSG_SPELLHEALLOG, 8 + 8 + 4 + 4 + 4 + 4 + 1 + 1); - data << victim->GetPackGUID(); - data << GetPackGUID(); - data << uint32(SpellID); - data << uint32(Damage); - data << uint32(OverHeal); - data << uint32(Absorb); // Absorb amount + data << healInfo.GetTarget()->GetPackGUID(); + data << healInfo.GetHealer()->GetPackGUID(); + data << uint32(healInfo.GetSpellInfo()->Id); + data << uint32(healInfo.GetHeal()); + data << uint32(healInfo.GetHeal() - healInfo.GetEffectiveHeal()); + data << uint32(healInfo.GetAbsorb()); // Absorb amount data << uint8(critical ? 1 : 0); data << uint8(0); // unused SendMessageToSet(&data, true); } -int32 Unit::HealBySpell(Unit* victim, SpellInfo const* spellInfo, uint32 addHealth, bool critical) +int32 Unit::HealBySpell(HealInfo& healInfo, bool critical /*= false*/) { - uint32 absorb = 0; // calculate heal absorb and reduce healing - CalcHealAbsorb(victim, spellInfo, addHealth, absorb); + CalcHealAbsorb(healInfo); - int32 gain = DealHeal(victim, addHealth); - SendHealSpellLog(victim, spellInfo->Id, addHealth, uint32(addHealth - gain), absorb, critical); - return gain; + DealHeal(healInfo); + SendHealSpellLog(healInfo, critical); + return healInfo.GetEffectiveHeal(); } void Unit::SendEnergizeSpellLog(Unit* victim, uint32 spellId, int32 damage, Powers powerType) @@ -9541,11 +6685,12 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin // Impurity (dummy effect) if (GetTypeId() == TYPEID_PLAYER) { - PlayerSpellMap playerSpells = ToPlayer()->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = playerSpells.begin(); itr != playerSpells.end(); ++itr) + PlayerSpellMap const& playerSpells = ToPlayer()->GetSpellMap(); + for (auto itr = playerSpells.begin(); itr != playerSpells.end(); ++itr) { if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled) continue; + switch (itr->first) { case 49220: @@ -9554,7 +6699,7 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin case 49636: case 49638: if (SpellInfo const* proto = sSpellMgr->GetSpellInfo(itr->first)) - AddPct(ApCoeffMod, proto->Effects[0].CalcValue()); + AddPct(ApCoeffMod, proto->Effects[EFFECT_0].CalcValue()); break; } } @@ -9570,33 +6715,28 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin DoneAdvertisedBenefit += ((Guardian*)this)->GetBonusDamage(); // Check for table values - float coeff = 0; + float coeff = 0.0f; SpellBonusEntry const* bonus = sSpellMgr->GetSpellBonusData(spellProto->Id); if (bonus) { + WeaponAttackType attType = (spellProto->IsRangedWeaponSpell() && spellProto->DmgClass != SPELL_DAMAGE_CLASS_MELEE) ? RANGED_ATTACK : BASE_ATTACK; + float APbonus = float(victim->GetTotalAuraModifier(attType == BASE_ATTACK ? SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS : SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS)); + APbonus += GetTotalAttackPowerValue(attType); + if (damagetype == DOT) { coeff = bonus->dot_damage; if (bonus->ap_dot_bonus > 0) - { - WeaponAttackType attType = (spellProto->IsRangedWeaponSpell() && spellProto->DmgClass != SPELL_DAMAGE_CLASS_MELEE) ? RANGED_ATTACK : BASE_ATTACK; - float APbonus = float(victim->GetTotalAuraModifier(attType == BASE_ATTACK ? SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS : SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS)); - APbonus += GetTotalAttackPowerValue(attType); DoneTotal += int32(bonus->ap_dot_bonus * stack * ApCoeffMod * APbonus); - } } else { coeff = bonus->direct_damage; if (bonus->ap_bonus > 0) - { - WeaponAttackType attType = (spellProto->IsRangedWeaponSpell() && spellProto->DmgClass != SPELL_DAMAGE_CLASS_MELEE) ? RANGED_ATTACK : BASE_ATTACK; - float APbonus = float(victim->GetTotalAuraModifier(attType == BASE_ATTACK ? SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS : SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS)); - APbonus += GetTotalAttackPowerValue(attType); DoneTotal += int32(bonus->ap_bonus * stack * ApCoeffMod * APbonus); - } } } + // Default calculation if (DoneAdvertisedBenefit) { @@ -9608,17 +6748,24 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin if (Player* modOwner = GetSpellModOwner()) { coeff *= 100.0f; - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff); + modOwner->ApplySpellMod<SPELLMOD_BONUS_MULTIPLIER>(spellProto->Id, coeff); coeff /= 100.0f; } DoneTotal += int32(DoneAdvertisedBenefit * coeff * factorMod); } + float tmpDamage = (int32(pdamage) + DoneTotal); + + // DOTs calculated in AuraEffect::PeriodicDamageAurasTick // Done Percentage for DOT is already calculated, no need to do it again. The percentage mod is applied in Aura::HandleAuraSpecificMods. - float tmpDamage = (int32(pdamage) + DoneTotal) * (damagetype == DOT ? 1.0f : SpellDamagePctDone(victim, spellProto, damagetype)); - // apply spellmod to Done damage (flat and pct) - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage); + if (damagetype != DOT) + { + tmpDamage *= SpellDamagePctDone(victim, spellProto, damagetype); + + // apply spellmod to Done damage (flat and pct) + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod<SPELLMOD_DAMAGE>(spellProto->Id, tmpDamage); + } return uint32(std::max(tmpDamage, 0.0f)); } @@ -9643,22 +6790,17 @@ float Unit::SpellDamagePctDone(Unit* victim, SpellInfo const* spellProto, Damage if (GetTypeId() == TYPEID_UNIT && !IsPet()) DoneTotalMod *= ToCreature()->GetSpellDamageMod(ToCreature()->GetCreatureTemplate()->rank); - AuraEffectList const& mModDamagePercentDone = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - for (AuraEffectList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i) + float maxModDamagePercentSchool = 0.0f; + if (GetTypeId() == TYPEID_PLAYER) { - if (spellProto->EquippedItemClass == -1 && (*i)->GetSpellInfo()->EquippedItemClass != -1) //prevent apply mods from weapon specific case to non weapon specific spells (Example: thunder clap and two-handed weapon specialization) - continue; - - if ((*i)->GetMiscValue() & spellProto->GetSchoolMask()) - { - if ((*i)->GetSpellInfo()->EquippedItemClass == -1) - AddPct(DoneTotalMod, (*i)->GetAmount()); - else if (!(*i)->GetSpellInfo()->HasAttribute(SPELL_ATTR5_SPECIAL_ITEM_CLASS_CHECK) && ((*i)->GetSpellInfo()->EquippedItemSubClassMask == 0)) - AddPct(DoneTotalMod, (*i)->GetAmount()); - else if (ToPlayer() && ToPlayer()->HasItemFitToSpellRequirements((*i)->GetSpellInfo())) - AddPct(DoneTotalMod, (*i)->GetAmount()); - } + for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i) + if (spellProto->GetSchoolMask() & (1 << i)) + maxModDamagePercentSchool = std::max(maxModDamagePercentSchool, GetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT + i)); } + else + maxModDamagePercentSchool = GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, spellProto->GetSchoolMask()); + + DoneTotalMod *= maxModDamagePercentSchool; uint32 creatureTypeMask = victim->GetCreatureTypeMask(); @@ -10007,7 +7149,7 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui if (Player* modOwner = GetSpellModOwner()) { coeff *= 100.0f; - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff); + modOwner->ApplySpellMod<SPELLMOD_BONUS_MULTIPLIER>(spellProto->Id, coeff); coeff /= 100.0f; } TakenTotal += int32(TakenAdvertisedBenefit * coeff * factorMod); @@ -10039,13 +7181,9 @@ int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) const int32 DoneAdvertisedBenefit = 0; AuraEffectList const& mDamageDone = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE); - for (AuraEffectList::const_iterator i = mDamageDone.begin(); i != mDamageDone.end(); ++i) - if (((*i)->GetMiscValue() & schoolMask) != 0 && - (*i)->GetSpellInfo()->EquippedItemClass == -1 && - // -1 == any item class (not wand then) - (*i)->GetSpellInfo()->EquippedItemInventoryTypeMask == 0) - // 0 == any inventory type (not wand then) - DoneAdvertisedBenefit += (*i)->GetAmount(); + for (AuraEffect const* aurEff : mDamageDone) + if (aurEff->GetMiscValue() & schoolMask) + DoneAdvertisedBenefit += aurEff->GetAmount(); if (GetTypeId() == TYPEID_PLAYER) { @@ -10054,22 +7192,20 @@ int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) const // Damage bonus from stats AuraEffectList const& mDamageDoneOfStatPercent = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT); - for (AuraEffectList::const_iterator i = mDamageDoneOfStatPercent.begin(); i != mDamageDoneOfStatPercent.end(); ++i) + for (AuraEffect const* aurEff : mDamageDoneOfStatPercent) { - if ((*i)->GetMiscValue() & schoolMask) + if ((aurEff->GetMiscValue() & schoolMask) != 0) { // stat used stored in miscValueB for this aura - Stats usedStat = Stats((*i)->GetMiscValueB()); - DoneAdvertisedBenefit += int32(CalculatePct(GetStat(usedStat), (*i)->GetAmount())); + Stats const usedStat = static_cast<Stats>(aurEff->GetMiscValueB()); + DoneAdvertisedBenefit += static_cast<int32>(CalculatePct(GetStat(usedStat), aurEff->GetAmount())); } } - // ... and attack power - AuraEffectList const& mDamageDonebyAP = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER); - for (AuraEffectList::const_iterator i =mDamageDonebyAP.begin(); i != mDamageDonebyAP.end(); ++i) - if ((*i)->GetMiscValue() & schoolMask) - DoneAdvertisedBenefit += int32(CalculatePct(GetTotalAttackPowerValue(BASE_ATTACK), (*i)->GetAmount())); + // ... and attack power + DoneAdvertisedBenefit += static_cast<int32>(CalculatePct(GetTotalAttackPowerValue(BASE_ATTACK), GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER, schoolMask))); } + return DoneAdvertisedBenefit; } @@ -10139,37 +7275,37 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto crit_chance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE, schoolMask); // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE crit_chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); - ApplyResilience(victim, &crit_chance, NULL, false, CR_CRIT_TAKEN_SPELL); + ApplyResilience(victim, &crit_chance, nullptr, false, CR_CRIT_TAKEN_SPELL); } // scripted (increase crit chance ... against ... target by x% AuraEffectList const& mOverrideClassScript = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for (AuraEffectList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) + for (AuraEffect const* aurEff : mOverrideClassScript) { - if (!((*i)->IsAffectedOnSpell(spellProto))) + if (!aurEff->IsAffectedOnSpell(spellProto)) continue; - switch ((*i)->GetMiscValue()) + float modChance = 0.f; + switch (aurEff->GetMiscValue()) { + case 911: // Shatter (Rank 3) + modChance += 16.f; + case 910: // Shatter (Rank 2) + modChance += 17.f; case 849: // Shatter (Rank 1) - if (victim->HasAuraState(AURA_STATE_FROZEN, spellProto, this)) - crit_chance += 17; + modChance += 17.f; + if (!victim->HasAuraState(AURA_STATE_FROZEN, spellProto, this)) + break; + + crit_chance += modChance; break; - case 910: // Shatter (Rank 2) - if (victim->HasAuraState(AURA_STATE_FROZEN, spellProto, this)) - crit_chance += 34; - break; - case 911: // Shatter (Rank 3) - if (victim->HasAuraState(AURA_STATE_FROZEN, spellProto, this)) - crit_chance += 50; - break; case 7917: // Glyph of Shadowburn if (victim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, spellProto, this)) - crit_chance+=(*i)->GetAmount(); + crit_chance += aurEff->GetAmount(); break; case 7997: // Renewed Hope case 7998: if (victim->HasAura(6788)) - crit_chance+=(*i)->GetAmount(); + crit_chance += aurEff->GetAmount(); break; default: break; @@ -10236,6 +7372,13 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto } break; } + + // Spell crit suppression + if (victim->GetTypeId() == TYPEID_UNIT) + { + int32 const levelDiff = static_cast<int32>(victim->getLevelForTarget(this)) - getLevel(); + crit_chance -= levelDiff * 0.7f; + } } break; } @@ -10284,7 +7427,7 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto // percent done // only players use intelligence for critical chance computations if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance); + modOwner->ApplySpellMod<SPELLMOD_CRITICAL_CHANCE>(spellProto->Id, crit_chance); // for this types the bonus was already added in GetUnitCriticalChance, do not add twice if (spellProto->DmgClass != SPELL_DAMAGE_CLASS_MELEE && spellProto->DmgClass != SPELL_DAMAGE_CLASS_RANGED) @@ -10299,7 +7442,7 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto } } - return crit_chance > 0.0f ? crit_chance : 0.0f; + return std::max(crit_chance, 0.0f); } uint32 Unit::SpellCriticalDamageBonus(SpellInfo const* spellProto, uint32 damage, Unit* victim) @@ -10334,7 +7477,7 @@ uint32 Unit::SpellCriticalDamageBonus(SpellInfo const* spellProto, uint32 damage { // adds additional damage to critBonus (from talents) if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus); + modOwner->ApplySpellMod<SPELLMOD_CRIT_DAMAGE_BONUS>(spellProto->Id, crit_bonus); } crit_bonus += damage; @@ -10383,6 +7526,7 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui if (spellProto->SpellFamilyName == SPELLFAMILY_POTION) return healamount; + float ApCoeffMod = 1.0f; int32 DoneTotal = 0; // done scripted mod (take it from owner) @@ -10404,6 +7548,35 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui } } + // Custom scripted damage + switch (spellProto->SpellFamilyName) + { + case SPELLFAMILY_DEATHKNIGHT: + // Impurity (dummy effect) + if (GetTypeId() == TYPEID_PLAYER) + { + PlayerSpellMap const& playerSpells = ToPlayer()->GetSpellMap(); + for (auto itr = playerSpells.begin(); itr != playerSpells.end(); ++itr) + { + if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled) + continue; + + switch (itr->first) + { + case 49220: + case 49633: + case 49635: + case 49636: + case 49638: + if (SpellInfo const* proto = sSpellMgr->GetSpellInfo(itr->first)) + AddPct(ApCoeffMod, proto->Effects[EFFECT_0].CalcValue()); + break; + } + } + } + break; + } + // Done fixed damage bonus auras int32 DoneAdvertisedBenefit = SpellBaseHealingBonusDone(spellProto->GetSchoolMask()); @@ -10417,15 +7590,15 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui { coeff = bonus->dot_damage; if (bonus->ap_dot_bonus > 0) - DoneTotal += int32(bonus->ap_dot_bonus * stack * GetTotalAttackPowerValue( - (spellProto->IsRangedWeaponSpell() && spellProto->DmgClass !=SPELL_DAMAGE_CLASS_MELEE) ? RANGED_ATTACK : BASE_ATTACK)); + DoneTotal += int32(bonus->ap_dot_bonus * ApCoeffMod * stack * GetTotalAttackPowerValue( + (spellProto->IsRangedWeaponSpell() && spellProto->DmgClass != SPELL_DAMAGE_CLASS_MELEE) ? RANGED_ATTACK : BASE_ATTACK)); } else { coeff = bonus->direct_damage; if (bonus->ap_bonus > 0) - DoneTotal += int32(bonus->ap_bonus * stack * GetTotalAttackPowerValue( - (spellProto->IsRangedWeaponSpell() && spellProto->DmgClass !=SPELL_DAMAGE_CLASS_MELEE) ? RANGED_ATTACK : BASE_ATTACK)); + DoneTotal += int32(bonus->ap_bonus * ApCoeffMod * stack * GetTotalAttackPowerValue( + (spellProto->IsRangedWeaponSpell() && spellProto->DmgClass != SPELL_DAMAGE_CLASS_MELEE) ? RANGED_ATTACK : BASE_ATTACK)); } } else @@ -10446,7 +7619,7 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui if (Player* modOwner = GetSpellModOwner()) { coeff *= 100.0f; - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff); + modOwner->ApplySpellMod<SPELLMOD_BONUS_MULTIPLIER>(spellProto->Id, coeff); coeff /= 100.0f; } @@ -10471,11 +7644,18 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui DoneTotal = 0; } + float heal = float(int32(healamount) + DoneTotal); + + // DOTs calculated in AuraEffect::HandlePeriodicHealAurasTick // Done Percentage for DOT is already calculated, no need to do it again. The percentage mod is applied in Aura::HandleAuraSpecificMods. - float heal = float(int32(healamount) + DoneTotal) * (damagetype == DOT ? 1.0f : SpellHealingPctDone(victim, spellProto)); - // apply spellmod to Done amount - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, heal); + if (damagetype != DOT) + { + heal *= SpellHealingPctDone(victim, spellProto); + + // apply spellmod to Done amount + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod<SPELLMOD_DAMAGE>(spellProto->Id, heal); + } return uint32(std::max(heal, 0.0f)); } @@ -10621,7 +7801,7 @@ uint32 Unit::SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, u if (Player* modOwner = GetSpellModOwner()) { coeff *= 100.0f; - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff); + modOwner->ApplySpellMod<SPELLMOD_BONUS_MULTIPLIER>(spellProto->Id, coeff); coeff /= 100.0f; } @@ -10701,18 +7881,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; @@ -10720,23 +7900,27 @@ bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask) const bool Unit::IsImmunedToDamage(SpellInfo const* spellInfo) const { - if (spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) + if (!spellInfo) 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; - } + // for example 40175 + if (spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) && spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) + return false; + + if (spellInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) || spellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) + return false; + + 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; @@ -10748,29 +7932,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; @@ -10778,8 +7959,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; @@ -10790,13 +7969,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; @@ -10809,9 +7988,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; } @@ -10819,9 +7998,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; } @@ -10836,33 +8015,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; @@ -10913,15 +8094,8 @@ uint32 Unit::MeleeDamageBonusDone(Unit* victim, uint32 pdamage, WeaponAttackType if (APbonus != 0) // Can be negative { - bool normalized = false; - if (spellProto) - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (spellProto->Effects[i].Effect == SPELL_EFFECT_NORMALIZED_WEAPON_DMG) - { - normalized = true; - break; - } - DoneFlatBenefit += int32(APbonus/14.0f * GetAPMultiplier(attType, normalized)); + bool const normalized = spellProto && spellProto->HasEffect(SPELL_EFFECT_NORMALIZED_WEAPON_DMG); + DoneFlatBenefit += int32(APbonus / 14.0f * GetAPMultiplier(attType, normalized)); } // Done total percent damage auras @@ -10929,22 +8103,23 @@ uint32 Unit::MeleeDamageBonusDone(Unit* victim, uint32 pdamage, WeaponAttackType // Some spells don't benefit from pct done mods if (spellProto) - if (!spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS)) + { + // mods for SPELL_SCHOOL_MASK_NORMAL are already factored in base melee damage calculation + if (!spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS) && !(spellProto->GetSchoolMask() & SPELL_SCHOOL_MASK_NORMAL)) { - AuraEffectList const& mModDamagePercentDone = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - for (AuraEffectList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i) + float maxModDamagePercentSchool = 0.0f; + if (GetTypeId() == TYPEID_PLAYER) { - if ((*i)->GetMiscValue() & spellProto->GetSchoolMask() && !(spellProto->GetSchoolMask() & SPELL_SCHOOL_MASK_NORMAL)) - { - if ((*i)->GetSpellInfo()->EquippedItemClass == -1) - AddPct(DoneTotalMod, (*i)->GetAmount()); - else if (!(*i)->GetSpellInfo()->HasAttribute(SPELL_ATTR5_SPECIAL_ITEM_CLASS_CHECK) && ((*i)->GetSpellInfo()->EquippedItemSubClassMask == 0)) - AddPct(DoneTotalMod, (*i)->GetAmount()); - else if (ToPlayer() && ToPlayer()->HasItemFitToSpellRequirements((*i)->GetSpellInfo())) - AddPct(DoneTotalMod, (*i)->GetAmount()); - } + for (uint32 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) + if (spellProto->GetSchoolMask() & (1 << i)) + maxModDamagePercentSchool = std::max(maxModDamagePercentSchool, GetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT + i)); } + else + maxModDamagePercentSchool = GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, spellProto->GetSchoolMask()); + + DoneTotalMod *= maxModDamagePercentSchool; } + } AuraEffectList const& mDamageDoneVersus = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS); for (AuraEffectList::const_iterator i = mDamageDoneVersus.begin(); i != mDamageDoneVersus.end(); ++i) @@ -11037,7 +8212,7 @@ uint32 Unit::MeleeDamageBonusDone(Unit* victim, uint32 pdamage, WeaponAttackType // apply spellmod to Done damage if (spellProto) if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, tmpDamage); + modOwner->ApplySpellMod<SPELLMOD_DAMAGE>(spellProto->Id, tmpDamage); // bonus result can be negative return uint32(std::max(tmpDamage, 0.0f)); @@ -11168,52 +8343,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) - { - 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();) + auto bounds = m_spellImmune[op].equal_range(type); + for (auto itr = bounds.first; itr != bounds.second;) { - 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; } @@ -11240,7 +8377,7 @@ float Unit::GetPPMProcChance(uint32 WeaponSpeed, float PPM, const SpellInfo* spe // Apply chance modifer aura if (spellProto) if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_PROC_PER_MINUTE, PPM); + modOwner->ApplySpellMod<SPELLMOD_PROC_PER_MINUTE>(spellProto->Id, PPM); return std::floor((WeaponSpeed * PPM) / 600.0f); // result is chance in percents (probability = Speed_in_sec * (PPM / 60)) } @@ -11531,14 +8668,26 @@ 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; + // visibility checks + // skip visibility check for GO casts, needs removal when go cast is implemented. Also ignore for gameobject and dynauras + if (GetEntry() != WORLD_TRIGGER && (!obj || !obj->isType(TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT))) + { + // can't attack invisible + if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) + { + if (obj && !obj->CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea())) + return false; + else if (!obj) + { + // ignore stealth for aoe spells. Ignore stealth if target is player and unit in combat with same player + bool const ignoreStealthCheck = (bySpell && bySpell->IsAffectingArea()) || + (target->GetTypeId() == TYPEID_PLAYER && target->HasStealthAura() && target->IsInCombat() && IsInCombatWith(target)); - // 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))))) - return false; + if (!CanSeeOrDetect(target, ignoreStealthCheck)) + return false; + } + } + } // can't attack dead if ((!bySpell || !bySpell->IsAllowingDeadTarget()) && !target->IsAlive()) @@ -11569,7 +8718,7 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo // PvP, PvC, CvP case // can't attack friendly targets - if ( GetReactionTo(target) > REP_NEUTRAL + if (GetReactionTo(target) > REP_NEUTRAL || target->GetReactionTo(this) > REP_NEUTRAL) return false; @@ -11577,10 +8726,8 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo Player const* playerAffectingTarget = target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr; // Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar) - if ( - (playerAffectingAttacker && !playerAffectingTarget) || - (!playerAffectingAttacker && playerAffectingTarget) - ) + if ((playerAffectingAttacker && !playerAffectingTarget) || + (!playerAffectingAttacker && playerAffectingTarget)) { Player const* player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget; Unit const* creature = playerAffectingAttacker ? target : this; @@ -11614,15 +8761,14 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo // additional checks - only PvP case if (playerAffectingAttacker && playerAffectingTarget) { - if (target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_PVP) + if (target->IsPvP()) return true; - if (GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_FFA_PVP - && target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_FFA_PVP) + if (IsFFAPvP() && target->IsFFAPvP()) return true; - return (GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_UNK1) - || (target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_UNK1); + return HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK1) + || target->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK1); } return true; } @@ -12151,6 +9297,7 @@ void Unit::setDeathState(DeathState s) // do not why since in IncreaseMaxHealth currenthealth is checked SetHealth(0); SetPower(getPowerType(), 0); + SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); // players in instance don't have ZoneScript, but they have InstanceScript if (ZoneScript* zoneScript = GetZoneScript() ? GetZoneScript() : GetInstanceScript()) @@ -12419,17 +9566,17 @@ float Unit::ApplyEffectModifiers(SpellInfo const* spellProto, uint8 effect_index { if (Player* modOwner = GetSpellModOwner()) { - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_ALL_EFFECTS, value); + modOwner->ApplySpellMod<SPELLMOD_ALL_EFFECTS>(spellProto->Id, value); switch (effect_index) { - case 0: - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT1, value); + case EFFECT_0: + modOwner->ApplySpellMod<SPELLMOD_EFFECT1>(spellProto->Id, value); break; - case 1: - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT2, value); + case EFFECT_1: + modOwner->ApplySpellMod<SPELLMOD_EFFECT2>(spellProto->Id, value); break; - case 2: - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT3, value); + case EFFECT_2: + modOwner->ApplySpellMod<SPELLMOD_EFFECT3>(spellProto->Id, value); break; } } @@ -12572,7 +9719,7 @@ void Unit::ModSpellCastTime(SpellInfo const* spellInfo, int32 & castTime, Spell* // called from caster if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, castTime, spell); + modOwner->ApplySpellMod<SPELLMOD_CASTING_TIME>(spellInfo->Id, castTime, spell); if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) @@ -12593,7 +9740,7 @@ void Unit::ModSpellDurationTime(SpellInfo const* spellInfo, int32 & duration, Sp // called from caster if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, duration, spell); + modOwner->ApplySpellMod<SPELLMOD_CASTING_TIME>(spellInfo->Id, duration, spell); if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) @@ -12604,63 +9751,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; - - if (!i->hitTime) - return DIMINISHING_LEVEL_1; + DiminishingReturn& diminish = m_Diminishing[group]; + if (!diminish.hitCount) + 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; @@ -12669,7 +9806,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; @@ -12682,12 +9819,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; @@ -12705,24 +9842,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) @@ -13634,152 +10773,110 @@ bool Unit::isFrozen() const return HasAuraState(AURA_STATE_FROZEN); } -struct ProcTriggeredData -{ - ProcTriggeredData(Aura* _aura) - : aura(_aura) - { - effMask = 0; - spellProcEvent = NULL; - } - SpellProcEventEntry const* spellProcEvent; - Aura* aura; - uint32 effMask; -}; - -typedef std::list< ProcTriggeredData > ProcTriggeredList; - -// 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; - isNonTriggerAura[i] = false; - isAlwaysTriggeredAura[i] = false; - } - isTriggerAura[SPELL_AURA_DUMMY] = true; - isTriggerAura[SPELL_AURA_MOD_CONFUSE] = true; - isTriggerAura[SPELL_AURA_MOD_THREAT] = true; - 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_SCHOOL_ABSORB] = true; // Savage Defense untested - 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_MECHANIC_IMMUNITY] = true; - isTriggerAura[SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN] = true; - isTriggerAura[SPELL_AURA_SPELL_MAGNET] = 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_MECHANIC_RESISTANCE] = true; - isTriggerAura[SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS] = 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_DAMAGE_FROM_CASTER] = true; - isTriggerAura[SPELL_AURA_MOD_SPELL_CRIT_CHANCE] = true; - isTriggerAura[SPELL_AURA_ABILITY_IGNORE_AURASTATE] = true; - - isNonTriggerAura[SPELL_AURA_MOD_POWER_REGEN] = true; - isNonTriggerAura[SPELL_AURA_REDUCE_PUSHBACK] = 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; -} - -uint32 createProcExtendMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition) +uint32 createProcHitMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition) { - uint32 procEx = PROC_EX_NONE; + uint32 hitMask = PROC_HIT_NONE; // Check victim state if (missCondition != SPELL_MISS_NONE) + { switch (missCondition) { - case SPELL_MISS_MISS: procEx|=PROC_EX_MISS; break; - case SPELL_MISS_RESIST: procEx|=PROC_EX_RESIST; break; - case SPELL_MISS_DODGE: procEx|=PROC_EX_DODGE; break; - case SPELL_MISS_PARRY: procEx|=PROC_EX_PARRY; break; - case SPELL_MISS_BLOCK: procEx|=PROC_EX_BLOCK; break; - case SPELL_MISS_EVADE: procEx|=PROC_EX_EVADE; break; - case SPELL_MISS_IMMUNE: procEx|=PROC_EX_IMMUNE; break; - case SPELL_MISS_IMMUNE2: procEx|=PROC_EX_IMMUNE; break; - case SPELL_MISS_DEFLECT: procEx|=PROC_EX_DEFLECT;break; - case SPELL_MISS_ABSORB: procEx|=PROC_EX_ABSORB; break; - case SPELL_MISS_REFLECT: procEx|=PROC_EX_REFLECT;break; + case SPELL_MISS_MISS: + hitMask |= PROC_HIT_MISS; + break; + case SPELL_MISS_DODGE: + hitMask |= PROC_HIT_DODGE; + break; + case SPELL_MISS_PARRY: + hitMask |= PROC_HIT_PARRY; + break; + case SPELL_MISS_BLOCK: + // spells can't be partially blocked (it's damage can though) + hitMask |= PROC_HIT_BLOCK | PROC_HIT_FULL_BLOCK; + break; + case SPELL_MISS_EVADE: + hitMask |= PROC_HIT_EVADE; + break; + case SPELL_MISS_IMMUNE: + case SPELL_MISS_IMMUNE2: + hitMask |= PROC_HIT_IMMUNE; + break; + case SPELL_MISS_DEFLECT: + hitMask |= PROC_HIT_DEFLECT; + break; + case SPELL_MISS_ABSORB: + hitMask |= PROC_HIT_ABSORB; + break; + case SPELL_MISS_REFLECT: + hitMask |= PROC_HIT_REFLECT; + break; + case SPELL_MISS_RESIST: + hitMask |= PROC_HIT_FULL_RESIST; + break; default: break; } + } else { // On block if (damageInfo->blocked) - procEx|=PROC_EX_BLOCK; + { + hitMask |= PROC_HIT_BLOCK; + if (damageInfo->fullBlock) + hitMask |= PROC_HIT_FULL_BLOCK; + } // On absorb if (damageInfo->absorb) - procEx|=PROC_EX_ABSORB; - // On crit - if (damageInfo->HitInfo & SPELL_HIT_TYPE_CRIT) - procEx|=PROC_EX_CRITICAL_HIT; - else - procEx|=PROC_EX_NORMAL_HIT; + hitMask |= PROC_HIT_ABSORB; + + // Don't set hit/crit hitMask if damage is nullified + bool const damageNullified = (damageInfo->HitInfo & (HITINFO_FULL_ABSORB | HITINFO_FULL_RESIST)) != 0 || (hitMask & PROC_HIT_FULL_BLOCK) != 0; + if (!damageNullified) + { + // On crit + if (damageInfo->HitInfo & SPELL_HIT_TYPE_CRIT) + hitMask |= PROC_HIT_CRITICAL; + else + hitMask |= PROC_HIT_NORMAL; + } + else if ((damageInfo->HitInfo & HITINFO_FULL_RESIST) != 0) + hitMask |= PROC_HIT_FULL_RESIST; } - return procEx; + + return hitMask; } -void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellInfo const* procSpell, uint32 damage, SpellInfo const* procAura) +void Unit::ProcSkillsAndReactives(bool isVictim, Unit* procTarget, uint32 typeMask, uint32 hitMask, WeaponAttackType attType) { // Player is loaded now - do not allow passive spell casts to proc if (GetTypeId() == TYPEID_PLAYER && ToPlayer()->GetSession()->PlayerLoading()) return; + // For melee/ranged based attack need update skills and set some Aura states if victim present - if (procFlag & MELEE_BASED_TRIGGER_MASK && target) + if (typeMask & MELEE_BASED_TRIGGER_MASK && procTarget) { // Update skills here for players if (GetTypeId() == TYPEID_PLAYER) { // On melee based hit/miss/resist need update skill (for victim and attacker) - if (procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_MISS|PROC_EX_RESIST)) + if (hitMask & (PROC_HIT_NORMAL | PROC_HIT_MISS | PROC_HIT_FULL_RESIST)) { - if (target->GetTypeId() != TYPEID_PLAYER && !target->IsCritter()) - ToPlayer()->UpdateCombatSkills(target, attType, isVictim); + if (procTarget->GetTypeId() != TYPEID_PLAYER && !procTarget->IsCritter()) + ToPlayer()->UpdateCombatSkills(procTarget, attType, isVictim); } - // Update defence if player is victim and parry/dodge/block - else if (isVictim && procExtra & (PROC_EX_DODGE|PROC_EX_PARRY|PROC_EX_BLOCK)) - ToPlayer()->UpdateCombatSkills(target, attType, true); + // Update defense if player is victim and parry/dodge/block + else if (isVictim && (hitMask & (PROC_HIT_DODGE | PROC_HIT_PARRY | PROC_HIT_BLOCK))) + ToPlayer()->UpdateCombatSkills(procTarget, attType, true); } // If exist crit/parry/dodge/block need update aura state (for victim and attacker) - if (procExtra & (PROC_EX_CRITICAL_HIT|PROC_EX_PARRY|PROC_EX_DODGE|PROC_EX_BLOCK)) + if (hitMask & (PROC_HIT_CRITICAL | PROC_HIT_PARRY | PROC_HIT_DODGE | PROC_HIT_BLOCK)) { // for victim if (isVictim) { // if victim and dodge attack - if (procExtra & PROC_EX_DODGE) + if (hitMask & PROC_HIT_DODGE) { // Update AURA_STATE on dodge if (getClass() != CLASS_ROGUE) // skip Rogue Riposte @@ -13789,7 +10886,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u } } // if victim and parry attack - if (procExtra & PROC_EX_PARRY) + if (hitMask & PROC_HIT_PARRY) { // For Hunters only Counterattack (skip Mongoose bite) if (getClass() == CLASS_HUNTER) @@ -13804,7 +10901,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u } } // if and victim block attack - if (procExtra & PROC_EX_BLOCK) + if (hitMask & PROC_HIT_BLOCK) { ModifyAuraState(AURA_STATE_DEFENSE, true); StartReactiveTimer(REACTIVE_DEFENSE); @@ -13813,362 +10910,42 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u else // For attacker { // Overpower on victim dodge - if (procExtra & PROC_EX_DODGE && GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR) + if ((hitMask & PROC_HIT_DODGE) && GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR) { - ToPlayer()->AddComboPoints(target, 1); + ToPlayer()->AddComboPoints(procTarget, 1); StartReactiveTimer(REACTIVE_OVERPOWER); } } } } - - Unit* actor = isVictim ? target : this; - Unit* actionTarget = !isVictim ? target : this; - - DamageInfo damageInfo = DamageInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL, SPELL_DIRECT_DAMAGE); - HealInfo healInfo = HealInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL); - ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, nullptr, &damageInfo, &healInfo); - - std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); - ProcTriggeredList procTriggered; - // Fill procTriggered list - for (AuraApplicationMap::const_iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr) - { - // Do not allow auras to proc from effect triggered by itself - if (procAura && procAura->Id == itr->first) - continue; - - if (itr->second->GetBase()->IsProcOnCooldown(now)) - continue; - - ProcTriggeredData triggerData(itr->second->GetBase()); - // Defensive procs are active on absorbs (so absorption effects are not a hindrance) - bool active = damage || (procExtra & PROC_EX_BLOCK && isVictim); - if (isVictim) - procExtra &= ~PROC_EX_INTERNAL_REQ_FAMILY; - - SpellInfo const* spellProto = itr->second->GetBase()->GetSpellInfo(); - - // only auras that has triggered spell should proc from fully absorbed damage - if (procExtra & PROC_EX_ABSORB && isVictim) - if (damage || spellProto->Effects[EFFECT_0].TriggerSpell || spellProto->Effects[EFFECT_1].TriggerSpell || spellProto->Effects[EFFECT_2].TriggerSpell) - active = true; - - if (!IsTriggeredAtSpellProcEvent(target, triggerData.aura, procSpell, procFlag, procExtra, attType, isVictim, active, triggerData.spellProcEvent)) - continue; - - // do checks using conditions table - if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_SPELL_PROC, spellProto->Id, eventInfo.GetActor(), eventInfo.GetActionTarget())) - continue; - - // AuraScript Hook - if (!triggerData.aura->CallScriptCheckProcHandlers(itr->second, eventInfo)) - continue; - - bool procSuccess = RollProcResult(target, triggerData.aura, attType, isVictim, triggerData.spellProcEvent); - if (!procSuccess) - continue; - - bool triggeredCanProcAura = true; - // Additional checks for triggered spells (ignore trap casts) - if (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) - { - if (!spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) - triggeredCanProcAura = false; - } - - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (itr->second->HasEffect(i)) - { - AuraEffect* aurEff = itr->second->GetBase()->GetEffect(i); - // Skip this auras - if (isNonTriggerAura[aurEff->GetAuraType()]) - continue; - // If not trigger by default and spellProcEvent == NULL - skip - if (!isTriggerAura[aurEff->GetAuraType()] && triggerData.spellProcEvent == NULL) - continue; - // Some spells must always trigger - if (triggeredCanProcAura || isAlwaysTriggeredAura[aurEff->GetAuraType()]) - triggerData.effMask |= 1<<i; - } - } - if (triggerData.effMask) - procTriggered.push_front(triggerData); - } - - // Nothing found - if (procTriggered.empty()) - return; - - // Note: must SetCantProc(false) before return - if (procExtra & (PROC_EX_INTERNAL_TRIGGERED | PROC_EX_INTERNAL_CANT_PROC)) - SetCantProc(true); - - // Handle effects proceed this time - for (ProcTriggeredList::const_iterator i = procTriggered.begin(); i != procTriggered.end(); ++i) - { - // look for aura in auras list, it may be removed while proc event processing - if (i->aura->IsRemoved()) - continue; - - bool useCharges = i->aura->IsUsingCharges(); - // no more charges to use, prevent proc - if (useCharges && !i->aura->GetCharges()) - continue; - - bool takeCharges = false; - SpellInfo const* spellInfo = i->aura->GetSpellInfo(); - uint32 Id = i->aura->GetId(); - - AuraApplication* aurApp = i->aura->GetApplicationOfTarget(GetGUID()); - - bool prepare = i->aura->CallScriptPrepareProcHandlers(aurApp, eventInfo); - - Milliseconds cooldown = Milliseconds::zero(); - if (prepare && i->spellProcEvent && i->spellProcEvent->cooldown) - cooldown = Seconds(i->spellProcEvent->cooldown); - - // Note: must SetCantProc(false) before return - if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) - SetCantProc(true); - - bool handled = i->aura->CallScriptProcHandlers(aurApp, eventInfo); - - // "handled" is needed as long as proc can be handled in multiple places - if (!handled && HandleAuraProc(target, damage, i->aura, procSpell, procFlag, procExtra, &handled)) - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), Id); - takeCharges = true; - } - - if (!handled) - { - for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) - { - if (!(i->effMask & (1 << effIndex))) - continue; - - AuraEffect* triggeredByAura = i->aura->GetEffect(effIndex); - ASSERT(triggeredByAura); - - bool prevented = i->aura->CallScriptEffectProcHandlers(triggeredByAura, aurApp, eventInfo); - if (prevented) - { - takeCharges = true; - continue; - } - - switch (triggeredByAura->GetAuraType()) - { - case SPELL_AURA_PROC_TRIGGER_SPELL: - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", - spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - // Don`t drop charge or add cooldown for not started trigger - if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra)) - takeCharges = true; - break; - } - case SPELL_AURA_PROC_TRIGGER_DAMAGE: - { - // target has to be valid - if (!eventInfo.GetProcTarget()) - break; - - triggeredByAura->HandleProcTriggerDamageAuraProc(aurApp, eventInfo); // this function is part of the new proc system - takeCharges = true; - break; - } - case SPELL_AURA_MANA_SHIELD: - case SPELL_AURA_DUMMY: - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", - spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - if (HandleDummyAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - takeCharges = true; - break; - } - case SPELL_AURA_OBS_MOD_POWER: - case SPELL_AURA_MOD_SPELL_CRIT_CHANCE: - case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN: - case SPELL_AURA_MOD_MELEE_HASTE: - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", - spellInfo->Id, isVictim ? "a victim's" : "an attacker's", triggeredByAura->GetId()); - takeCharges = true; - break; - case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS: - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", - spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - if (HandleOverrideClassScriptAuraProc(target, damage, triggeredByAura, procSpell)) - takeCharges = true; - break; - } - case SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE: - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", - (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - - HandleAuraRaidProcFromChargeWithValue(triggeredByAura); - takeCharges = true; - break; - } - case SPELL_AURA_RAID_PROC_FROM_CHARGE: - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", - (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - - HandleAuraRaidProcFromCharge(triggeredByAura); - takeCharges = true; - break; - } - case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE: - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", - spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - - if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra)) - takeCharges = true; - break; - } - case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK: - // Skip melee hits or instant cast spells - if (procSpell && procSpell->CalcCastTime() != 0) - takeCharges = true; - break; - case SPELL_AURA_REFLECT_SPELLS_SCHOOL: - // Skip Melee hits and spells ws wrong school - if (procSpell && (triggeredByAura->GetMiscValue() & procSpell->SchoolMask)) // School check - takeCharges = true; - break; - case SPELL_AURA_SPELL_MAGNET: - // Skip Melee hits and targets with magnet aura - if (procSpell && (triggeredByAura->GetBase()->GetUnitOwner()->ToUnit() == ToUnit())) // Magnet - takeCharges = true; - break; - case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT: - case SPELL_AURA_MOD_POWER_COST_SCHOOL: - // Skip melee hits and spells ws wrong school or zero cost - if (procSpell && - (procSpell->ManaCost != 0 || procSpell->ManaCostPercentage != 0) && // Cost check - (triggeredByAura->GetMiscValue() & procSpell->SchoolMask)) // School check - takeCharges = true; - break; - case SPELL_AURA_MECHANIC_IMMUNITY: - // Compare mechanic - if (procSpell && procSpell->Mechanic == uint32(triggeredByAura->GetMiscValue())) - takeCharges = true; - break; - case SPELL_AURA_MOD_MECHANIC_RESISTANCE: - // Compare mechanic - if (procSpell && procSpell->Mechanic == uint32(triggeredByAura->GetMiscValue())) - takeCharges = true; - break; - case SPELL_AURA_MOD_DAMAGE_FROM_CASTER: - // Compare casters - if (triggeredByAura->GetCasterGUID() == target->GetGUID()) - takeCharges = true; - break; - // CC Auras which use their amount amount to drop - // Are there any more auras which need this? - case SPELL_AURA_MOD_CONFUSE: - case SPELL_AURA_MOD_FEAR: - case SPELL_AURA_MOD_STUN: - case SPELL_AURA_MOD_ROOT: - case SPELL_AURA_TRANSFORM: - { - // chargeable mods are breaking on hit - if (useCharges) - takeCharges = true; - else - { - // Spell own direct damage at apply wont break the CC - if (procSpell && (procSpell->Id == triggeredByAura->GetId())) - { - Aura* aura = triggeredByAura->GetBase(); - // called from spellcast, should not have ticked yet - if (aura->GetDuration() == aura->GetMaxDuration()) - break; - } - int32 damageLeft = triggeredByAura->GetAmount(); - // No damage left - if (damageLeft < int32(damage)) - i->aura->Remove(); - else - triggeredByAura->SetAmount(damageLeft - damage); - } - break; - } - //case SPELL_AURA_ADD_FLAT_MODIFIER: - //case SPELL_AURA_ADD_PCT_MODIFIER: - // HandleSpellModAuraProc - //break; - default: - // nothing do, just charges counter - takeCharges = true; - break; - } // switch (triggeredByAura->GetAuraType()) - i->aura->CallScriptAfterEffectProcHandlers(triggeredByAura, aurApp, eventInfo); - } // for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) - } // if (!handled) - - if (prepare && takeCharges && cooldown != Milliseconds::zero()) - i->aura->AddProcCooldown(now + cooldown); - - // Remove charge (aura can be removed by triggers) - if (prepare && useCharges && takeCharges) - { - // Set charge drop delay (only for missiles) - if ((procExtra & PROC_EX_REFLECT) && target && procSpell && procSpell->Speed > 0.0f) - { - // Set up missile speed based delay (from Spell.cpp: Spell::AddUnitTarget()::L2237) - uint32 delay = uint32(std::floor(std::max<float>(target->GetDistance(this), 5.0f) / procSpell->Speed * 1000.0f)); - // Schedule charge drop - i->aura->DropChargeDelayed(delay); - } - else - i->aura->DropCharge(); - } - - i->aura->CallScriptAfterProcHandlers(aurApp, eventInfo); - - if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) - SetCantProc(false); - } - - // Cleanup proc requirements - if (procExtra & (PROC_EX_INTERNAL_TRIGGERED | PROC_EX_INTERNAL_CANT_PROC)) - SetCantProc(false); } -void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTriggeringProc, std::list<AuraApplication*>* procAuras, ProcEventInfo eventInfo) +void Unit::GetProcAurasTriggeredOnEvent(AuraApplicationProcContainer& aurasTriggeringProc, AuraApplicationList* procAuras, ProcEventInfo& eventInfo) { std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); // use provided list of auras which can proc if (procAuras) { - for (std::list<AuraApplication*>::iterator itr = procAuras->begin(); itr!= procAuras->end(); ++itr) + for (AuraApplication* aurApp : *procAuras) { - ASSERT((*itr)->GetTarget() == this); - if (!(*itr)->GetRemoveMode()) - if ((*itr)->GetBase()->IsProcTriggeredOnEvent(*itr, eventInfo, now)) - { - (*itr)->GetBase()->PrepareProcToTrigger(*itr, eventInfo, now); - aurasTriggeringProc.push_back(*itr); - } + ASSERT(aurApp->GetTarget() == this); + if (uint8 procEffectMask = aurApp->GetBase()->GetProcEffectMask(aurApp, eventInfo, now)) + { + aurApp->GetBase()->PrepareProcToTrigger(aurApp, eventInfo, now); + aurasTriggeringProc.emplace_back(procEffectMask, aurApp); + } } } // or generate one on our own else { - for (AuraApplicationMap::iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr) + for (AuraApplicationMap::iterator itr = GetAppliedAuras().begin(); itr != GetAppliedAuras().end(); ++itr) { - if (itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo, now)) + if (uint8 procEffectMask = itr->second->GetBase()->GetProcEffectMask(itr->second, eventInfo, now)) { itr->second->GetBase()->PrepareProcToTrigger(itr->second, eventInfo, now); - aurasTriggeringProc.push_back(itr->second); + aurasTriggeringProc.emplace_back(procEffectMask, itr->second); } } } @@ -14177,35 +10954,73 @@ void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTrigge void Unit::TriggerAurasProcOnEvent(CalcDamageInfo& damageInfo) { DamageInfo dmgInfo = DamageInfo(damageInfo); - TriggerAurasProcOnEvent(NULL, NULL, damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, 0, 0, damageInfo.procEx, NULL, &dmgInfo, NULL); + TriggerAurasProcOnEvent(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, PROC_SPELL_TYPE_NONE, PROC_SPELL_PHASE_NONE, dmgInfo.GetHitMask(), nullptr, &dmgInfo, nullptr); } -void Unit::TriggerAurasProcOnEvent(std::list<AuraApplication*>* myProcAuras, std::list<AuraApplication*>* targetProcAuras, Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo) +void Unit::TriggerAurasProcOnEvent(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo) { // prepare data for self trigger - ProcEventInfo myProcEventInfo = ProcEventInfo(this, actionTarget, actionTarget, typeMaskActor, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); - std::list<AuraApplication*> myAurasTriggeringProc; - GetProcAurasTriggeredOnEvent(myAurasTriggeringProc, myProcAuras, myProcEventInfo); - - // prepare data for target trigger - ProcEventInfo targetProcEventInfo = ProcEventInfo(this, actionTarget, this, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); - std::list<AuraApplication*> targetAurasTriggeringProc; - if (typeMaskActionTarget) - GetProcAurasTriggeredOnEvent(targetAurasTriggeringProc, targetProcAuras, targetProcEventInfo); + ProcEventInfo myProcEventInfo(this, actionTarget, actionTarget, typeMaskActor, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); + if (typeMaskActor) + { + AuraApplicationProcContainer myAurasTriggeringProc; + GetProcAurasTriggeredOnEvent(myAurasTriggeringProc, nullptr, myProcEventInfo); - TriggerAurasProcOnEvent(myProcEventInfo, myAurasTriggeringProc); + // 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); + } - if (typeMaskActionTarget) - TriggerAurasProcOnEvent(targetProcEventInfo, targetAurasTriggeringProc); + // prepare data for target trigger + ProcEventInfo targetProcEventInfo(this, actionTarget, this, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); + if (typeMaskActionTarget && actionTarget) + { + AuraApplicationProcContainer targetAurasTriggeringProc; + actionTarget->GetProcAurasTriggeredOnEvent(targetAurasTriggeringProc, nullptr, targetProcEventInfo); + actionTarget->TriggerAurasProcOnEvent(targetProcEventInfo, targetAurasTriggeringProc); + } } -void Unit::TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, std::list<AuraApplication*>& aurasTriggeringProc) +void Unit::TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationProcContainer& aurasTriggeringProc) { - for (std::list<AuraApplication*>::iterator itr = aurasTriggeringProc.begin(); itr != aurasTriggeringProc.end(); ++itr) + Spell const* triggeringSpell = eventInfo.GetProcSpell(); + bool const disableProcs = triggeringSpell && triggeringSpell->IsProcDisabled(); + if (disableProcs) + SetCantProc(true); + + for (auto const& aurAppProc : aurasTriggeringProc) { - if (!(*itr)->GetRemoveMode()) - (*itr)->GetBase()->TriggerProcOnEvent(*itr, eventInfo); + AuraApplication* aurApp; + uint8 procEffectMask; + std::tie(procEffectMask, aurApp) = aurAppProc; + + if (aurApp->GetRemoveMode()) + continue; + + SpellInfo const* spellInfo = aurApp->GetBase()->GetSpellInfo(); + if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) + SetCantProc(true); + + aurApp->GetBase()->TriggerProcOnEvent(procEffectMask, aurApp, eventInfo); + + if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) + SetCantProc(false); } + + if (disableProcs) + SetCantProc(false); } SpellSchoolMask Unit::GetMeleeDamageSchoolMask() const @@ -14655,26 +11470,41 @@ float Unit::CalculateDefaultCoefficient(SpellInfo const* spellInfo, DamageEffect float Unit::GetAPMultiplier(WeaponAttackType attType, bool normalized) { - if (!normalized || GetTypeId() != TYPEID_PLAYER) - return float(GetAttackTime(attType)) / 1000.0f; + if (GetTypeId() != TYPEID_PLAYER) + return GetAttackTime(attType) / 1000.0f; - Item* Weapon = ToPlayer()->GetWeaponForAttack(attType, true); - if (!Weapon) - return 2.4f; // fist attack + Item* weapon = ToPlayer()->GetWeaponForAttack(attType, true); + if (!weapon) + return BASE_ATTACK_TIME / 1000.0f; - switch (Weapon->GetTemplate()->InventoryType) + if (!normalized) + return weapon->GetTemplate()->Delay / 1000.0f; + + switch (weapon->GetTemplate()->SubClass) { - case INVTYPE_2HWEAPON: + case ITEM_SUBCLASS_WEAPON_AXE2: + case ITEM_SUBCLASS_WEAPON_MACE2: + case ITEM_SUBCLASS_WEAPON_POLEARM: + case ITEM_SUBCLASS_WEAPON_SWORD2: + case ITEM_SUBCLASS_WEAPON_STAFF: + case ITEM_SUBCLASS_WEAPON_FISHING_POLE: return 3.3f; - case INVTYPE_RANGED: - case INVTYPE_RANGEDRIGHT: - case INVTYPE_THROWN: + case ITEM_SUBCLASS_WEAPON_BOW: + case ITEM_SUBCLASS_WEAPON_GUN: + case ITEM_SUBCLASS_WEAPON_CROSSBOW: + case ITEM_SUBCLASS_WEAPON_THROWN: return 2.8f; - case INVTYPE_WEAPON: - case INVTYPE_WEAPONMAINHAND: - case INVTYPE_WEAPONOFFHAND: + case ITEM_SUBCLASS_WEAPON_AXE: + case ITEM_SUBCLASS_WEAPON_MACE: + case ITEM_SUBCLASS_WEAPON_SWORD: + case ITEM_SUBCLASS_WEAPON_EXOTIC: + case ITEM_SUBCLASS_WEAPON_EXOTIC2: + case ITEM_SUBCLASS_WEAPON_FIST: + return 2.4f; + case ITEM_SUBCLASS_WEAPON_DAGGER: + return 1.7f; default: - return Weapon->GetTemplate()->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER ? 1.7f : 2.4f; + return weapon->GetTemplate()->Delay / 1000.0f; } } @@ -14789,203 +11619,6 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) return true; } -bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent) -{ - SpellInfo const* spellInfo = aura->GetSpellInfo(); - - // let the aura be handled by new proc system if it has new entry - if (sSpellMgr->GetSpellProcEntry(spellInfo->Id)) - return false; - - // Get proc Event Entry - spellProcEvent = sSpellMgr->GetSpellProcEvent(spellInfo->Id); - - // Get EventProcFlag - uint32 EventProcFlag; - if (spellProcEvent && spellProcEvent->procFlags) // if exist get custom spellProcEvent->procFlags - EventProcFlag = spellProcEvent->procFlags; - else - EventProcFlag = spellInfo->ProcFlags; // else get from spell proto - // Continue if no trigger exist - if (!EventProcFlag) - return false; - - // Check spellProcEvent data requirements - if (!sSpellMgr->IsSpellProcEventCanTriggeredBy(spellInfo, spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active)) - return false; - // In most cases req get honor or XP from kill - if (EventProcFlag & PROC_FLAG_KILL && GetTypeId() == TYPEID_PLAYER) - { - bool allow = false; - - if (victim) - allow = ToPlayer()->isHonorOrXPTarget(victim); - - // Shadow Word: Death - can trigger from every kill - if (aura->GetId() == 32409) - allow = true; - if (!allow) - return false; - } - // Aura added by spell can`t trigger from self (prevent drop charges/do triggers) - // But except periodic and kill triggers (can triggered from self) - if (procSpell && procSpell->Id == spellInfo->Id - && !(spellInfo->ProcFlags&(PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_KILL))) - return false; - - // Check if current equipment allows aura to proc - if (!isVictim && GetTypeId() == TYPEID_PLAYER) - { - Player* player = ToPlayer(); - if (spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON) - { - Item* item = NULL; - if (attType == BASE_ATTACK) - item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); - else if (attType == OFF_ATTACK) - item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - else - item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); - - if (player->IsInFeralForm()) - return false; - - if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetTemplate()->SubClass) & spellInfo->EquippedItemSubClassMask)) - return false; - } - else if (spellInfo->EquippedItemClass == ITEM_CLASS_ARMOR) - { - // Check if player is wearing shield - Item* item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1<<item->GetTemplate()->SubClass) & spellInfo->EquippedItemSubClassMask)) - return false; - } - } - - return true; -} - -bool Unit::RollProcResult(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const* spellProcEvent) -{ - SpellInfo const* spellInfo = aura->GetSpellInfo(); - // Get chance from spell - float chance = float(spellInfo->ProcChance); - // If in spellProcEvent exist custom chance, chance = spellProcEvent->customChance; - if (spellProcEvent && spellProcEvent->customChance) - chance = spellProcEvent->customChance; - // If PPM exist calculate chance from PPM - if (spellProcEvent && spellProcEvent->ppmRate != 0) - { - if (!isVictim) - { - uint32 weaponSpeed = GetAttackTime(attType); - chance = GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellInfo); - } - else if (victim) - { - uint32 weaponSpeed = victim->GetAttackTime(attType); - chance = victim->GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellInfo); - } - } - // Apply chance modifer aura - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance); - - return roll_chance_f(chance); -} - -bool Unit::HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura) -{ - // aura can be deleted at casts - SpellInfo const* spellProto = triggeredByAura->GetSpellInfo(); - int32 heal = triggeredByAura->GetAmount(); - ObjectGuid caster_guid = triggeredByAura->GetCasterGUID(); - - // Currently only Prayer of Mending - if (!(spellProto->SpellFamilyName == SPELLFAMILY_PRIEST && spellProto->SpellFamilyFlags[1] & 0x20)) - { - TC_LOG_DEBUG("spells", "Unit::HandleAuraRaidProcFromChargeWithValue, received not handled spell: %u", spellProto->Id); - return false; - } - - // jumps - int32 jumps = triggeredByAura->GetBase()->GetCharges()-1; - - // current aura expire - triggeredByAura->GetBase()->SetCharges(1); // will removed at next charges decrease - - // next target selection - if (jumps > 0) - { - if (Unit* caster = triggeredByAura->GetCaster()) - { - float radius = triggeredByAura->GetSpellInfo()->Effects[triggeredByAura->GetEffIndex()].CalcRadius(caster); - - if (Unit* target = GetNextRandomRaidMemberOrPet(radius)) - { - CastCustomSpell(target, spellProto->Id, &heal, NULL, NULL, true, NULL, triggeredByAura, caster_guid); - if (Aura* aura = target->GetAura(spellProto->Id, caster->GetGUID())) - aura->SetCharges(jumps); - } - } - } - - // heal - CastCustomSpell(this, 33110, &heal, NULL, NULL, true, NULL, NULL, caster_guid); - return true; - -} -bool Unit::HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura) -{ - // aura can be deleted at casts - SpellInfo const* spellProto = triggeredByAura->GetSpellInfo(); - - uint32 damageSpellId; - switch (spellProto->Id) - { - case 57949: // shiver - damageSpellId = 57952; - //animationSpellId = 57951; dummy effects for jump spell have unknown use (see also 41637) - break; - case 59978: // shiver - damageSpellId = 59979; - break; - case 43593: // Cold Stare - damageSpellId = 43594; - break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleAuraRaidProcFromCharge, received unhandled spell: %u", spellProto->Id); - return false; - } - - ObjectGuid caster_guid = triggeredByAura->GetCasterGUID(); - - // jumps - int32 jumps = triggeredByAura->GetBase()->GetCharges()-1; - - // current aura expire - triggeredByAura->GetBase()->SetCharges(1); // will removed at next charges decrease - - // next target selection - if (jumps > 0) - { - if (Unit* caster = triggeredByAura->GetCaster()) - { - float radius = triggeredByAura->GetSpellInfo()->Effects[triggeredByAura->GetEffIndex()].CalcRadius(caster); - if (Unit* target= GetNextRandomRaidMemberOrPet(radius)) - { - CastSpell(target, spellProto, true, NULL, triggeredByAura, caster_guid); - if (Aura* aura = target->GetAura(spellProto->Id, caster->GetGUID())) - aura->SetCharges(jumps); - } - } - } - - CastSpell(this, damageSpellId, true, NULL, triggeredByAura, caster_guid); - - return true; -} - void Unit::Kill(Unit* victim, bool durabilityLoss) { // Prevent killing unit twice (and giving reward from kill twice) @@ -15080,14 +11713,17 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) // Do KILL and KILLED procs. KILL proc is called only for the unit who landed the killing blow (and its owner - for pets and totems) regardless of who tapped the victim if (IsPet() || IsTotem()) + { + // proc only once for victim if (Unit* owner = GetOwner()) - owner->ProcDamageAndSpell(victim, PROC_FLAG_KILL, PROC_FLAG_NONE, PROC_EX_NONE, 0); + owner->ProcSkillsAndAuras(victim, PROC_FLAG_KILL, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); + } if (!victim->IsCritter()) - ProcDamageAndSpell(victim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_EX_NONE, 0); + ProcSkillsAndAuras(victim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); // Proc auras on death - must be before aura/combat remove - victim->ProcDamageAndSpell(NULL, PROC_FLAG_DEATH, PROC_FLAG_NONE, PROC_EX_NONE, 0, BASE_ATTACK, 0); + victim->ProcSkillsAndAuras(victim, PROC_FLAG_NONE, PROC_FLAG_DEATH, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); // update get killing blow achievements, must be done before setDeathState to be able to require auras on target // and before Spirit of Redemption as it also removes auras @@ -15896,8 +12532,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 @@ -15915,8 +12552,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) @@ -15981,7 +12619,7 @@ Aura* Unit::AddAura(uint32 spellId, Unit* target) if (!spellInfo) return NULL; - if (!target->IsAlive() && !spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE) && !spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_DEAD)) + if (!target->IsAlive() && !spellInfo->IsPassive() && !spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_DEAD)) return NULL; return AddAura(spellInfo, MAX_EFFECT_MASK, target); @@ -16122,7 +12760,7 @@ float Unit::MeleeSpellMissChance(const Unit* victim, WeaponAttackType attType, i if (spellId) { if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_MISS_CHANCE, hitChance); + modOwner->ApplySpellMod<SPELLMOD_RESIST_MISS_CHANCE>(spellId, hitChance); } missChance += hitChance - 100.0f; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index aa232577d3d..917e5d24c79 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -81,42 +81,42 @@ enum SpellAuraInterruptFlags AURA_INTERRUPT_FLAG_NOT_VICTIM = (AURA_INTERRUPT_FLAG_HITBYSPELL | AURA_INTERRUPT_FLAG_TAKE_DAMAGE | AURA_INTERRUPT_FLAG_DIRECT_DAMAGE) }; -enum SpellModOp -{ - SPELLMOD_DAMAGE = 0, - SPELLMOD_DURATION = 1, - SPELLMOD_THREAT = 2, - SPELLMOD_EFFECT1 = 3, - SPELLMOD_CHARGES = 4, - SPELLMOD_RANGE = 5, - SPELLMOD_RADIUS = 6, - SPELLMOD_CRITICAL_CHANCE = 7, - SPELLMOD_ALL_EFFECTS = 8, - SPELLMOD_NOT_LOSE_CASTING_TIME = 9, - SPELLMOD_CASTING_TIME = 10, - SPELLMOD_COOLDOWN = 11, - SPELLMOD_EFFECT2 = 12, - SPELLMOD_IGNORE_ARMOR = 13, - SPELLMOD_COST = 14, - SPELLMOD_CRIT_DAMAGE_BONUS = 15, - SPELLMOD_RESIST_MISS_CHANCE = 16, - SPELLMOD_JUMP_TARGETS = 17, - SPELLMOD_CHANCE_OF_SUCCESS = 18, - SPELLMOD_ACTIVATION_TIME = 19, - SPELLMOD_DAMAGE_MULTIPLIER = 20, - SPELLMOD_GLOBAL_COOLDOWN = 21, - SPELLMOD_DOT = 22, - SPELLMOD_EFFECT3 = 23, - SPELLMOD_BONUS_MULTIPLIER = 24, +enum SpellModOp : uint8 +{ + SPELLMOD_DAMAGE = 0, + SPELLMOD_DURATION = 1, + SPELLMOD_THREAT = 2, + SPELLMOD_EFFECT1 = 3, + SPELLMOD_CHARGES = 4, + SPELLMOD_RANGE = 5, + SPELLMOD_RADIUS = 6, + SPELLMOD_CRITICAL_CHANCE = 7, + SPELLMOD_ALL_EFFECTS = 8, + SPELLMOD_NOT_LOSE_CASTING_TIME = 9, + SPELLMOD_CASTING_TIME = 10, + SPELLMOD_COOLDOWN = 11, + SPELLMOD_EFFECT2 = 12, + SPELLMOD_IGNORE_ARMOR = 13, + SPELLMOD_COST = 14, + SPELLMOD_CRIT_DAMAGE_BONUS = 15, + SPELLMOD_RESIST_MISS_CHANCE = 16, + SPELLMOD_JUMP_TARGETS = 17, + SPELLMOD_CHANCE_OF_SUCCESS = 18, + SPELLMOD_ACTIVATION_TIME = 19, + SPELLMOD_DAMAGE_MULTIPLIER = 20, + SPELLMOD_GLOBAL_COOLDOWN = 21, + SPELLMOD_DOT = 22, + SPELLMOD_EFFECT3 = 23, + SPELLMOD_BONUS_MULTIPLIER = 24, // spellmod 25 - SPELLMOD_PROC_PER_MINUTE = 26, - SPELLMOD_VALUE_MULTIPLIER = 27, - SPELLMOD_RESIST_DISPEL_CHANCE = 28, - SPELLMOD_CRIT_DAMAGE_BONUS_2 = 29, //one not used spell - SPELLMOD_SPELL_COST_REFUND_ON_FAIL = 30 -}; + SPELLMOD_PROC_PER_MINUTE = 26, + SPELLMOD_VALUE_MULTIPLIER = 27, + SPELLMOD_RESIST_DISPEL_CHANCE = 28, + SPELLMOD_CRIT_DAMAGE_BONUS_2 = 29, //one not used spell + SPELLMOD_SPELL_COST_REFUND_ON_FAIL = 30, -#define MAX_SPELLMOD 32 + MAX_SPELLMOD +}; enum SpellValueMod { @@ -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_DISALLOW_PROC_EVENTS = 0x00020000, //! Disallows proc events from triggered spell (default) + 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,17 +802,21 @@ 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) { } - DiminishingGroup DRGroup:16; - uint16 stack:16; + void Clear() + { + stack = 0; + hitTime = 0; + hitCount = DIMINISHING_LEVEL_1; + } + + uint16 stack; uint32 hitTime; uint32 hitCount; }; -enum MeleeHitOutcome +enum MeleeHitOutcome : uint8 { MELEE_HIT_EVADE, MELEE_HIT_MISS, MELEE_HIT_DODGE, MELEE_HIT_BLOCK, MELEE_HIT_PARRY, MELEE_HIT_GLANCING, MELEE_HIT_CRIT, MELEE_HIT_CRUSHING, MELEE_HIT_NORMAL @@ -819,21 +824,18 @@ enum MeleeHitOutcome class DispelInfo { -public: - explicit DispelInfo(Unit* dispeller, uint32 dispellerSpellId, uint8 chargesRemoved) : - _dispellerUnit(dispeller), _dispellerSpell(dispellerSpellId), _chargesRemoved(chargesRemoved) { } + public: + explicit DispelInfo(Unit* dispeller, uint32 dispellerSpellId, uint8 chargesRemoved) : + _dispellerUnit(dispeller), _dispellerSpell(dispellerSpellId), _chargesRemoved(chargesRemoved) { } - Unit* GetDispeller() const { return _dispellerUnit; } - uint32 GetDispellerSpellId() const { return _dispellerSpell; } - uint8 GetRemovedCharges() const { return _chargesRemoved; } - void SetRemovedCharges(uint8 amount) - { - _chargesRemoved = amount; - } -private: - Unit* _dispellerUnit; - uint32 _dispellerSpell; - uint8 _chargesRemoved; + Unit* GetDispeller() const { return _dispellerUnit; } + uint32 GetDispellerSpellId() const { return _dispellerSpell; } + uint8 GetRemovedCharges() const { return _chargesRemoved; } + void SetRemovedCharges(uint8 amount) { _chargesRemoved = amount; } + private: + Unit* _dispellerUnit; + uint32 _dispellerSpell; + uint8 _chargesRemoved; }; struct CleanDamage @@ -849,103 +851,110 @@ struct CleanDamage }; struct CalcDamageInfo; +struct SpellNonMeleeDamage; class TC_GAME_API DamageInfo { -private: - Unit* const m_attacker; - Unit* const m_victim; - uint32 m_damage; - SpellInfo const* const m_spellInfo; - SpellSchoolMask const m_schoolMask; - DamageEffectType const m_damageType; - WeaponAttackType m_attackType; - uint32 m_absorb; - uint32 m_resist; - uint32 m_block; -public: - explicit DamageInfo(Unit* _attacker, Unit* _victim, uint32 _damage, SpellInfo const* _spellInfo, SpellSchoolMask _schoolMask, DamageEffectType _damageType); - explicit DamageInfo(CalcDamageInfo& dmgInfo); - - void ModifyDamage(int32 amount); - void AbsorbDamage(uint32 amount); - void ResistDamage(uint32 amount); - void BlockDamage(uint32 amount); - - Unit* GetAttacker() const { return m_attacker; } - Unit* GetVictim() const { return m_victim; } - SpellInfo const* GetSpellInfo() const { return m_spellInfo; } - SpellSchoolMask GetSchoolMask() const { return m_schoolMask; } - DamageEffectType GetDamageType() const { return m_damageType; } - WeaponAttackType GetAttackType() const { return m_attackType; } - uint32 GetDamage() const { return m_damage; } - uint32 GetAbsorb() const { return m_absorb; } - uint32 GetResist() const { return m_resist; } - uint32 GetBlock() const { return m_block; } + private: + Unit* const m_attacker; + Unit* const m_victim; + uint32 m_damage; + SpellInfo const* const m_spellInfo; + SpellSchoolMask const m_schoolMask; + DamageEffectType const m_damageType; + WeaponAttackType m_attackType; + uint32 m_absorb; + uint32 m_resist; + uint32 m_block; + uint32 m_hitMask; + public: + DamageInfo(Unit* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, DamageEffectType damageType, WeaponAttackType attackType); + explicit DamageInfo(CalcDamageInfo const& dmgInfo); + DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType, uint32 hitMask); + + void ModifyDamage(int32 amount); + void AbsorbDamage(uint32 amount); + void ResistDamage(uint32 amount); + void BlockDamage(uint32 amount); + + Unit* GetAttacker() const { return m_attacker; } + Unit* GetVictim() const { return m_victim; } + SpellInfo const* GetSpellInfo() const { return m_spellInfo; } + SpellSchoolMask GetSchoolMask() const { return m_schoolMask; } + DamageEffectType GetDamageType() const { return m_damageType; } + WeaponAttackType GetAttackType() const { return m_attackType; } + uint32 GetDamage() const { return m_damage; } + uint32 GetAbsorb() const { return m_absorb; } + uint32 GetResist() const { return m_resist; } + uint32 GetBlock() const { return m_block; } + + uint32 GetHitMask() const; }; -class HealInfo +class TC_GAME_API HealInfo { -private: - Unit* const _healer; - Unit* const _target; - uint32 _heal; - uint32 _absorb; - SpellInfo const* const _spellInfo; - SpellSchoolMask const _schoolMask; + private: + Unit* const _healer; + Unit* const _target; + uint32 _heal; + uint32 _effectiveHeal; + uint32 _absorb; + SpellInfo const* const _spellInfo; + SpellSchoolMask const _schoolMask; + uint32 _hitMask; -public: - explicit HealInfo(Unit* healer, Unit* target, uint32 heal, SpellInfo const* spellInfo, SpellSchoolMask schoolMask) - : _healer(healer), _target(target), _heal(heal), _absorb(0), _spellInfo(spellInfo), _schoolMask(schoolMask) { } + public: + HealInfo(Unit* healer, Unit* target, uint32 heal, SpellInfo const* spellInfo, SpellSchoolMask schoolMask); - void AbsorbHeal(uint32 amount) - { - amount = std::min(amount, GetHeal()); - _absorb += amount; - _heal -= amount; - } + void AbsorbHeal(uint32 amount); + void SetEffectiveHeal(uint32 amount) { _effectiveHeal = amount; } - Unit* GetHealer() const { return _healer; } - Unit* GetTarget() const { return _target; } - uint32 GetHeal() const { return _heal; } - uint32 GetAbsorb() const { return _absorb; } - SpellInfo const* GetSpellInfo() const { return _spellInfo; }; - SpellSchoolMask GetSchoolMask() const { return _schoolMask; }; + Unit* GetHealer() const { return _healer; } + Unit* GetTarget() const { return _target; } + uint32 GetHeal() const { return _heal; } + uint32 GetEffectiveHeal() const { return _effectiveHeal; } + uint32 GetAbsorb() const { return _absorb; } + SpellInfo const* GetSpellInfo() const { return _spellInfo; }; + SpellSchoolMask GetSchoolMask() const { return _schoolMask; }; + + uint32 GetHitMask() const; }; class TC_GAME_API ProcEventInfo { -public: - ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, uint32 typeMask, - uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, - Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo); + public: + ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, uint32 typeMask, + uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, + Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo); - Unit* GetActor() { return _actor; } - Unit* GetActionTarget() const { return _actionTarget; } - Unit* GetProcTarget() const { return _procTarget; } + Unit* GetActor() { return _actor; } + Unit* GetActionTarget() const { return _actionTarget; } + Unit* GetProcTarget() const { return _procTarget; } - uint32 GetTypeMask() const { return _typeMask; } - uint32 GetSpellTypeMask() const { return _spellTypeMask; } - uint32 GetSpellPhaseMask() const { return _spellPhaseMask; } - uint32 GetHitMask() const { return _hitMask; } + uint32 GetTypeMask() const { return _typeMask; } + uint32 GetSpellTypeMask() const { return _spellTypeMask; } + uint32 GetSpellPhaseMask() const { return _spellPhaseMask; } + uint32 GetHitMask() const { return _hitMask; } - SpellInfo const* GetSpellInfo() const; - SpellSchoolMask GetSchoolMask() const; + SpellInfo const* GetSpellInfo() const; + SpellSchoolMask GetSchoolMask() const; - DamageInfo* GetDamageInfo() const { return _damageInfo; } - HealInfo* GetHealInfo() const { return _healInfo; } + DamageInfo* GetDamageInfo() const { return _damageInfo; } + HealInfo* GetHealInfo() const { return _healInfo; } -private: - Unit* const _actor; - Unit* const _actionTarget; - Unit* const _procTarget; - uint32 _typeMask; - uint32 _spellTypeMask; - uint32 _spellPhaseMask; - uint32 _hitMask; - Spell* _spell; - DamageInfo* _damageInfo; - HealInfo* _healInfo; + Spell const* GetProcSpell() const { return _spell; } + + private: + Unit* const _actor; + Unit* const _actionTarget; + Unit* const _procTarget; + uint32 _typeMask; + uint32 _spellTypeMask; + uint32 _spellPhaseMask; + uint32 _hitMask; + Spell* _spell; + DamageInfo* _damageInfo; + HealInfo* _healInfo; }; // Struct for use in Unit::CalculateMeleeDamage @@ -965,7 +974,6 @@ struct CalcDamageInfo WeaponAttackType attackType; // uint32 procAttacker; uint32 procVictim; - uint32 procEx; uint32 cleanDamage; // Used only for rage calculation MeleeHitOutcome hitOutCome; /// @todo remove this field (need use TargetState) }; @@ -975,7 +983,7 @@ struct TC_GAME_API SpellNonMeleeDamage { SpellNonMeleeDamage(Unit* _attacker, Unit* _target, uint32 _SpellID, uint32 _schoolMask) : target(_target), attacker(_attacker), SpellID(_SpellID), damage(0), overkill(0), schoolMask(_schoolMask), - absorb(0), resist(0), physicalLog(false), unused(false), blocked(0), HitInfo(0), cleanDamage(0) + absorb(0), resist(0), physicalLog(false), unused(false), blocked(0), HitInfo(0), cleanDamage(0), fullBlock(false) { } Unit *target; @@ -992,6 +1000,7 @@ struct TC_GAME_API SpellNonMeleeDamage uint32 HitInfo; // Used for help uint32 cleanDamage; + bool fullBlock; }; struct SpellPeriodicAuraLogInfo @@ -1008,7 +1017,7 @@ struct SpellPeriodicAuraLogInfo bool critical; }; -uint32 createProcExtendMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition); +uint32 createProcHitMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition); struct RedirectThreatInfo { @@ -1232,8 +1241,6 @@ enum PlayerTotemType #define ATTACK_DISPLAY_DELAY 200 #define MAX_PLAYER_STEALTH_DETECT_RANGE 30.0f // max distance for detection targets by player -struct SpellProcEventEntry; // used only privately - class TC_GAME_API Unit : public WorldObject { public: @@ -1254,7 +1261,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::vector<std::pair<uint8 /*procEffectMask*/, AuraApplication*>> AuraApplicationProcContainer; typedef std::map<uint8, AuraApplication*> VisibleAuraMap; @@ -1269,11 +1278,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; @@ -1425,25 +1434,26 @@ 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); } - int32 DealHeal(Unit* victim, uint32 addhealth); + void DealHeal(HealInfo& healInfo); - void ProcDamageAndSpell(Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 amount, WeaponAttackType attType = BASE_ATTACK, SpellInfo const* procSpell = NULL, SpellInfo const* procAura = NULL); - void ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellInfo const* procSpell, uint32 damage, SpellInfo const* procAura = NULL); + void ProcSkillsAndAuras(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, + uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, + DamageInfo* damageInfo, HealInfo* healInfo); - void GetProcAurasTriggeredOnEvent(AuraApplicationList& aurasTriggeringProc, AuraApplicationList* procAuras, ProcEventInfo eventInfo); + void GetProcAurasTriggeredOnEvent(AuraApplicationProcContainer& aurasTriggeringProc, AuraApplicationList* procAuras, ProcEventInfo& eventInfo); void TriggerAurasProcOnEvent(CalcDamageInfo& damageInfo); - void TriggerAurasProcOnEvent(AuraApplicationList* myProcAuras, AuraApplicationList* targetProcAuras, - Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, + void TriggerAurasProcOnEvent(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo); - void TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationList& procAuras); + void TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationProcContainer& procAuras); 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); @@ -1470,28 +1480,28 @@ class TC_GAME_API Unit : public WorldObject void ApplyResilience(Unit const* victim, float* crit, int32* damage, bool isCrit, CombatRating type) const; float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const; - SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo); - SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo); + SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const; + SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const; SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect = false); - float GetUnitDodgeChance() const; - float GetUnitParryChance() const; - float GetUnitBlockChance() const; + float GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const; + float GetUnitParryChance(WeaponAttackType attType, Unit const* victim) const; + float GetUnitBlockChance(WeaponAttackType attType, Unit const* victim) const; float GetUnitMissChance(WeaponAttackType attType) const; - float GetUnitCriticalChance(WeaponAttackType attackType, const Unit* victim) const; + float GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victim) const; int32 GetMechanicResistChance(SpellInfo const* spellInfo) const; bool CanUseAttackType(uint8 attacktype) const; - virtual uint32 GetShieldBlockValue() const =0; + virtual uint32 GetShieldBlockValue() const = 0; uint32 GetShieldBlockValue(uint32 soft_cap, uint32 hard_cap) const; - uint32 GetUnitMeleeSkill(Unit const* target = NULL) const; - uint32 GetDefenseSkillValue(Unit const* target = NULL) const; - uint32 GetWeaponSkillValue(WeaponAttackType attType, Unit const* target = NULL) const; + uint32 GetUnitMeleeSkill(Unit const* target = nullptr) const; + uint32 GetDefenseSkillValue(Unit const* target = nullptr) const; + uint32 GetWeaponSkillValue(WeaponAttackType attType, Unit const* target = nullptr) const; float GetWeaponProcChance() const; float GetPPMProcChance(uint32 WeaponSpeed, float PPM, const SpellInfo* spellProto) const; - MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackType attType) const; - MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const; + MeleeHitOutcome RollMeleeOutcomeAgainst(Unit const* victim, WeaponAttackType attType) const; + MeleeHitOutcome RollMeleeOutcomeAgainst(Unit const* victim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const; bool IsVendor() const { return HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_VENDOR); } bool IsTrainer() const { return HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER); } @@ -1547,23 +1557,23 @@ class TC_GAME_API Unit : public WorldObject virtual void UpdateUnderwaterState(Map* m, float x, float y, float z); bool isInAccessiblePlaceFor(Creature const* c) const; - void SendHealSpellLog(Unit* victim, uint32 SpellID, uint32 Damage, uint32 OverHeal, uint32 Absorb, bool critical = false); - int32 HealBySpell(Unit* victim, SpellInfo const* spellInfo, uint32 addHealth, bool critical = false); + void SendHealSpellLog(HealInfo& healInfo, bool critical = false); + int32 HealBySpell(HealInfo& healInfo, bool critical = false); void SendEnergizeSpellLog(Unit* victim, uint32 spellID, int32 damage, Powers powerType); 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); @@ -1704,16 +1714,14 @@ class TC_GAME_API Unit : public WorldObject bool InitTamedPet(Pet* pet, uint8 level, uint32 spell_id); // aura apply/remove helpers - you should better not use these - Aura* _TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 effMask, Unit* caster, int32* baseAmount = NULL, Item* castItem = NULL, ObjectGuid casterGUID = ObjectGuid::Empty); + Aura* _TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 effMask, Unit* caster, int32* baseAmount = nullptr, Item* castItem = nullptr, ObjectGuid casterGUID = ObjectGuid::Empty, bool resetPeriodicTimer = true); void _AddAura(UnitAura* aura, Unit* caster); AuraApplication * _CreateAuraApplication(Aura* aura, uint8 effMask); void _ApplyAuraEffect(Aura* aura, uint8 effIndex); void _ApplyAura(AuraApplication * aurApp, uint8 effMask); void _UnapplyAura(AuraApplicationMap::iterator &i, AuraRemoveMode removeMode); void _UnapplyAura(AuraApplication * aurApp, AuraRemoveMode removeMode); - void _RemoveNoStackAuraApplicationsDueToAura(Aura* aura); void _RemoveNoStackAurasDueToAura(Aura* aura); - bool _IsNoStackAuraDueToAura(Aura* appliedAura, Aura* existingAura) const; void _RegisterAuraEffect(AuraEffect* aurEff, bool apply); // m_ownedAuras container management @@ -1929,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 @@ -2014,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; @@ -2026,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(Unit* victim, SpellInfo const* spellInfo, uint32& healAmount, uint32& absorb); + void CalcAbsorbResist(DamageInfo& damageInfo); + void CalcHealAbsorb(HealInfo& healInfo) const; void UpdateSpeed(UnitMoveType mtype); float GetSpeed(UnitMoveType mtype) const; @@ -2089,7 +2096,7 @@ class TC_GAME_API Unit : public WorldObject void UpdateAuraForGroup(uint8 slot); // proc trigger system - bool CanProc() const {return !m_procDeep;} + bool CanProc() const { return !m_procDeep; } void SetCantProc(bool apply); // pet auras @@ -2260,15 +2267,8 @@ class TC_GAME_API Unit : public WorldObject bool IsAlwaysDetectableFor(WorldObject const* seer) const override; void DisableSpline(); + private: - bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent); - bool RollProcResult(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const* spellProcEvent); - bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, Milliseconds& cooldown); - bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, bool* handled); - bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx); - bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell); - bool HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura); - bool HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura); void UpdateSplineMovement(uint32 t_diff); void UpdateSplinePosition(); @@ -2277,6 +2277,8 @@ class TC_GAME_API Unit : public WorldObject float GetCombatRatingReduction(CombatRating cr) const; uint32 GetCombatRatingDamageReduction(CombatRating cr, float rate, float cap, uint32 damage) const; + void ProcSkillsAndReactives(bool isVictim, Unit* procTarget, uint32 typeMask, uint32 hitMask, WeaponAttackType attType); + void SetFeared(bool apply); void SetConfused(bool apply); void SetStunned(bool apply); 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/GridRefManager.h b/src/server/game/Grids/GridRefManager.h index 755417d873d..ceaf632aa63 100644 --- a/src/server/game/Grids/GridRefManager.h +++ b/src/server/game/Grids/GridRefManager.h @@ -35,8 +35,6 @@ class GridRefManager : public RefManager<GridRefManager<OBJECT>, OBJECT> iterator begin() { return iterator(getFirst()); } iterator end() { return iterator(NULL); } - iterator rbegin() { return iterator(getLast()); } - iterator rend() { return iterator(NULL); } }; #endif diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 8304054c663..ac153f31b8e 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -170,6 +170,31 @@ namespace Trinity // WorldObject searchers & workers + // Generic base class to insert elements into arbitrary containers using push_back + template<typename Type> + class ContainerInserter { + using InserterType = void(*)(void*, Type&&); + + void* ref; + InserterType inserter; + + // MSVC workaround + template<typename T> + static void InserterOf(void* ref, Type&& type) + { + static_cast<T*>(ref)->push_back(std::move(type)); + } + + protected: + template<typename T> + ContainerInserter(T& ref_) : ref(&ref_), inserter(&InserterOf<T>) { } + + void Insert(Type type) + { + inserter(ref, std::move(type)); + } + }; + template<class Check> struct WorldObjectSearcher { @@ -211,15 +236,16 @@ namespace Trinity }; template<class Check> - struct WorldObjectListSearcher + struct WorldObjectListSearcher : ContainerInserter<WorldObject*> { uint32 i_mapTypeMask; uint32 i_phaseMask; - std::list<WorldObject*> &i_objects; Check& i_check; - WorldObjectListSearcher(WorldObject const* searcher, std::list<WorldObject*> &objects, Check & check, uint32 mapTypeMask = GRID_MAP_TYPE_MASK_ALL) - : i_mapTypeMask(mapTypeMask), i_phaseMask(searcher->GetPhaseMask()), i_objects(objects), i_check(check) { } + template<typename Container> + WorldObjectListSearcher(WorldObject const* searcher, Container& container, Check & check, uint32 mapTypeMask = GRID_MAP_TYPE_MASK_ALL) + : ContainerInserter<WorldObject*>(container), + i_mapTypeMask(mapTypeMask), i_phaseMask(searcher->GetPhaseMask()), i_check(check) { } void Visit(PlayerMapType &m); void Visit(CreatureMapType &m); @@ -321,14 +347,15 @@ namespace Trinity }; template<class Check> - struct GameObjectListSearcher + struct GameObjectListSearcher : ContainerInserter<GameObject*> { uint32 i_phaseMask; - std::list<GameObject*> &i_objects; Check& i_check; - GameObjectListSearcher(WorldObject const* searcher, std::list<GameObject*> &objects, Check & check) - : i_phaseMask(searcher->GetPhaseMask()), i_objects(objects), i_check(check) { } + template<typename Container> + GameObjectListSearcher(WorldObject const* searcher, Container& container, Check & check) + : ContainerInserter<GameObject*>(container), + i_phaseMask(searcher->GetPhaseMask()), i_check(check) { } void Visit(GameObjectMapType &m); @@ -393,14 +420,15 @@ namespace Trinity // All accepted by Check units if any template<class Check> - struct UnitListSearcher + struct UnitListSearcher : ContainerInserter<Unit*> { uint32 i_phaseMask; - std::list<Unit*> &i_objects; Check& i_check; - UnitListSearcher(WorldObject const* searcher, std::list<Unit*> &objects, Check & check) - : i_phaseMask(searcher->GetPhaseMask()), i_objects(objects), i_check(check) { } + template<typename Container> + UnitListSearcher(WorldObject const* searcher, Container& container, Check& check) + : ContainerInserter<Unit*>(container), + i_phaseMask(searcher->GetPhaseMask()), i_check(check) { } void Visit(PlayerMapType &m); void Visit(CreatureMapType &m); @@ -442,14 +470,15 @@ namespace Trinity }; template<class Check> - struct CreatureListSearcher + struct CreatureListSearcher : ContainerInserter<Creature*> { uint32 i_phaseMask; - std::list<Creature*> &i_objects; Check& i_check; - CreatureListSearcher(WorldObject const* searcher, std::list<Creature*> &objects, Check & check) - : i_phaseMask(searcher->GetPhaseMask()), i_objects(objects), i_check(check) { } + template<typename Container> + CreatureListSearcher(WorldObject const* searcher, Container& container, Check & check) + : ContainerInserter<Creature*>(container), + i_phaseMask(searcher->GetPhaseMask()), i_check(check) { } void Visit(CreatureMapType &m); @@ -493,14 +522,15 @@ namespace Trinity }; template<class Check> - struct PlayerListSearcher + struct PlayerListSearcher : ContainerInserter<Player*> { uint32 i_phaseMask; - std::list<Player*> &i_objects; Check& i_check; - PlayerListSearcher(WorldObject const* searcher, std::list<Player*> &objects, Check & check) - : i_phaseMask(searcher->GetPhaseMask()), i_objects(objects), i_check(check) { } + template<typename Container> + PlayerListSearcher(WorldObject const* searcher, Container& container, Check & check) + : ContainerInserter<Player*>(container), + i_phaseMask(searcher->GetPhaseMask()), i_check(check) { } void Visit(PlayerMapType &m); @@ -612,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) @@ -627,6 +658,7 @@ namespace Trinity return go->IsWithinDistInMap(i_unit, dist); } + private: Unit const* i_unit; uint32 i_focusId; @@ -637,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)) @@ -646,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)) @@ -668,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) @@ -682,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)) @@ -691,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 @@ -731,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) @@ -740,6 +777,7 @@ namespace Trinity } return false; } + private: Unit const* i_obj; float i_range; @@ -750,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))) @@ -759,6 +798,7 @@ namespace Trinity } return false; } + private: Unit const* i_obj; float i_range; @@ -768,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; @@ -787,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 NearestAttackableNoTotemUnitInObjectRangeCheck { public: - AnyUnfriendlyNoTotemUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) { } + NearestAttackableNoTotemUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) { } + bool operator()(Unit* u) { if (!u->IsAlive()) @@ -812,14 +855,19 @@ namespace Trinity if (u->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET) return false; - if (u->GetTypeId() == TYPEID_UNIT && ((Creature*)u)->IsTotem()) + if (u->GetTypeId() == TYPEID_UNIT && u->ToCreature()->IsTotem()) return false; 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->_IsValidAttackTarget(u, nullptr, i_obj)) + return false; + + i_range = i_obj->GetDistance(*u); + return true; } + private: WorldObject const* i_obj; Unit const* i_funit; @@ -830,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; @@ -847,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) @@ -869,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; @@ -892,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) && @@ -903,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; @@ -950,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; @@ -977,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)) @@ -1013,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)) @@ -1047,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; @@ -1078,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; @@ -1109,6 +1153,7 @@ namespace Trinity return true; } + private: Unit* const i_funit; Unit* const i_enemy; @@ -1137,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) @@ -1163,7 +1208,7 @@ namespace Trinity } return false; } - float GetLastRange() const { return i_range; } + private: WorldObject const& i_obj; uint32 i_entry; @@ -1171,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; @@ -1198,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) { @@ -1216,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; @@ -1264,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; } @@ -1334,7 +1389,8 @@ namespace Trinity { public: ObjectGUIDCheck(ObjectGuid GUID) : _GUID(GUID) { } - bool operator()(WorldObject* object) + + bool operator()(WorldObject* object) const { return object->GetGUID() == _GUID; } @@ -1347,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; @@ -1377,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/Grids/Notifiers/GridNotifiersImpl.h b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h index 340531c5883..5a3f41e5351 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h +++ b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h @@ -246,7 +246,7 @@ void Trinity::WorldObjectListSearcher<Check>::Visit(PlayerMapType &m) for (PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) if (i_check(itr->GetSource())) - i_objects.push_back(itr->GetSource()); + Insert(itr->GetSource()); } template<class Check> @@ -257,7 +257,7 @@ void Trinity::WorldObjectListSearcher<Check>::Visit(CreatureMapType &m) for (CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) if (i_check(itr->GetSource())) - i_objects.push_back(itr->GetSource()); + Insert(itr->GetSource()); } template<class Check> @@ -268,7 +268,7 @@ void Trinity::WorldObjectListSearcher<Check>::Visit(CorpseMapType &m) for (CorpseMapType::iterator itr=m.begin(); itr != m.end(); ++itr) if (i_check(itr->GetSource())) - i_objects.push_back(itr->GetSource()); + Insert(itr->GetSource()); } template<class Check> @@ -279,7 +279,7 @@ void Trinity::WorldObjectListSearcher<Check>::Visit(GameObjectMapType &m) for (GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) if (i_check(itr->GetSource())) - i_objects.push_back(itr->GetSource()); + Insert(itr->GetSource()); } template<class Check> @@ -290,7 +290,7 @@ void Trinity::WorldObjectListSearcher<Check>::Visit(DynamicObjectMapType &m) for (DynamicObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) if (i_check(itr->GetSource())) - i_objects.push_back(itr->GetSource()); + Insert(itr->GetSource()); } // Gameobject searchers @@ -334,7 +334,7 @@ void Trinity::GameObjectListSearcher<Check>::Visit(GameObjectMapType &m) for (GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) if (itr->GetSource()->InSamePhase(i_phaseMask)) if (i_check(itr->GetSource())) - i_objects.push_back(itr->GetSource()); + Insert(itr->GetSource()); } // Unit searchers @@ -411,7 +411,7 @@ void Trinity::UnitListSearcher<Check>::Visit(PlayerMapType &m) for (PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) if (itr->GetSource()->InSamePhase(i_phaseMask)) if (i_check(itr->GetSource())) - i_objects.push_back(itr->GetSource()); + Insert(itr->GetSource()); } template<class Check> @@ -420,7 +420,7 @@ void Trinity::UnitListSearcher<Check>::Visit(CreatureMapType &m) for (CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) if (itr->GetSource()->InSamePhase(i_phaseMask)) if (i_check(itr->GetSource())) - i_objects.push_back(itr->GetSource()); + Insert(itr->GetSource()); } // Creature searchers @@ -464,7 +464,7 @@ void Trinity::CreatureListSearcher<Check>::Visit(CreatureMapType &m) for (CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) if (itr->GetSource()->InSamePhase(i_phaseMask)) if (i_check(itr->GetSource())) - i_objects.push_back(itr->GetSource()); + Insert(itr->GetSource()); } template<class Check> @@ -473,7 +473,7 @@ void Trinity::PlayerListSearcher<Check>::Visit(PlayerMapType &m) for (PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) if (itr->GetSource()->InSamePhase(i_phaseMask)) if (i_check(itr->GetSource())) - i_objects.push_back(itr->GetSource()); + Insert(itr->GetSource()); } template<class Check> 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/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index b95d2ed1981..84b2da9b2ea 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -29,10 +29,13 @@ #include "SocialMgr.h" #include "Opcodes.h" -#define MAX_GUILD_BANK_TAB_TEXT_LEN 500 -#define EMBLEM_PRICE 10 * GOLD -std::string _GetGuildEventString(GuildEvents event) +size_t const MAX_GUILD_BANK_TAB_TEXT_LEN = 500; + +uint32 const EMBLEM_PRICE = 10 * GOLD; + +// only used in logs +char const* GetGuildEventString(GuildEvents event) { switch (event) { @@ -82,18 +85,13 @@ std::string _GetGuildEventString(GuildEvents event) return "<None>"; } -inline uint32 _GetGuildBankTabPrice(uint8 tabId) +inline uint32 GetGuildBankTabPrice(uint8 tabId) { - switch (tabId) - { - case 0: return 100; - case 1: return 250; - case 2: return 500; - case 3: return 1000; - case 4: return 2500; - case 5: return 5000; - default: return 0; - } + // these prices are in gold units, not copper + static uint32 const tabPrices[GUILD_BANK_MAX_TABS] = { 100, 250, 500, 1000, 2500, 5000 }; + ASSERT(tabId < GUILD_BANK_MAX_TABS); + + return tabPrices[tabId]; } void Guild::SendCommandResult(WorldSession* session, GuildCommandType type, GuildCommandError errCode, std::string const& param) @@ -121,7 +119,7 @@ void Guild::SendSaveEmblemResult(WorldSession* session, GuildEmblemError errCode Guild::LogHolder::~LogHolder() { // Cleanup - for (GuildLog::iterator itr = m_log.begin(); itr != m_log.end(); ++itr) + for (auto itr = m_log.begin(); itr != m_log.end(); ++itr) delete (*itr); } @@ -154,7 +152,7 @@ inline void Guild::LogHolder::AddEvent(SQLTransaction& trans, LogEntry* entry) inline void Guild::LogHolder::WritePacket(WorldPacket& data) const { data << uint8(m_log.size()); - for (GuildLog::const_iterator itr = m_log.begin(); itr != m_log.end(); ++itr) + for (auto itr = m_log.begin(); itr != m_log.end(); ++itr) (*itr)->WritePacket(data); } @@ -175,7 +173,7 @@ void Guild::EventLogEntry::SaveToDB(SQLTransaction& trans) const PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_EVENTLOG); stmt->setUInt32(0, m_guildId); stmt->setUInt32(1, m_guid); - CharacterDatabase.ExecuteOrAppend(trans, stmt); + trans->Append(stmt); uint8 index = 0; stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_EVENTLOG); @@ -186,7 +184,7 @@ void Guild::EventLogEntry::SaveToDB(SQLTransaction& trans) const stmt->setUInt32(++index, m_playerGuid2); stmt->setUInt8 (++index, m_newRank); stmt->setUInt64(++index, m_timestamp); - CharacterDatabase.ExecuteOrAppend(trans, stmt); + trans->Append(stmt); } void Guild::EventLogEntry::WritePacket(WorldPacket& data) const @@ -202,7 +200,7 @@ void Guild::EventLogEntry::WritePacket(WorldPacket& data) const if (m_eventType == GUILD_EVENT_LOG_PROMOTE_PLAYER || m_eventType == GUILD_EVENT_LOG_DEMOTE_PLAYER) data << uint8(m_newRank); // Event timestamp - data << uint32(::time(NULL) - m_timestamp); + data << uint32(::time(nullptr) - m_timestamp); } // BankEventLogEntry @@ -214,7 +212,7 @@ void Guild::BankEventLogEntry::SaveToDB(SQLTransaction& trans) const stmt->setUInt32( index, m_guildId); stmt->setUInt32(++index, m_guid); stmt->setUInt8 (++index, m_bankTabId); - CharacterDatabase.ExecuteOrAppend(trans, stmt); + trans->Append(stmt); index = 0; stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_EVENTLOG); @@ -227,7 +225,7 @@ void Guild::BankEventLogEntry::SaveToDB(SQLTransaction& trans) const stmt->setUInt16(++index, m_itemStackCount); stmt->setUInt8 (++index, m_destTabId); stmt->setUInt64(++index, m_timestamp); - CharacterDatabase.ExecuteOrAppend(trans, stmt); + trans->Append(stmt); } void Guild::BankEventLogEntry::WritePacket(WorldPacket& data) const @@ -252,7 +250,7 @@ void Guild::BankEventLogEntry::WritePacket(WorldPacket& data) const data << uint32(m_itemOrMoney); } - data << uint32(time(NULL) - m_timestamp); + data << uint32(time(nullptr) - m_timestamp); } // RankInfo @@ -434,14 +432,16 @@ bool Guild::BankTab::LoadItemFromDB(Field* fields) void Guild::BankTab::Delete(SQLTransaction& trans, bool removeItemsFromDB) { for (uint8 slotId = 0; slotId < GUILD_BANK_MAX_SLOTS; ++slotId) + { if (Item* pItem = m_items[slotId]) { pItem->RemoveFromWorld(); if (removeItemsFromDB) pItem->DeleteFromDB(trans); delete pItem; - pItem = NULL; + pItem = nullptr; } + } } inline void Guild::BankTab::WritePacket(WorldPacket& data) const @@ -491,12 +491,14 @@ bool Guild::BankTab::WriteSlotPacket(WorldPacket& data, uint8 slotId, bool ignor data << uint8(enchCount); // Number of enchantments for (uint32 i = PERM_ENCHANTMENT_SLOT; i < MAX_ENCHANTMENT_SLOT; ++i) + { if (uint32 enchId = pItem->GetEnchantmentId(EnchantmentSlot(i))) { data << uint8(i); data << uint32(enchId); ++enchCount; } + } data.put<uint8>(enchCountPos, enchCount); } return true; @@ -534,7 +536,7 @@ void Guild::BankTab::SetText(std::string const& text) } // Sets/removes contents of specified slot. -// If pItem == NULL contents are removed. +// If pItem == nullptr contents are removed. bool Guild::BankTab::SetItem(SQLTransaction& trans, uint8 slotId, Item* item) { if (slotId >= GUILD_BANK_MAX_SLOTS) @@ -546,7 +548,7 @@ bool Guild::BankTab::SetItem(SQLTransaction& trans, uint8 slotId, Item* item) stmt->setUInt32(0, m_guildId); stmt->setUInt8 (1, m_tabId); stmt->setUInt8 (2, slotId); - CharacterDatabase.ExecuteOrAppend(trans, stmt); + trans->Append(stmt); if (item) { @@ -555,13 +557,14 @@ bool Guild::BankTab::SetItem(SQLTransaction& trans, uint8 slotId, Item* item) stmt->setUInt8 (1, m_tabId); stmt->setUInt8 (2, slotId); stmt->setUInt32(3, item->GetGUID().GetCounter()); - CharacterDatabase.ExecuteOrAppend(trans, stmt); + trans->Append(stmt); item->SetGuidValue(ITEM_FIELD_CONTAINED, ObjectGuid::Empty); item->SetGuidValue(ITEM_FIELD_OWNER, ObjectGuid::Empty); item->FSetState(ITEM_NEW); item->SaveToDB(trans); // Not in inventory and can be saved standalone } + return true; } @@ -629,7 +632,7 @@ void Guild::Member::SetOfficerNote(std::string const& officerNote) CharacterDatabase.Execute(stmt); } -void Guild::Member::ChangeRank(uint8 newRank) +void Guild::Member::ChangeRank(SQLTransaction& trans, uint8 newRank) { m_rankId = newRank; @@ -640,7 +643,7 @@ void Guild::Member::ChangeRank(uint8 newRank) PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_RANK); stmt->setUInt8 (0, newRank); stmt->setUInt32(1, m_guid.GetCounter()); - CharacterDatabase.Execute(stmt); + CharacterDatabase.ExecuteOrAppend(trans, stmt); } void Guild::Member::SaveToDB(SQLTransaction& trans) const @@ -714,7 +717,7 @@ void Guild::Member::WritePacket(WorldPacket& data, bool sendOfficerNote) const << uint32(m_zoneId); if (!m_flags) - data << float(float(::time(NULL) - m_logoutTime) / DAY); + data << float(float(::time(nullptr) - m_logoutTime) / DAY); data << m_publicNote; @@ -839,7 +842,7 @@ void Guild::MoveItemData::LogAction(MoveItemData* pFrom) const inline void Guild::MoveItemData::CopySlots(SlotIds& ids) const { - for (ItemPosCountVec::const_iterator itr = m_vec.begin(); itr != m_vec.end(); ++itr) + for (auto itr = m_vec.begin(); itr != m_vec.end(); ++itr) ids.insert(uint8(itr->pos)); } @@ -853,16 +856,16 @@ bool Guild::PlayerMoveItemData::InitItem() if (m_pItem->IsNotEmptyBag()) { m_pPlayer->SendEquipError(EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS, m_pItem); - m_pItem = NULL; + m_pItem = nullptr; } // Bound items cannot be put into bank. else if (!m_pItem->CanBeTraded()) { m_pPlayer->SendEquipError(EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, m_pItem); - m_pItem = NULL; + m_pItem = nullptr; } } - return (m_pItem != NULL); + return (m_pItem != nullptr); } void Guild::PlayerMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* /*pOther*/, uint32 splitedAmount) @@ -877,7 +880,7 @@ void Guild::PlayerMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* { m_pPlayer->MoveItemFromInventory(m_container, m_slotId, true); m_pItem->DeleteFromInventoryDB(trans); - m_pItem = NULL; + m_pItem = nullptr; } } @@ -906,7 +909,7 @@ inline InventoryResult Guild::PlayerMoveItemData::CanStore(Item* pItem, bool swa bool Guild::BankMoveItemData::InitItem() { m_pItem = m_pGuild->_GetItem(m_container, m_slotId); - return (m_pItem != NULL); + return (m_pItem != nullptr); } bool Guild::BankMoveItemData::HasStoreRights(MoveItemData* pOther) const @@ -944,7 +947,7 @@ void Guild::BankMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* pO else { m_pGuild->_RemoveItem(trans, m_container, m_slotId); - m_pItem = NULL; + m_pItem = nullptr; } // Decrease amount of player's remaining items (if item is moved to different tab or to player) if (!pOther->IsBank() || pOther->GetContainer() != m_container) @@ -954,14 +957,14 @@ void Guild::BankMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* pO Item* Guild::BankMoveItemData::StoreItem(SQLTransaction& trans, Item* pItem) { if (!pItem) - return NULL; + return nullptr; BankTab* pTab = m_pGuild->GetBankTab(m_container); if (!pTab) - return NULL; + return nullptr; Item* pLastItem = pItem; - for (ItemPosCountVec::const_iterator itr = m_vec.begin(); itr != m_vec.end(); ) + for (auto itr = m_vec.begin(); itr != m_vec.end(); ) { ItemPosCount pos(*itr); ++itr; @@ -1025,7 +1028,7 @@ Item* Guild::BankMoveItemData::_StoreItem(SQLTransaction& trans, BankTab* pTab, if (pItem && pTab->SetItem(trans, slotId, pItem)) return pItem; - return NULL; + return nullptr; } // Tries to reserve space for source item. @@ -1065,10 +1068,10 @@ void Guild::BankMoveItemData::CanStoreItemInTab(Item* pItem, uint8 skipSlotId, b Item* pItemDest = m_pGuild->_GetItem(m_container, slotId); if (pItemDest == pItem) - pItemDest = NULL; + pItemDest = nullptr; // If merge skip empty, if not merge skip non-empty - if ((pItemDest != NULL) != merge) + if ((pItemDest != nullptr) != merge) continue; _ReserveSpace(slotId, pItem, pItemDest, count); @@ -1095,7 +1098,7 @@ InventoryResult Guild::BankMoveItemData::CanStore(Item* pItem, bool swap) Item* pItemDest = m_pGuild->_GetItem(m_container, m_slotId); // Ignore swapped item (this slot will be empty after move) if ((pItemDest == pItem) || swap) - pItemDest = NULL; + pItemDest = nullptr; if (!_ReserveSpace(m_slotId, pItem, pItemDest, count)) return EQUIP_ERR_ITEM_CANT_STACK; @@ -1128,30 +1131,30 @@ Guild::Guild(): m_createdDate(0), m_accountsNumber(0), m_bankMoney(0), - m_eventLog(NULL) + m_eventLog(nullptr) { memset(&m_bankEventLog, 0, (GUILD_BANK_MAX_TABS + 1) * sizeof(LogHolder*)); } Guild::~Guild() { - SQLTransaction temp(NULL); + SQLTransaction temp(nullptr); _DeleteBankItems(temp); // Cleanup delete m_eventLog; - m_eventLog = NULL; + m_eventLog = nullptr; for (uint8 tabId = 0; tabId <= GUILD_BANK_MAX_TABS; ++tabId) { delete m_bankEventLog[tabId]; - m_bankEventLog[tabId] = NULL; + m_bankEventLog[tabId] = nullptr; } - for (Members::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) { delete itr->second; - itr->second = NULL; + itr->second = nullptr; } } @@ -1172,7 +1175,7 @@ bool Guild::Create(Player* pLeader, std::string const& name) m_info = ""; m_motd = "No message set."; m_bankMoney = 0; - m_createdDate = ::time(NULL); + m_createdDate = ::time(nullptr); _CreateLogHolders(); TC_LOG_DEBUG("guild", "GUILD: creating guild [%s] for leader %s (%u)", @@ -1200,9 +1203,10 @@ bool Guild::Create(Player* pLeader, std::string const& name) stmt->setUInt64(++index, m_bankMoney); trans->Append(stmt); + _CreateDefaultGuildRanks(trans, pLeaderSession->GetSessionDbLocaleIndex()); // Create default ranks + bool ret = AddMember(trans, m_leaderGuid, GR_GUILDMASTER); // Add guildmaster + CharacterDatabase.CommitTransaction(trans); - _CreateDefaultGuildRanks(pLeaderSession->GetSessionDbLocaleIndex()); // Create default ranks - bool ret = AddMember(m_leaderGuid, GR_GUILDMASTER); // Add guildmaster if (ret) sScriptMgr->OnGuildCreate(this, pLeader, name); @@ -1217,15 +1221,15 @@ void Guild::Disband() sScriptMgr->OnGuildDisband(this); _BroadcastEvent(GE_DISBANDED, ObjectGuid::Empty); + + SQLTransaction trans = CharacterDatabase.BeginTransaction(); // Remove all members while (!m_members.empty()) { - Members::const_iterator itr = m_members.begin(); - DeleteMember(itr->second->GetGUID(), true); + auto itr = m_members.begin(); + DeleteMember(trans, itr->second->GetGUID(), true); } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD); stmt->setUInt32(0, m_id); trans->Append(stmt); @@ -1313,10 +1317,10 @@ void Guild::HandleRoster(WorldSession* session) data << m_info; data << uint32(_GetRanksSize()); - for (Ranks::const_iterator ritr = m_ranks.begin(); ritr != m_ranks.end(); ++ritr) + for (auto ritr = m_ranks.begin(); ritr != m_ranks.end(); ++ritr) ritr->WritePacket(data); - for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) itr->second->WritePacket(data, _HasRankRight(session->GetPlayer(), GR_RIGHT_VIEWOFFNOTE)); TC_LOG_DEBUG("guild", "SMSG_GUILD_ROSTER [%s]", session->GetPlayerInfo().c_str()); @@ -1420,7 +1424,9 @@ void Guild::HandleSetLeader(WorldSession* session, std::string const& name) if (Member* pNewLeader = GetMember(name)) { _SetLeaderGUID(pNewLeader); - pOldLeader->ChangeRank(GR_OFFICER); + + SQLTransaction trans(nullptr); + pOldLeader->ChangeRank(trans, GR_OFFICER); _BroadcastEvent(GE_LEADER_CHANGED, ObjectGuid::Empty, player->GetName().c_str(), name.c_str()); } } @@ -1436,11 +1442,8 @@ void Guild::HandleSetBankTabInfo(WorldSession* session, uint8 tabId, std::string return; } - char aux[2]; - sprintf(aux, "%u", tabId); - tab->SetInfo(name, icon); - _BroadcastEvent(GE_BANK_TAB_UPDATED, ObjectGuid::Empty, aux, name.c_str(), icon.c_str()); + _BroadcastEvent(GE_BANK_TAB_UPDATED, ObjectGuid::Empty, std::to_string(tabId).c_str(), name.c_str(), icon.c_str()); } void Guild::HandleSetMemberNote(WorldSession* session, std::string const& name, std::string const& note, bool officer) @@ -1472,12 +1475,10 @@ void Guild::HandleSetRankInfo(WorldSession* session, uint8 rankId, std::string c rankInfo->SetRights(rights); _SetRankBankMoneyPerDay(rankId, moneyPerDay); - for (GuildBankRightsAndSlotsVec::const_iterator itr = rightsAndSlots.begin(); itr != rightsAndSlots.end(); ++itr) + for (auto itr = rightsAndSlots.begin(); itr != rightsAndSlots.end(); ++itr) _SetRankBankTabRightsAndSlots(rankId, *itr); - char aux[2]; - sprintf(aux, "%u", rankId); - _BroadcastEvent(GE_RANK_UPDATED, ObjectGuid::Empty, aux, name.c_str()); + _BroadcastEvent(GE_RANK_UPDATED, ObjectGuid::Empty, std::to_string(rankId).c_str(), name.c_str()); } } @@ -1497,10 +1498,10 @@ void Guild::HandleBuyBankTab(WorldSession* session, uint8 tabId) if (tabId != _GetPurchasedTabsSize()) return; - uint32 tabCost = _GetGuildBankTabPrice(tabId) * GOLD; - if (!tabCost) + if (tabId >= GUILD_BANK_MAX_TABS) return; + uint32 tabCost = GetGuildBankTabPrice(tabId) * GOLD; if (!player->HasEnoughMoney(tabCost)) // Should not happen, this is checked by client return; @@ -1570,7 +1571,8 @@ void Guild::HandleAcceptMember(WorldSession* session) player->GetTeam() != sObjectMgr->GetPlayerTeamByGUID(GetLeaderGUID())) return; - AddMember(player->GetGUID()); + SQLTransaction trans(nullptr); + AddMember(trans, player->GetGUID()); } void Guild::HandleLeaveMember(WorldSession* session) @@ -1593,7 +1595,8 @@ void Guild::HandleLeaveMember(WorldSession* session) } else { - DeleteMember(player->GetGUID(), false, false); + SQLTransaction trans(nullptr); + DeleteMember(trans, player->GetGUID(), false, false); _LogEvent(GUILD_EVENT_LOG_LEAVE_GUILD, player->GetGUID().GetCounter()); _BroadcastEvent(GE_LEFT, player->GetGUID(), player->GetName().c_str()); @@ -1627,8 +1630,10 @@ void Guild::HandleRemoveMember(WorldSession* session, std::string const& name) else { ObjectGuid guid = member->GetGUID(); + // After call to DeleteMember pointer to member becomes invalid - DeleteMember(guid, false, true); + SQLTransaction trans(nullptr); + DeleteMember(trans, guid, false, true); _LogEvent(GUILD_EVENT_LOG_UNINVITE_PLAYER, player->GetGUID().GetCounter(), guid.GetCounter()); _BroadcastEvent(GE_REMOVED, ObjectGuid::Empty, name.c_str(), player->GetName().c_str()); } @@ -1682,7 +1687,8 @@ void Guild::HandleUpdateMemberRank(WorldSession* session, std::string const& nam } uint32 newRankId = member->GetRankId() + (demote ? 1 : -1); - member->ChangeRank(newRankId); + SQLTransaction trans(nullptr); + member->ChangeRank(trans, newRankId); _LogEvent(demote ? GUILD_EVENT_LOG_DEMOTE_PLAYER : GUILD_EVENT_LOG_PROMOTE_PLAYER, player->GetGUID().GetCounter(), member->GetGUID().GetCounter(), newRankId); _BroadcastEvent(demote ? GE_DEMOTION : GE_PROMOTION, ObjectGuid::Empty, player->GetName().c_str(), name.c_str(), _GetRankName(newRankId).c_str()); } @@ -1696,12 +1702,11 @@ void Guild::HandleAddNewRank(WorldSession* session, std::string const& name) // Only leader can add new rank if (_IsLeader(session->GetPlayer())) - if (_CreateRank(name, GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK)) - { - char aux[2]; - sprintf(aux, "%u", size); - _BroadcastEvent(GE_RANK_UPDATED, ObjectGuid::Empty, aux, name.c_str()); - } + { + SQLTransaction trans(nullptr); + if (_CreateRank(trans, name, GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK)) + _BroadcastEvent(GE_RANK_UPDATED, ObjectGuid::Empty, std::to_string(size).c_str(), name.c_str()); + } } void Guild::HandleRemoveLowestRank(WorldSession* session) @@ -1973,10 +1978,11 @@ void Guild::LoadRankFromDB(Field* fields) bool Guild::LoadMemberFromDB(Field* fields) { ObjectGuid::LowType lowguid = fields[1].GetUInt32(); - Member *member = new Member(m_id, ObjectGuid(HighGuid::Player, lowguid), fields[2].GetUInt8()); + Member* member = new Member(m_id, ObjectGuid(HighGuid::Player, lowguid), fields[2].GetUInt8()); if (!member->LoadFromDB(fields)) { - _DeleteMemberFromDB(lowguid); + SQLTransaction trans(nullptr); + _DeleteMemberFromDB(trans, lowguid); delete member; return false; } @@ -2080,6 +2086,8 @@ bool Guild::Validate() // Min ranks count is 5 and max is 10. bool broken_ranks = false; uint8 ranks = _GetRanksSize(); + + SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (ranks < GUILD_RANKS_MIN_COUNT || ranks > GUILD_RANKS_MAX_COUNT) { TC_LOG_ERROR("guild", "Guild %u has invalid number of ranks, creating new...", m_id); @@ -2096,24 +2104,20 @@ bool Guild::Validate() broken_ranks = true; } else - { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); rankInfo->CreateMissingTabsIfNeeded(_GetPurchasedTabsSize(), trans, true); - CharacterDatabase.CommitTransaction(trans); - } } } if (broken_ranks) { m_ranks.clear(); - _CreateDefaultGuildRanks(DEFAULT_LOCALE); + _CreateDefaultGuildRanks(trans, DEFAULT_LOCALE); } // Validate members' data - for (Members::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) if (itr->second->GetRankId() > _GetRanksSize()) - itr->second->ChangeRank(_GetLowestRankId()); + itr->second->ChangeRank(trans, _GetLowestRankId()); // Repair the structure of the guild. // If the guildmaster doesn't exist or isn't member of the guild @@ -2121,7 +2125,8 @@ bool Guild::Validate() Member* pLeader = GetMember(m_leaderGuid); if (!pLeader) { - DeleteMember(m_leaderGuid); + SQLTransaction trans(nullptr); + DeleteMember(trans, m_leaderGuid); // If no more members left, disband guild if (m_members.empty()) { @@ -2134,10 +2139,12 @@ bool Guild::Validate() // Check config if multiple guildmasters are allowed if (!sConfigMgr->GetBoolDefault("Guild.AllowMultipleGuildMaster", 0)) - for (Members::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) if (itr->second->GetRankId() == GR_GUILDMASTER && !itr->second->IsSamePlayer(m_leaderGuid)) - itr->second->ChangeRank(GR_OFFICER); + itr->second->ChangeRank(trans, GR_OFFICER); + if (trans->GetSize() > 0) + CharacterDatabase.CommitTransaction(trans); _UpdateAccountsNumber(); return true; } @@ -2148,8 +2155,8 @@ void Guild::BroadcastToGuild(WorldSession* session, bool officerOnly, std::strin if (session && session->GetPlayer() && _HasRankRight(session->GetPlayer(), officerOnly ? GR_RIGHT_OFFCHATSPEAK : GR_RIGHT_GCHATSPEAK)) { WorldPacket data; - ChatHandler::BuildChatPacket(data, officerOnly ? CHAT_MSG_OFFICER : CHAT_MSG_GUILD, Language(language), session->GetPlayer(), NULL, msg); - for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + ChatHandler::BuildChatPacket(data, officerOnly ? CHAT_MSG_OFFICER : CHAT_MSG_GUILD, Language(language), session->GetPlayer(), nullptr, msg); + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) if (Player* player = itr->second->FindConnectedPlayer()) if (player->GetSession() && _HasRankRight(player, officerOnly ? GR_RIGHT_OFFCHATLISTEN : GR_RIGHT_GCHATLISTEN) && !player->GetSocial()->HasIgnore(session->GetPlayer()->GetGUID().GetCounter())) @@ -2159,7 +2166,7 @@ void Guild::BroadcastToGuild(WorldSession* session, bool officerOnly, std::strin void Guild::BroadcastPacketToRank(WorldPacket* packet, uint8 rankId) const { - for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) if (itr->second->IsRank(rankId)) if (Player* player = itr->second->FindConnectedPlayer()) player->GetSession()->SendPacket(packet); @@ -2167,7 +2174,7 @@ void Guild::BroadcastPacketToRank(WorldPacket* packet, uint8 rankId) const void Guild::BroadcastPacket(WorldPacket* packet) const { - for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) if (Player* player = itr->second->FindPlayer()) player->GetSession()->SendPacket(packet); } @@ -2179,7 +2186,7 @@ void Guild::MassInviteToEvent(WorldSession* session, uint32 minLevel, uint32 max WorldPacket data(SMSG_CALENDAR_FILTER_GUILD); data << uint32(count); // count placeholder - for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) { // not sure if needed, maybe client checks it as well if (count >= CALENDAR_MAX_INVITES) @@ -2206,7 +2213,7 @@ void Guild::MassInviteToEvent(WorldSession* session, uint32 minLevel, uint32 max } // Members handling -bool Guild::AddMember(ObjectGuid guid, uint8 rankId) +bool Guild::AddMember(SQLTransaction& trans, ObjectGuid guid, uint8 rankId) { Player* player = ObjectAccessor::FindConnectedPlayer(guid); // Player cannot be in guild @@ -2269,7 +2276,6 @@ bool Guild::AddMember(ObjectGuid guid, uint8 rankId) m_members[lowguid] = member; } - SQLTransaction trans(NULL); member->SaveToDB(trans); _UpdateAccountsNumber(); @@ -2282,7 +2288,7 @@ bool Guild::AddMember(ObjectGuid guid, uint8 rankId) return true; } -void Guild::DeleteMember(ObjectGuid guid, bool isDisbanding, bool isKicked, bool canDeleteGuild) +void Guild::DeleteMember(SQLTransaction& trans, ObjectGuid guid, bool isDisbanding, bool isKicked, bool canDeleteGuild) { ObjectGuid::LowType lowguid = guid.GetCounter(); Player* player = ObjectAccessor::FindConnectedPlayer(guid); @@ -2291,9 +2297,9 @@ void Guild::DeleteMember(ObjectGuid guid, bool isDisbanding, bool isKicked, bool // or when he is removed from guild by gm command if (m_leaderGuid == guid && !isDisbanding) { - Member* oldLeader = NULL; - Member* newLeader = NULL; - for (Guild::Members::iterator i = m_members.begin(); i != m_members.end(); ++i) + Member* oldLeader = nullptr; + Member* newLeader = nullptr; + for (auto i = m_members.begin(); i != m_members.end(); ++i) { if (i->first == lowguid) oldLeader = i->second; @@ -2336,19 +2342,22 @@ void Guild::DeleteMember(ObjectGuid guid, bool isDisbanding, bool isKicked, bool player->SetRank(0); } - _DeleteMemberFromDB(lowguid); + _DeleteMemberFromDB(trans, lowguid); if (!isDisbanding) _UpdateAccountsNumber(); } -bool Guild::ChangeMemberRank(ObjectGuid guid, uint8 newRank) +bool Guild::ChangeMemberRank(SQLTransaction& trans, ObjectGuid guid, uint8 newRank) { if (newRank <= _GetLowestRankId()) // Validate rank (allow only existing ranks) + { if (Member* member = GetMember(guid)) { - member->ChangeRank(newRank); + member->ChangeRank(trans, newRank); return true; } + } + return false; } @@ -2386,7 +2395,7 @@ void Guild::SetBankTabText(uint8 tabId, std::string const& text) if (BankTab* pTab = GetBankTab(tabId)) { pTab->SetText(text); - pTab->SendText(this, NULL); + pTab->SendText(this, nullptr); } } @@ -2416,30 +2425,32 @@ void Guild::_CreateNewBankTab() trans->Append(stmt); ++tabId; - for (Ranks::iterator itr = m_ranks.begin(); itr != m_ranks.end(); ++itr) + for (auto itr = m_ranks.begin(); itr != m_ranks.end(); ++itr) (*itr).CreateMissingTabsIfNeeded(tabId, trans, false); CharacterDatabase.CommitTransaction(trans); } -void Guild::_CreateDefaultGuildRanks(LocaleConstant loc) +void Guild::_CreateDefaultGuildRanks(SQLTransaction& trans, LocaleConstant loc) { + ASSERT(trans); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_RANKS); stmt->setUInt32(0, m_id); - CharacterDatabase.Execute(stmt); + trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_RIGHTS); stmt->setUInt32(0, m_id); - CharacterDatabase.Execute(stmt); + trans->Append(stmt); - _CreateRank(sObjectMgr->GetTrinityString(LANG_GUILD_MASTER, loc), GR_RIGHT_ALL); - _CreateRank(sObjectMgr->GetTrinityString(LANG_GUILD_OFFICER, loc), GR_RIGHT_ALL); - _CreateRank(sObjectMgr->GetTrinityString(LANG_GUILD_VETERAN, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); - _CreateRank(sObjectMgr->GetTrinityString(LANG_GUILD_MEMBER, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); - _CreateRank(sObjectMgr->GetTrinityString(LANG_GUILD_INITIATE, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); + _CreateRank(trans, sObjectMgr->GetTrinityString(LANG_GUILD_MASTER, loc), GR_RIGHT_ALL); + _CreateRank(trans, sObjectMgr->GetTrinityString(LANG_GUILD_OFFICER, loc), GR_RIGHT_ALL); + _CreateRank(trans, sObjectMgr->GetTrinityString(LANG_GUILD_VETERAN, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); + _CreateRank(trans, sObjectMgr->GetTrinityString(LANG_GUILD_MEMBER, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); + _CreateRank(trans, sObjectMgr->GetTrinityString(LANG_GUILD_INITIATE, loc), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); } -bool Guild::_CreateRank(std::string const& name, uint32 rights) +bool Guild::_CreateRank(SQLTransaction& trans, std::string const& name, uint32 rights) { uint8 newRankId = _GetRanksSize(); if (newRankId >= GUILD_RANKS_MAX_COUNT) @@ -2449,10 +2460,15 @@ bool Guild::_CreateRank(std::string const& name, uint32 rights) RankInfo info(m_id, newRankId, name, rights, 0); m_ranks.push_back(info); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + bool const isInTransaction = bool(trans); + if (!isInTransaction) + trans = CharacterDatabase.BeginTransaction(); + info.CreateMissingTabsIfNeeded(_GetPurchasedTabsSize(), trans); info.SaveToDB(trans); - CharacterDatabase.CommitTransaction(trans); + + if (!isInTransaction) + CharacterDatabase.CommitTransaction(trans); return true; } @@ -2463,7 +2479,7 @@ void Guild::_UpdateAccountsNumber() { // We use a set to be sure each element will be unique std::set<uint32> accountsIdSet; - for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) accountsIdSet.insert(itr->second->GetAccountId()); m_accountsNumber = accountsIdSet.size(); @@ -2487,7 +2503,7 @@ void Guild::_DeleteBankItems(SQLTransaction& trans, bool removeItemsFromDB) { m_bankTabs[tabId]->Delete(trans, removeItemsFromDB); delete m_bankTabs[tabId]; - m_bankTabs[tabId] = NULL; + m_bankTabs[tabId] = nullptr; } m_bankTabs.clear(); } @@ -2516,13 +2532,16 @@ void Guild::_SetLeaderGUID(Member* pLeader) if (!pLeader) return; + SQLTransaction trans = CharacterDatabase.BeginTransaction(); m_leaderGuid = pLeader->GetGUID(); - pLeader->ChangeRank(GR_GUILDMASTER); + pLeader->ChangeRank(trans, GR_GUILDMASTER); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_LEADER); stmt->setUInt32(0, m_leaderGuid.GetCounter()); stmt->setUInt32(1, m_id); - CharacterDatabase.Execute(stmt); + trans->Append(stmt); + + CharacterDatabase.CommitTransaction(trans); } void Guild::_SetRankBankMoneyPerDay(uint8 rankId, uint32 moneyPerDay) @@ -2670,13 +2689,13 @@ inline Item* Guild::_GetItem(uint8 tabId, uint8 slotId) const { if (const BankTab* tab = GetBankTab(tabId)) return tab->GetItem(slotId); - return NULL; + return nullptr; } inline void Guild::_RemoveItem(SQLTransaction& trans, uint8 tabId, uint8 slotId) { if (BankTab* pTab = GetBankTab(tabId)) - pTab->SetItem(trans, slotId, NULL); + pTab->SetItem(trans, slotId, nullptr); } void Guild::_MoveItems(MoveItemData* pSrc, MoveItemData* pDest, uint32 splitedAmount) @@ -2717,7 +2736,7 @@ void Guild::_MoveItems(MoveItemData* pSrc, MoveItemData* pDest, uint32 splitedAm } else // 6. No split { - // 6.1. Try to merge items in destination (pDest->GetItem() == NULL) + // 6.1. Try to merge items in destination (pDest->GetItem() == nullptr) if (!_DoItemsMove(pSrc, pDest, false)) // Item could not be merged { // 6.2. Try to swap items @@ -2732,7 +2751,7 @@ void Guild::_MoveItems(MoveItemData* pSrc, MoveItemData* pDest, uint32 splitedAm if (!pDest->HasWithdrawRights(pSrc)) return; // Player has no rights to withdraw item from destination (opposite direction) - // 6.2.3. Swap items (pDest->GetItem() != NULL) + // 6.2.3. Swap items (pDest->GetItem() != nullptr) _DoItemsMove(pSrc, pDest, true); } } @@ -2743,7 +2762,7 @@ void Guild::_MoveItems(MoveItemData* pSrc, MoveItemData* pDest, uint32 splitedAm bool Guild::_DoItemsMove(MoveItemData* pSrc, MoveItemData* pDest, bool sendError, uint32 splitedAmount) { Item* pDestItem = pDest->GetItem(); - bool swap = (pDestItem != NULL); + bool swap = (pDestItem != nullptr); Item* pSrcItem = pSrc->GetItem(splitedAmount != 0); // 1. Can store source item in destination @@ -2832,7 +2851,7 @@ void Guild::_SendBankContentUpdate(MoveItemData* pSrc, MoveItemData* pDest) cons void Guild::_SendBankContentUpdate(uint8 tabId, SlotIds slots) const { - _SendBankList(NULL, tabId, false, &slots); + _SendBankList(nullptr, tabId, false, &slots); } void Guild::_BroadcastEvent(GuildEvents guildEvent, ObjectGuid guid, const char* param1, const char* param2, const char* param3) const @@ -2855,10 +2874,10 @@ void Guild::_BroadcastEvent(GuildEvents guildEvent, ObjectGuid guid, const char* BroadcastPacket(&data); - TC_LOG_DEBUG("guild", "SMSG_GUILD_EVENT [Broadcast] Event: %s (%u)", _GetGuildEventString(guildEvent).c_str(), guildEvent); + TC_LOG_DEBUG("guild", "SMSG_GUILD_EVENT [Broadcast] Event: %s (%u)", GetGuildEventString(guildEvent), guildEvent); } -void Guild::_SendBankList(WorldSession* session /* = NULL*/, uint8 tabId /*= 0*/, bool sendAllSlots /*= false*/, SlotIds *slots /*= NULL*/) const +void Guild::_SendBankList(WorldSession* session /* = nullptr*/, uint8 tabId /*= 0*/, bool sendAllSlots /*= false*/, SlotIds *slots /*= nullptr*/) const { WorldPacket data(SMSG_GUILD_BANK_LIST, 500); data << uint64(m_bankMoney); @@ -2882,7 +2901,7 @@ void Guild::_SendBankList(WorldSession* session /* = NULL*/, uint8 tabId /*= 0*/ else if (slots && !slots->empty()) { data << uint8(slots->size()); - for (SlotIds::const_iterator itr = slots->begin(); itr != slots->end(); ++itr) + for (auto itr = slots->begin(); itr != slots->end(); ++itr) tab->WriteSlotPacket(data, *itr, false); } else @@ -2900,7 +2919,7 @@ void Guild::_SendBankList(WorldSession* session /* = NULL*/, uint8 tabId /*= 0*/ } else /// @todo - Probably this is just sent to session + those that have sent CMSG_GUILD_BANKER_ACTIVATE { - for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) { if (!_MemberHasTabRights(itr->second->GetGUID(), tabId, GUILD_BANK_RIGHT_VIEW_TAB)) continue; @@ -2919,7 +2938,7 @@ void Guild::_SendBankList(WorldSession* session /* = NULL*/, uint8 tabId /*= 0*/ void Guild::ResetTimes() { - for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) itr->second->ResetValues(); _BroadcastEvent(GE_BANK_TAB_AND_MONEY_UPDATED, ObjectGuid::Empty); diff --git a/src/server/game/Guilds/Guild.h b/src/server/game/Guilds/Guild.h index e25a3201957..742923f51a1 100644 --- a/src/server/game/Guilds/Guild.h +++ b/src/server/game/Guilds/Guild.h @@ -225,54 +225,54 @@ enum GuildMemberFlags // Emblem info class TC_GAME_API EmblemInfo { -public: - EmblemInfo() : m_style(0), m_color(0), m_borderStyle(0), m_borderColor(0), m_backgroundColor(0) { } - - void LoadFromDB(Field* fields); - void SaveToDB(ObjectGuid::LowType guildId) const; - void ReadPacket(WorldPacket& recv); - void WritePacket(WorldPacket& data) const; - - uint32 GetStyle() const { return m_style; } - uint32 GetColor() const { return m_color; } - uint32 GetBorderStyle() const { return m_borderStyle; } - uint32 GetBorderColor() const { return m_borderColor; } - uint32 GetBackgroundColor() const { return m_backgroundColor; } - -private: - uint32 m_style; - uint32 m_color; - uint32 m_borderStyle; - uint32 m_borderColor; - uint32 m_backgroundColor; + public: + EmblemInfo() : m_style(0), m_color(0), m_borderStyle(0), m_borderColor(0), m_backgroundColor(0) { } + + void LoadFromDB(Field* fields); + void SaveToDB(ObjectGuid::LowType guildId) const; + void ReadPacket(WorldPacket& recv); + void WritePacket(WorldPacket& data) const; + + uint32 GetStyle() const { return m_style; } + uint32 GetColor() const { return m_color; } + uint32 GetBorderStyle() const { return m_borderStyle; } + uint32 GetBorderColor() const { return m_borderColor; } + uint32 GetBackgroundColor() const { return m_backgroundColor; } + + private: + uint32 m_style; + uint32 m_color; + uint32 m_borderStyle; + uint32 m_borderColor; + uint32 m_backgroundColor; }; // Structure for storing guild bank rights and remaining slots together. class GuildBankRightsAndSlots { -public: - GuildBankRightsAndSlots() : tabId(TAB_UNDEFINED), rights(0), slots(0) { } - GuildBankRightsAndSlots(uint8 _tabId) : tabId(_tabId), rights(0), slots(0) { } - GuildBankRightsAndSlots(uint8 _tabId, uint8 _rights, uint32 _slots) : tabId(_tabId), rights(_rights), slots(_slots) { } - - void SetGuildMasterValues() - { - rights = GUILD_BANK_RIGHT_FULL; - slots = uint32(GUILD_WITHDRAW_SLOT_UNLIMITED); - } - - void SetTabId(uint8 _tabId) { tabId = _tabId; } - void SetSlots(uint32 _slots) { slots = _slots; } - void SetRights(uint8 _rights) { rights = _rights; } - - int8 GetTabId() const { return tabId; } - int32 GetSlots() const { return slots; } - int8 GetRights() const { return rights; } - -private: - uint8 tabId; - uint8 rights; - uint32 slots; + public: + GuildBankRightsAndSlots() : tabId(TAB_UNDEFINED), rights(0), slots(0) { } + GuildBankRightsAndSlots(uint8 _tabId) : tabId(_tabId), rights(0), slots(0) { } + GuildBankRightsAndSlots(uint8 _tabId, uint8 _rights, uint32 _slots) : tabId(_tabId), rights(_rights), slots(_slots) { } + + void SetGuildMasterValues() + { + rights = GUILD_BANK_RIGHT_FULL; + slots = uint32(GUILD_WITHDRAW_SLOT_UNLIMITED); + } + + void SetTabId(uint8 _tabId) { tabId = _tabId; } + void SetSlots(uint32 _slots) { slots = _slots; } + void SetRights(uint8 _rights) { rights = _rights; } + + int8 GetTabId() const { return tabId; } + int32 GetSlots() const { return slots; } + int8 GetRights() const { return rights; } + + private: + uint8 tabId; + uint8 rights; + uint32 slots; }; typedef std::vector <GuildBankRightsAndSlots> GuildBankRightsAndSlotsVec; @@ -281,577 +281,577 @@ typedef std::set <uint8> SlotIds; class TC_GAME_API Guild { -private: - // Class representing guild member - class Member - { - public: - Member(ObjectGuid::LowType guildId, ObjectGuid guid, uint8 rankId) : - m_guildId(guildId), - m_guid(guid), - m_zoneId(0), - m_level(0), - m_class(0), - m_flags(GUILDMEMBER_STATUS_NONE), - m_logoutTime(::time(NULL)), - m_accountId(0), - m_rankId(rankId) + private: + // Class representing guild member + class Member { - memset(m_bankWithdraw, 0, (GUILD_BANK_MAX_TABS + 1) * sizeof(int32)); - } - - void SetStats(Player* player); - void SetStats(std::string const& name, uint8 level, uint8 _class, uint32 zoneId, uint32 accountId); - bool CheckStats() const; + public: + Member(ObjectGuid::LowType guildId, ObjectGuid guid, uint8 rankId) : + m_guildId(guildId), + m_guid(guid), + m_zoneId(0), + m_level(0), + m_class(0), + m_flags(GUILDMEMBER_STATUS_NONE), + m_logoutTime(::time(nullptr)), + m_accountId(0), + m_rankId(rankId) + { + memset(m_bankWithdraw, 0, (GUILD_BANK_MAX_TABS + 1) * sizeof(int32)); + } + + void SetStats(Player* player); + void SetStats(std::string const& name, uint8 level, uint8 _class, uint32 zoneId, uint32 accountId); + bool CheckStats() const; + + void SetPublicNote(std::string const& publicNote); + void SetOfficerNote(std::string const& officerNote); + void SetZoneID(uint32 id) { m_zoneId = id; } + void SetLevel(uint8 var) { m_level = var; } + + void AddFlag(uint8 var) { m_flags |= var; } + void RemFlag(uint8 var) { m_flags &= ~var; } + void ResetFlags() { m_flags = GUILDMEMBER_STATUS_NONE; } + + bool LoadFromDB(Field* fields); + void SaveToDB(SQLTransaction& trans) const; + void WritePacket(WorldPacket& data, bool sendOfficerNote) const; + + ObjectGuid GetGUID() const { return m_guid; } + std::string const& GetName() const { return m_name; } + uint32 GetAccountId() const { return m_accountId; } + uint8 GetRankId() const { return m_rankId; } + uint64 GetLogoutTime() const { return m_logoutTime; } + std::string GetPublicNote() const { return m_publicNote; } + std::string GetOfficerNote() const { return m_officerNote; } + uint8 GetClass() const { return m_class; } + uint8 GetLevel() const { return m_level; } + uint8 GetFlags() const { return m_flags; } + uint32 GetZoneId() const { return m_zoneId; } + bool IsOnline() const { return (m_flags & GUILDMEMBER_STATUS_ONLINE); } + + void ChangeRank(SQLTransaction& trans, uint8 newRank); + + inline void UpdateLogoutTime() { m_logoutTime = ::time(nullptr); } + inline bool IsRank(uint8 rankId) const { return m_rankId == rankId; } + inline bool IsRankNotLower(uint8 rankId) const { return m_rankId <= rankId; } + inline bool IsSamePlayer(ObjectGuid guid) const { return m_guid == guid; } + + void UpdateBankWithdrawValue(SQLTransaction& trans, uint8 tabId, uint32 amount); + int32 GetBankWithdrawValue(uint8 tabId) const; + void ResetValues(); + + inline Player* FindPlayer() const { return ObjectAccessor::FindPlayer(m_guid); } + inline Player* FindConnectedPlayer() const { return ObjectAccessor::FindConnectedPlayer(m_guid); } + + private: + ObjectGuid::LowType m_guildId; + // Fields from characters table + ObjectGuid m_guid; + std::string m_name; + uint32 m_zoneId; + uint8 m_level; + uint8 m_class; + uint8 m_flags; + uint64 m_logoutTime; + uint32 m_accountId; + // Fields from guild_member table + uint8 m_rankId; + std::string m_publicNote; + std::string m_officerNote; + + int32 m_bankWithdraw[GUILD_BANK_MAX_TABS + 1]; + }; + + // Base class for event entries + class LogEntry + { + public: + LogEntry(ObjectGuid::LowType guildId, uint32 guid) : m_guildId(guildId), m_guid(guid), m_timestamp(::time(nullptr)) { } + LogEntry(ObjectGuid::LowType guildId, uint32 guid, time_t timestamp) : m_guildId(guildId), m_guid(guid), m_timestamp(timestamp) { } + virtual ~LogEntry() { } - void SetPublicNote(std::string const& publicNote); - void SetOfficerNote(std::string const& officerNote); - void SetZoneID(uint32 id) { m_zoneId = id; } - void SetLevel(uint8 var) { m_level = var; } + uint32 GetGUID() const { return m_guid; } + uint64 GetTimestamp() const { return m_timestamp; } - void AddFlag(uint8 var) { m_flags |= var; } - void RemFlag(uint8 var) { m_flags &= ~var; } - void ResetFlags() { m_flags = GUILDMEMBER_STATUS_NONE; } + virtual void SaveToDB(SQLTransaction& trans) const = 0; + virtual void WritePacket(WorldPacket& data) const = 0; - bool LoadFromDB(Field* fields); - void SaveToDB(SQLTransaction& trans) const; - void WritePacket(WorldPacket& data, bool sendOfficerNote) const; + protected: + ObjectGuid::LowType m_guildId; + uint32 m_guid; + uint64 m_timestamp; + }; - ObjectGuid GetGUID() const { return m_guid; } - std::string const& GetName() const { return m_name; } - uint32 GetAccountId() const { return m_accountId; } - uint8 GetRankId() const { return m_rankId; } - uint64 GetLogoutTime() const { return m_logoutTime; } - std::string GetPublicNote() const { return m_publicNote; } - std::string GetOfficerNote() const { return m_officerNote; } - uint8 GetClass() const { return m_class; } - uint8 GetLevel() const { return m_level; } - uint8 GetFlags() const { return m_flags; } - uint32 GetZoneId() const { return m_zoneId; } - bool IsOnline() const { return (m_flags & GUILDMEMBER_STATUS_ONLINE); } - - void ChangeRank(uint8 newRank); - - inline void UpdateLogoutTime() { m_logoutTime = ::time(NULL); } - inline bool IsRank(uint8 rankId) const { return m_rankId == rankId; } - inline bool IsRankNotLower(uint8 rankId) const { return m_rankId <= rankId; } - inline bool IsSamePlayer(ObjectGuid guid) const { return m_guid == guid; } - - void UpdateBankWithdrawValue(SQLTransaction& trans, uint8 tabId, uint32 amount); - int32 GetBankWithdrawValue(uint8 tabId) const; - void ResetValues(); - - inline Player* FindPlayer() const { return ObjectAccessor::FindPlayer(m_guid); } - inline Player* FindConnectedPlayer() const { return ObjectAccessor::FindConnectedPlayer(m_guid); } + // Event log entry + class EventLogEntry : public LogEntry + { + public: + EventLogEntry(ObjectGuid::LowType guildId, uint32 guid, GuildEventLogTypes eventType, ObjectGuid::LowType playerGuid1, ObjectGuid::LowType playerGuid2, uint8 newRank) : + LogEntry(guildId, guid), m_eventType(eventType), m_playerGuid1(playerGuid1), m_playerGuid2(playerGuid2), m_newRank(newRank) { } - private: - ObjectGuid::LowType m_guildId; - // Fields from characters table - ObjectGuid m_guid; - std::string m_name; - uint32 m_zoneId; - uint8 m_level; - uint8 m_class; - uint8 m_flags; - uint64 m_logoutTime; - uint32 m_accountId; - // Fields from guild_member table - uint8 m_rankId; - std::string m_publicNote; - std::string m_officerNote; - - int32 m_bankWithdraw[GUILD_BANK_MAX_TABS + 1]; - }; - - // Base class for event entries - class LogEntry - { - public: - LogEntry(ObjectGuid::LowType guildId, uint32 guid) : m_guildId(guildId), m_guid(guid), m_timestamp(::time(NULL)) { } - LogEntry(ObjectGuid::LowType guildId, uint32 guid, time_t timestamp) : m_guildId(guildId), m_guid(guid), m_timestamp(timestamp) { } - virtual ~LogEntry() { } + EventLogEntry(ObjectGuid::LowType guildId, uint32 guid, time_t timestamp, GuildEventLogTypes eventType, ObjectGuid::LowType playerGuid1, ObjectGuid::LowType playerGuid2, uint8 newRank) : + LogEntry(guildId, guid, timestamp), m_eventType(eventType), m_playerGuid1(playerGuid1), m_playerGuid2(playerGuid2), m_newRank(newRank) { } - uint32 GetGUID() const { return m_guid; } - uint64 GetTimestamp() const { return m_timestamp; } + ~EventLogEntry() { } - virtual void SaveToDB(SQLTransaction& trans) const = 0; - virtual void WritePacket(WorldPacket& data) const = 0; + void SaveToDB(SQLTransaction& trans) const override; + void WritePacket(WorldPacket& data) const override; - protected: - ObjectGuid::LowType m_guildId; - uint32 m_guid; - uint64 m_timestamp; - }; - - // Event log entry - class EventLogEntry : public LogEntry - { - public: - EventLogEntry(ObjectGuid::LowType guildId, uint32 guid, GuildEventLogTypes eventType, ObjectGuid::LowType playerGuid1, ObjectGuid::LowType playerGuid2, uint8 newRank) : - LogEntry(guildId, guid), m_eventType(eventType), m_playerGuid1(playerGuid1), m_playerGuid2(playerGuid2), m_newRank(newRank) { } + private: + GuildEventLogTypes m_eventType; + ObjectGuid::LowType m_playerGuid1; + ObjectGuid::LowType m_playerGuid2; + uint8 m_newRank; + }; - EventLogEntry(ObjectGuid::LowType guildId, uint32 guid, time_t timestamp, GuildEventLogTypes eventType, ObjectGuid::LowType playerGuid1, ObjectGuid::LowType playerGuid2, uint8 newRank) : - LogEntry(guildId, guid, timestamp), m_eventType(eventType), m_playerGuid1(playerGuid1), m_playerGuid2(playerGuid2), m_newRank(newRank) { } + // Bank event log entry + class BankEventLogEntry : public LogEntry + { + public: + static bool IsMoneyEvent(GuildBankEventLogTypes eventType) + { + return + eventType == GUILD_BANK_LOG_DEPOSIT_MONEY || + eventType == GUILD_BANK_LOG_WITHDRAW_MONEY || + eventType == GUILD_BANK_LOG_REPAIR_MONEY; + } + + BankEventLogEntry(ObjectGuid::LowType guildId, uint32 guid, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid::LowType playerGuid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId) : + LogEntry(guildId, guid), m_eventType(eventType), m_bankTabId(tabId), m_playerGuid(playerGuid), + m_itemOrMoney(itemOrMoney), m_itemStackCount(itemStackCount), m_destTabId(destTabId) { } + + BankEventLogEntry(ObjectGuid::LowType guildId, uint32 guid, time_t timestamp, uint8 tabId, GuildBankEventLogTypes eventType, ObjectGuid::LowType playerGuid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId) : + LogEntry(guildId, guid, timestamp), m_eventType(eventType), m_bankTabId(tabId), m_playerGuid(playerGuid), + m_itemOrMoney(itemOrMoney), m_itemStackCount(itemStackCount), m_destTabId(destTabId) { } + + ~BankEventLogEntry() { } + + void SaveToDB(SQLTransaction& trans) const override; + void WritePacket(WorldPacket& data) const override; + + private: + GuildBankEventLogTypes m_eventType; + uint8 m_bankTabId; + ObjectGuid::LowType m_playerGuid; + uint32 m_itemOrMoney; + uint16 m_itemStackCount; + uint8 m_destTabId; + }; + + // Class encapsulating work with events collection + typedef std::list<LogEntry*> GuildLog; + + class LogHolder + { + public: + LogHolder(uint32 maxRecords) : m_maxRecords(maxRecords), m_nextGUID(uint32(GUILD_EVENT_LOG_GUID_UNDEFINED)) { } + ~LogHolder(); + + uint8 GetSize() const { return uint8(m_log.size()); } + // Checks if new log entry can be added to holder when loading from DB + inline bool CanInsert() const { return m_log.size() < m_maxRecords; } + // Adds event from DB to collection + void LoadEvent(LogEntry* entry); + // Adds new event to collection and saves it to DB + void AddEvent(SQLTransaction& trans, LogEntry* entry); + // Writes information about all events to packet + void WritePacket(WorldPacket& data) const; + uint32 GetNextGUID(); + + private: + GuildLog m_log; + uint32 m_maxRecords; + uint32 m_nextGUID; + }; + + // Class encapsulating guild rank data + class RankInfo + { + public: + RankInfo(): m_guildId(0), m_rankId(GUILD_RANK_NONE), m_rights(GR_RIGHT_EMPTY), m_bankMoneyPerDay(0) { } + RankInfo(ObjectGuid::LowType guildId) : m_guildId(guildId), m_rankId(GUILD_RANK_NONE), m_rights(GR_RIGHT_EMPTY), m_bankMoneyPerDay(0) { } + RankInfo(ObjectGuid::LowType guildId, uint8 rankId, std::string const& name, uint32 rights, uint32 money) : + m_guildId(guildId), m_rankId(rankId), m_name(name), m_rights(rights), + m_bankMoneyPerDay(rankId != GR_GUILDMASTER ? money : GUILD_WITHDRAW_MONEY_UNLIMITED) { } - ~EventLogEntry() { } + void LoadFromDB(Field* fields); + void SaveToDB(SQLTransaction& trans) const; + void WritePacket(WorldPacket& data) const; - void SaveToDB(SQLTransaction& trans) const override; - void WritePacket(WorldPacket& data) const override; + uint8 GetId() const { return m_rankId; } - private: - GuildEventLogTypes m_eventType; - ObjectGuid::LowType m_playerGuid1; - ObjectGuid::LowType m_playerGuid2; - uint8 m_newRank; - }; - - // Bank event log entry - class BankEventLogEntry : public LogEntry - { - public: - static bool IsMoneyEvent(GuildBankEventLogTypes eventType) - { - return - eventType == GUILD_BANK_LOG_DEPOSIT_MONEY || - eventType == GUILD_BANK_LOG_WITHDRAW_MONEY || - eventType == GUILD_BANK_LOG_REPAIR_MONEY; - } + std::string const& GetName() const { return m_name; } + void SetName(std::string const& name); - BankEventLogEntry(ObjectGuid::LowType guildId, uint32 guid, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid::LowType playerGuid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId) : - LogEntry(guildId, guid), m_eventType(eventType), m_bankTabId(tabId), m_playerGuid(playerGuid), - m_itemOrMoney(itemOrMoney), m_itemStackCount(itemStackCount), m_destTabId(destTabId) { } + uint32 GetRights() const { return m_rights; } + void SetRights(uint32 rights); - BankEventLogEntry(ObjectGuid::LowType guildId, uint32 guid, time_t timestamp, uint8 tabId, GuildBankEventLogTypes eventType, ObjectGuid::LowType playerGuid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId) : - LogEntry(guildId, guid, timestamp), m_eventType(eventType), m_bankTabId(tabId), m_playerGuid(playerGuid), - m_itemOrMoney(itemOrMoney), m_itemStackCount(itemStackCount), m_destTabId(destTabId) { } + int32 GetBankMoneyPerDay() const { return m_bankMoneyPerDay; } - ~BankEventLogEntry() { } + void SetBankMoneyPerDay(uint32 money); - void SaveToDB(SQLTransaction& trans) const override; - void WritePacket(WorldPacket& data) const override; + inline int8 GetBankTabRights(uint8 tabId) const + { + return tabId < GUILD_BANK_MAX_TABS ? m_bankTabRightsAndSlots[tabId].GetRights() : 0; + } - private: - GuildBankEventLogTypes m_eventType; - uint8 m_bankTabId; - ObjectGuid::LowType m_playerGuid; - uint32 m_itemOrMoney; - uint16 m_itemStackCount; - uint8 m_destTabId; - }; - - // Class encapsulating work with events collection - typedef std::list<LogEntry*> GuildLog; - - class LogHolder - { - public: - LogHolder(uint32 maxRecords) : m_maxRecords(maxRecords), m_nextGUID(uint32(GUILD_EVENT_LOG_GUID_UNDEFINED)) { } - ~LogHolder(); - - uint8 GetSize() const { return uint8(m_log.size()); } - // Checks if new log entry can be added to holder when loading from DB - inline bool CanInsert() const { return m_log.size() < m_maxRecords; } - // Adds event from DB to collection - void LoadEvent(LogEntry* entry); - // Adds new event to collection and saves it to DB - void AddEvent(SQLTransaction& trans, LogEntry* entry); - // Writes information about all events to packet - void WritePacket(WorldPacket& data) const; - uint32 GetNextGUID(); + inline int32 GetBankTabSlotsPerDay(uint8 tabId) const + { + return tabId < GUILD_BANK_MAX_TABS ? m_bankTabRightsAndSlots[tabId].GetSlots() : 0; + } - private: - GuildLog m_log; - uint32 m_maxRecords; - uint32 m_nextGUID; - }; - - // Class encapsulating guild rank data - class RankInfo - { - public: - RankInfo(): m_guildId(0), m_rankId(GUILD_RANK_NONE), m_rights(GR_RIGHT_EMPTY), m_bankMoneyPerDay(0) { } - RankInfo(ObjectGuid::LowType guildId) : m_guildId(guildId), m_rankId(GUILD_RANK_NONE), m_rights(GR_RIGHT_EMPTY), m_bankMoneyPerDay(0) { } - RankInfo(ObjectGuid::LowType guildId, uint8 rankId, std::string const& name, uint32 rights, uint32 money) : - m_guildId(guildId), m_rankId(rankId), m_name(name), m_rights(rights), - m_bankMoneyPerDay(rankId != GR_GUILDMASTER ? money : GUILD_WITHDRAW_MONEY_UNLIMITED) { } + void SetBankTabSlotsAndRights(GuildBankRightsAndSlots rightsAndSlots, bool saveToDB); + void CreateMissingTabsIfNeeded(uint8 ranks, SQLTransaction& trans, bool logOnCreate = false); - void LoadFromDB(Field* fields); - void SaveToDB(SQLTransaction& trans) const; - void WritePacket(WorldPacket& data) const; + private: + ObjectGuid::LowType m_guildId; - uint8 GetId() const { return m_rankId; } + uint8 m_rankId; + std::string m_name; + uint32 m_rights; + uint32 m_bankMoneyPerDay; + GuildBankRightsAndSlots m_bankTabRightsAndSlots[GUILD_BANK_MAX_TABS]; + }; - std::string const& GetName() const { return m_name; } - void SetName(std::string const& name); + class BankTab + { + public: + BankTab(ObjectGuid::LowType guildId, uint8 tabId) : m_guildId(guildId), m_tabId(tabId) + { + memset(m_items, 0, GUILD_BANK_MAX_SLOTS * sizeof(Item*)); + } + + void LoadFromDB(Field* fields); + bool LoadItemFromDB(Field* fields); + void Delete(SQLTransaction& trans, bool removeItemsFromDB = false); + + void WritePacket(WorldPacket& data) const; + bool WriteSlotPacket(WorldPacket& data, uint8 slotId, bool ignoreEmpty = true) const; + void WriteInfoPacket(WorldPacket& data) const + { + data << m_name; + data << m_icon; + } + + void SetInfo(std::string const& name, std::string const& icon); + void SetText(std::string const& text); + void SendText(const Guild* guild, WorldSession* session) const; + + inline Item* GetItem(uint8 slotId) const { return slotId < GUILD_BANK_MAX_SLOTS ? m_items[slotId] : NULL; } + bool SetItem(SQLTransaction& trans, uint8 slotId, Item* pItem); + + private: + ObjectGuid::LowType m_guildId; + uint8 m_tabId; + + Item* m_items[GUILD_BANK_MAX_SLOTS]; + std::string m_name; + std::string m_icon; + std::string m_text; + }; + + // Movement data + class MoveItemData + { + public: + MoveItemData(Guild* guild, Player* player, uint8 container, uint8 slotId) : m_pGuild(guild), m_pPlayer(player), + m_container(container), m_slotId(slotId), m_pItem(NULL), m_pClonedItem(NULL) { } + virtual ~MoveItemData() { } + + virtual bool IsBank() const = 0; + // Initializes item pointer. Returns true, if item exists, false otherwise. + virtual bool InitItem() = 0; + // Checks splited amount against item. Splited amount cannot be more that number of items in stack. + virtual bool CheckItem(uint32& splitedAmount); + // Defines if player has rights to save item in container + virtual bool HasStoreRights(MoveItemData* /*pOther*/) const { return true; } + // Defines if player has rights to withdraw item from container + virtual bool HasWithdrawRights(MoveItemData* /*pOther*/) const { return true; } + // Checks if container can store specified item + bool CanStore(Item* pItem, bool swap, bool sendError); + // Clones stored item + bool CloneItem(uint32 count); + // Remove item from container (if splited update items fields) + virtual void RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount = 0) = 0; + // Saves item to container + virtual Item* StoreItem(SQLTransaction& trans, Item* pItem) = 0; + // Log bank event + virtual void LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const = 0; + // Log GM action + virtual void LogAction(MoveItemData* pFrom) const; + // Copy slots id from position vector + void CopySlots(SlotIds& ids) const; + + Item* GetItem(bool isCloned = false) const { return isCloned ? m_pClonedItem : m_pItem; } + uint8 GetContainer() const { return m_container; } + uint8 GetSlotId() const { return m_slotId; } + + protected: + virtual InventoryResult CanStore(Item* pItem, bool swap) = 0; + + Guild* m_pGuild; + Player* m_pPlayer; + uint8 m_container; + uint8 m_slotId; + Item* m_pItem; + Item* m_pClonedItem; + ItemPosCountVec m_vec; + }; + + class PlayerMoveItemData : public MoveItemData + { + public: + PlayerMoveItemData(Guild* guild, Player* player, uint8 container, uint8 slotId) : + MoveItemData(guild, player, container, slotId) { } + + bool IsBank() const override { return false; } + bool InitItem() override; + void RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount = 0) override; + Item* StoreItem(SQLTransaction& trans, Item* pItem) override; + void LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const override; + protected: + InventoryResult CanStore(Item* pItem, bool swap) override; + }; + + class BankMoveItemData : public MoveItemData + { + public: + BankMoveItemData(Guild* guild, Player* player, uint8 container, uint8 slotId) : + MoveItemData(guild, player, container, slotId) { } + + bool IsBank() const override { return true; } + bool InitItem() override; + bool HasStoreRights(MoveItemData* pOther) const override; + bool HasWithdrawRights(MoveItemData* pOther) const override; + void RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount) override; + Item* StoreItem(SQLTransaction& trans, Item* pItem) override; + void LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const override; + void LogAction(MoveItemData* pFrom) const override; + + protected: + InventoryResult CanStore(Item* pItem, bool swap) override; + + private: + Item* _StoreItem(SQLTransaction& trans, BankTab* pTab, Item* pItem, ItemPosCount& pos, bool clone) const; + bool _ReserveSpace(uint8 slotId, Item* pItem, Item* pItemDest, uint32& count); + void CanStoreItemInTab(Item* pItem, uint8 skipSlotId, bool merge, uint32& count); + }; + + typedef std::unordered_map<uint32, Member*> Members; + typedef std::vector<RankInfo> Ranks; + typedef std::vector<BankTab*> BankTabs; - uint32 GetRights() const { return m_rights; } - void SetRights(uint32 rights); + public: + static void SendCommandResult(WorldSession* session, GuildCommandType type, GuildCommandError errCode, std::string const& param = ""); + static void SendSaveEmblemResult(WorldSession* session, GuildEmblemError errCode); - int32 GetBankMoneyPerDay() const { return m_bankMoneyPerDay; } + Guild(); + ~Guild(); - void SetBankMoneyPerDay(uint32 money); + bool Create(Player* pLeader, std::string const& name); + void Disband(); - inline int8 GetBankTabRights(uint8 tabId) const + // Getters + ObjectGuid::LowType GetId() const { return m_id; } + ObjectGuid GetLeaderGUID() const { return m_leaderGuid; } + std::string const& GetName() const { return m_name; } + std::string const& GetMOTD() const { return m_motd; } + std::string const& GetInfo() const { return m_info; } + uint32 GetMemberCount() const { return m_members.size(); } + time_t GetCreatedDate() const { return m_createdDate; } + uint64 GetBankMoney() const { return m_bankMoney; } + + bool SetName(std::string const& name); + + // Handle client commands + void HandleRoster(WorldSession* session); + void HandleQuery(WorldSession* session); + void HandleSetMOTD(WorldSession* session, std::string const& motd); + void HandleSetInfo(WorldSession* session, std::string const& info); + void HandleSetEmblem(WorldSession* session, const EmblemInfo& emblemInfo); + void HandleSetLeader(WorldSession* session, std::string const& name); + void HandleSetBankTabInfo(WorldSession* session, uint8 tabId, std::string const& name, std::string const& icon); + void HandleSetMemberNote(WorldSession* session, std::string const& name, std::string const& note, bool officer); + void HandleSetRankInfo(WorldSession* session, uint8 rankId, std::string const& name, uint32 rights, uint32 moneyPerDay, const GuildBankRightsAndSlotsVec& rightsAndSlots); + void HandleBuyBankTab(WorldSession* session, uint8 tabId); + void HandleInviteMember(WorldSession* session, std::string const& name); + void HandleAcceptMember(WorldSession* session); + void HandleLeaveMember(WorldSession* session); + void HandleRemoveMember(WorldSession* session, std::string const& name); + void HandleUpdateMemberRank(WorldSession* session, std::string const& name, bool demote); + void HandleAddNewRank(WorldSession* session, std::string const& name); + void HandleRemoveRank(WorldSession* session, uint8 rankId); + void HandleRemoveLowestRank(WorldSession* session); + void HandleMemberDepositMoney(WorldSession* session, uint32 amount); + bool HandleMemberWithdrawMoney(WorldSession* session, uint32 amount, bool repair = false); + void HandleMemberLogout(WorldSession* session); + void HandleDisband(WorldSession* session); + + void UpdateMemberData(Player* player, uint8 dataid, uint32 value); + void OnPlayerStatusChange(Player* player, uint32 flag, bool state); + + // Send info to client + void SendInfo(WorldSession* session) const; + void SendEventLog(WorldSession* session) const; + void SendBankLog(WorldSession* session, uint8 tabId) const; + void SendBankTabsInfo(WorldSession* session, bool showTabs = false) const; + void SendBankTabData(WorldSession* session, uint8 tabId) const; + void SendBankTabText(WorldSession* session, uint8 tabId) const; + void SendPermissions(WorldSession* session) const; + void SendMoneyInfo(WorldSession* session) const; + void SendLoginInfo(WorldSession* session); + + // Load from DB + bool LoadFromDB(Field* fields); + void LoadRankFromDB(Field* fields); + bool LoadMemberFromDB(Field* fields); + bool LoadEventLogFromDB(Field* fields); + void LoadBankRightFromDB(Field* fields); + void LoadBankTabFromDB(Field* fields); + bool LoadBankEventLogFromDB(Field* fields); + bool LoadBankItemFromDB(Field* fields); + bool Validate(); + + // Broadcasts + void BroadcastToGuild(WorldSession* session, bool officerOnly, std::string const& msg, uint32 language = LANG_UNIVERSAL) const; + void BroadcastPacketToRank(WorldPacket* packet, uint8 rankId) const; + void BroadcastPacket(WorldPacket* packet) const; + + void MassInviteToEvent(WorldSession* session, uint32 minLevel, uint32 maxLevel, uint32 minRank); + + template<class Do> + void BroadcastWorker(Do& _do, Player* except = nullptr) { - return tabId < GUILD_BANK_MAX_TABS ? m_bankTabRightsAndSlots[tabId].GetRights() : 0; + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) + if (Player* player = itr->second->FindConnectedPlayer()) + if (player != except) + _do(player); } - inline int32 GetBankTabSlotsPerDay(uint8 tabId) const - { - return tabId < GUILD_BANK_MAX_TABS ? m_bankTabRightsAndSlots[tabId].GetSlots() : 0; - } + // Members + // Adds member to guild. If rankId == GUILD_RANK_NONE, lowest rank is assigned. + bool AddMember(SQLTransaction& trans, ObjectGuid guid, uint8 rankId = GUILD_RANK_NONE); + void DeleteMember(SQLTransaction& trans, ObjectGuid guid, bool isDisbanding = false, bool isKicked = false, bool canDeleteGuild = false); + bool ChangeMemberRank(SQLTransaction& trans, ObjectGuid guid, uint8 newRank); - void SetBankTabSlotsAndRights(GuildBankRightsAndSlots rightsAndSlots, bool saveToDB); - void CreateMissingTabsIfNeeded(uint8 ranks, SQLTransaction& trans, bool logOnCreate = false); + // Bank + void SwapItems(Player* player, uint8 tabId, uint8 slotId, uint8 destTabId, uint8 destSlotId, uint32 splitedAmount); + void SwapItemsWithInventory(Player* player, bool toChar, uint8 tabId, uint8 slotId, uint8 playerBag, uint8 playerSlotId, uint32 splitedAmount); - private: - ObjectGuid::LowType m_guildId; + // Bank tabs + void SetBankTabText(uint8 tabId, std::string const& text); - uint8 m_rankId; + void ResetTimes(); + + protected: + ObjectGuid::LowType m_id; std::string m_name; - uint32 m_rights; - uint32 m_bankMoneyPerDay; - GuildBankRightsAndSlots m_bankTabRightsAndSlots[GUILD_BANK_MAX_TABS]; - }; + ObjectGuid m_leaderGuid; + std::string m_motd; + std::string m_info; + time_t m_createdDate; - class BankTab - { - public: - BankTab(ObjectGuid::LowType guildId, uint8 tabId) : m_guildId(guildId), m_tabId(tabId) - { - memset(m_items, 0, GUILD_BANK_MAX_SLOTS * sizeof(Item*)); - } + EmblemInfo m_emblemInfo; + uint32 m_accountsNumber; + uint64 m_bankMoney; - void LoadFromDB(Field* fields); - bool LoadItemFromDB(Field* fields); - void Delete(SQLTransaction& trans, bool removeItemsFromDB = false); + Ranks m_ranks; + Members m_members; + BankTabs m_bankTabs; - void WritePacket(WorldPacket& data) const; - bool WriteSlotPacket(WorldPacket& data, uint8 slotId, bool ignoreEmpty = true) const; - void WriteInfoPacket(WorldPacket& data) const + // These are actually ordered lists. The first element is the oldest entry. + LogHolder* m_eventLog; + LogHolder* m_bankEventLog[GUILD_BANK_MAX_TABS + 1]; + + private: + inline uint8 _GetRanksSize() const { return uint8(m_ranks.size()); } + inline RankInfo const* GetRankInfo(uint8 rankId) const { return rankId < _GetRanksSize() ? &m_ranks[rankId] : nullptr; } + inline RankInfo* GetRankInfo(uint8 rankId) { return rankId < _GetRanksSize() ? &m_ranks[rankId] : nullptr; } + inline bool _HasRankRight(Player* player, uint32 right) const { - data << m_name; - data << m_icon; + if (player) + if (Member const* member = GetMember(player->GetGUID())) + return (_GetRankRights(member->GetRankId()) & right) != GR_RIGHT_EMPTY; + return false; } - void SetInfo(std::string const& name, std::string const& icon); - void SetText(std::string const& text); - void SendText(const Guild* guild, WorldSession* session) const; + inline uint8 _GetLowestRankId() const { return uint8(m_ranks.size() - 1); } - inline Item* GetItem(uint8 slotId) const { return slotId < GUILD_BANK_MAX_SLOTS ? m_items[slotId] : NULL; } - bool SetItem(SQLTransaction& trans, uint8 slotId, Item* pItem); + inline uint8 _GetPurchasedTabsSize() const { return uint8(m_bankTabs.size()); } + inline BankTab* GetBankTab(uint8 tabId) { return tabId < m_bankTabs.size() ? m_bankTabs[tabId] : nullptr; } + inline BankTab const* GetBankTab(uint8 tabId) const { return tabId < m_bankTabs.size() ? m_bankTabs[tabId] : nullptr; } - private: - ObjectGuid::LowType m_guildId; - uint8 m_tabId; - - Item* m_items[GUILD_BANK_MAX_SLOTS]; - std::string m_name; - std::string m_icon; - std::string m_text; - }; + inline Member const* GetMember(ObjectGuid guid) const + { + auto itr = m_members.find(guid.GetCounter()); + return itr != m_members.end() ? itr->second : nullptr; + } - // Movement data - class MoveItemData - { - public: - MoveItemData(Guild* guild, Player* player, uint8 container, uint8 slotId) : m_pGuild(guild), m_pPlayer(player), - m_container(container), m_slotId(slotId), m_pItem(NULL), m_pClonedItem(NULL) { } - virtual ~MoveItemData() { } - - virtual bool IsBank() const = 0; - // Initializes item pointer. Returns true, if item exists, false otherwise. - virtual bool InitItem() = 0; - // Checks splited amount against item. Splited amount cannot be more that number of items in stack. - virtual bool CheckItem(uint32& splitedAmount); - // Defines if player has rights to save item in container - virtual bool HasStoreRights(MoveItemData* /*pOther*/) const { return true; } - // Defines if player has rights to withdraw item from container - virtual bool HasWithdrawRights(MoveItemData* /*pOther*/) const { return true; } - // Checks if container can store specified item - bool CanStore(Item* pItem, bool swap, bool sendError); - // Clones stored item - bool CloneItem(uint32 count); - // Remove item from container (if splited update items fields) - virtual void RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount = 0) = 0; - // Saves item to container - virtual Item* StoreItem(SQLTransaction& trans, Item* pItem) = 0; - // Log bank event - virtual void LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const = 0; - // Log GM action - virtual void LogAction(MoveItemData* pFrom) const; - // Copy slots id from position vector - void CopySlots(SlotIds& ids) const; - - Item* GetItem(bool isCloned = false) const { return isCloned ? m_pClonedItem : m_pItem; } - uint8 GetContainer() const { return m_container; } - uint8 GetSlotId() const { return m_slotId; } + inline Member* GetMember(ObjectGuid guid) + { + auto itr = m_members.find(guid.GetCounter()); + return itr != m_members.end() ? itr->second : nullptr; + } - protected: - virtual InventoryResult CanStore(Item* pItem, bool swap) = 0; - - Guild* m_pGuild; - Player* m_pPlayer; - uint8 m_container; - uint8 m_slotId; - Item* m_pItem; - Item* m_pClonedItem; - ItemPosCountVec m_vec; - }; - - class PlayerMoveItemData : public MoveItemData - { - public: - PlayerMoveItemData(Guild* guild, Player* player, uint8 container, uint8 slotId) : - MoveItemData(guild, player, container, slotId) { } - - bool IsBank() const override { return false; } - bool InitItem() override; - void RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount = 0) override; - Item* StoreItem(SQLTransaction& trans, Item* pItem) override; - void LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const override; - protected: - InventoryResult CanStore(Item* pItem, bool swap) override; - }; + inline Member* GetMember(std::string const& name) + { + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) + if (itr->second->GetName() == name) + return itr->second; - class BankMoveItemData : public MoveItemData - { - public: - BankMoveItemData(Guild* guild, Player* player, uint8 container, uint8 slotId) : - MoveItemData(guild, player, container, slotId) { } - - bool IsBank() const override { return true; } - bool InitItem() override; - bool HasStoreRights(MoveItemData* pOther) const override; - bool HasWithdrawRights(MoveItemData* pOther) const override; - void RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount) override; - Item* StoreItem(SQLTransaction& trans, Item* pItem) override; - void LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const override; - void LogAction(MoveItemData* pFrom) const override; + return nullptr; + } - protected: - InventoryResult CanStore(Item* pItem, bool swap) override; + inline void _DeleteMemberFromDB(SQLTransaction& trans, ObjectGuid::LowType lowguid) const + { + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_MEMBER); + stmt->setUInt32(0, lowguid); + CharacterDatabase.ExecuteOrAppend(trans, stmt); + } - private: - Item* _StoreItem(SQLTransaction& trans, BankTab* pTab, Item* pItem, ItemPosCount& pos, bool clone) const; - bool _ReserveSpace(uint8 slotId, Item* pItem, Item* pItemDest, uint32& count); - void CanStoreItemInTab(Item* pItem, uint8 skipSlotId, bool merge, uint32& count); - }; - - typedef std::unordered_map<uint32, Member*> Members; - typedef std::vector<RankInfo> Ranks; - typedef std::vector<BankTab*> BankTabs; - -public: - static void SendCommandResult(WorldSession* session, GuildCommandType type, GuildCommandError errCode, std::string const& param = ""); - static void SendSaveEmblemResult(WorldSession* session, GuildEmblemError errCode); - - Guild(); - ~Guild(); - - bool Create(Player* pLeader, std::string const& name); - void Disband(); - - // Getters - ObjectGuid::LowType GetId() const { return m_id; } - ObjectGuid GetLeaderGUID() const { return m_leaderGuid; } - std::string const& GetName() const { return m_name; } - std::string const& GetMOTD() const { return m_motd; } - std::string const& GetInfo() const { return m_info; } - uint32 GetMemberCount() const { return m_members.size(); } - time_t GetCreatedDate() const { return m_createdDate; } - uint64 GetBankMoney() const { return m_bankMoney; } - - bool SetName(std::string const& name); - - // Handle client commands - void HandleRoster(WorldSession* session); - void HandleQuery(WorldSession* session); - void HandleSetMOTD(WorldSession* session, std::string const& motd); - void HandleSetInfo(WorldSession* session, std::string const& info); - void HandleSetEmblem(WorldSession* session, const EmblemInfo& emblemInfo); - void HandleSetLeader(WorldSession* session, std::string const& name); - void HandleSetBankTabInfo(WorldSession* session, uint8 tabId, std::string const& name, std::string const& icon); - void HandleSetMemberNote(WorldSession* session, std::string const& name, std::string const& note, bool officer); - void HandleSetRankInfo(WorldSession* session, uint8 rankId, std::string const& name, uint32 rights, uint32 moneyPerDay, const GuildBankRightsAndSlotsVec& rightsAndSlots); - void HandleBuyBankTab(WorldSession* session, uint8 tabId); - void HandleInviteMember(WorldSession* session, std::string const& name); - void HandleAcceptMember(WorldSession* session); - void HandleLeaveMember(WorldSession* session); - void HandleRemoveMember(WorldSession* session, std::string const& name); - void HandleUpdateMemberRank(WorldSession* session, std::string const& name, bool demote); - void HandleAddNewRank(WorldSession* session, std::string const& name); - void HandleRemoveRank(WorldSession* session, uint8 rankId); - void HandleRemoveLowestRank(WorldSession* session); - void HandleMemberDepositMoney(WorldSession* session, uint32 amount); - bool HandleMemberWithdrawMoney(WorldSession* session, uint32 amount, bool repair = false); - void HandleMemberLogout(WorldSession* session); - void HandleDisband(WorldSession* session); - - void UpdateMemberData(Player* player, uint8 dataid, uint32 value); - void OnPlayerStatusChange(Player* player, uint32 flag, bool state); - - // Send info to client - void SendInfo(WorldSession* session) const; - void SendEventLog(WorldSession* session) const; - void SendBankLog(WorldSession* session, uint8 tabId) const; - void SendBankTabsInfo(WorldSession* session, bool showTabs = false) const; - void SendBankTabData(WorldSession* session, uint8 tabId) const; - void SendBankTabText(WorldSession* session, uint8 tabId) const; - void SendPermissions(WorldSession* session) const; - void SendMoneyInfo(WorldSession* session) const; - void SendLoginInfo(WorldSession* session); - - // Load from DB - bool LoadFromDB(Field* fields); - void LoadRankFromDB(Field* fields); - bool LoadMemberFromDB(Field* fields); - bool LoadEventLogFromDB(Field* fields); - void LoadBankRightFromDB(Field* fields); - void LoadBankTabFromDB(Field* fields); - bool LoadBankEventLogFromDB(Field* fields); - bool LoadBankItemFromDB(Field* fields); - bool Validate(); - - // Broadcasts - void BroadcastToGuild(WorldSession* session, bool officerOnly, std::string const& msg, uint32 language = LANG_UNIVERSAL) const; - void BroadcastPacketToRank(WorldPacket* packet, uint8 rankId) const; - void BroadcastPacket(WorldPacket* packet) const; - - void MassInviteToEvent(WorldSession* session, uint32 minLevel, uint32 maxLevel, uint32 minRank); - - template<class Do> - void BroadcastWorker(Do& _do, Player* except = NULL) - { - for (Members::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - if (Player* player = itr->second->FindConnectedPlayer()) - if (player != except) - _do(player); - } - - // Members - // Adds member to guild. If rankId == GUILD_RANK_NONE, lowest rank is assigned. - bool AddMember(ObjectGuid guid, uint8 rankId = GUILD_RANK_NONE); - void DeleteMember(ObjectGuid guid, bool isDisbanding = false, bool isKicked = false, bool canDeleteGuild = false); - bool ChangeMemberRank(ObjectGuid guid, uint8 newRank); - - // Bank - void SwapItems(Player* player, uint8 tabId, uint8 slotId, uint8 destTabId, uint8 destSlotId, uint32 splitedAmount); - void SwapItemsWithInventory(Player* player, bool toChar, uint8 tabId, uint8 slotId, uint8 playerBag, uint8 playerSlotId, uint32 splitedAmount); - - // Bank tabs - void SetBankTabText(uint8 tabId, std::string const& text); - - void ResetTimes(); - -protected: - ObjectGuid::LowType m_id; - std::string m_name; - ObjectGuid m_leaderGuid; - std::string m_motd; - std::string m_info; - time_t m_createdDate; - - EmblemInfo m_emblemInfo; - uint32 m_accountsNumber; - uint64 m_bankMoney; - - Ranks m_ranks; - Members m_members; - BankTabs m_bankTabs; - - // These are actually ordered lists. The first element is the oldest entry. - LogHolder* m_eventLog; - LogHolder* m_bankEventLog[GUILD_BANK_MAX_TABS + 1]; - -private: - inline uint8 _GetRanksSize() const { return uint8(m_ranks.size()); } - inline const RankInfo* GetRankInfo(uint8 rankId) const { return rankId < _GetRanksSize() ? &m_ranks[rankId] : NULL; } - inline RankInfo* GetRankInfo(uint8 rankId) { return rankId < _GetRanksSize() ? &m_ranks[rankId] : NULL; } - inline bool _HasRankRight(Player* player, uint32 right) const - { - if (player) - if (Member const* member = GetMember(player->GetGUID())) - return (_GetRankRights(member->GetRankId()) & right) != GR_RIGHT_EMPTY; - return false; - } - - inline uint8 _GetLowestRankId() const { return uint8(m_ranks.size() - 1); } - - inline uint8 _GetPurchasedTabsSize() const { return uint8(m_bankTabs.size()); } - inline BankTab* GetBankTab(uint8 tabId) { return tabId < m_bankTabs.size() ? m_bankTabs[tabId] : NULL; } - inline const BankTab* GetBankTab(uint8 tabId) const { return tabId < m_bankTabs.size() ? m_bankTabs[tabId] : NULL; } - - inline const Member* GetMember(ObjectGuid guid) const - { - Members::const_iterator itr = m_members.find(guid.GetCounter()); - return itr != m_members.end() ? itr->second : NULL; - } - - inline Member* GetMember(ObjectGuid guid) - { - Members::iterator itr = m_members.find(guid.GetCounter()); - return itr != m_members.end() ? itr->second : NULL; - } - - inline Member* GetMember(std::string const& name) - { - for (Members::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - if (itr->second->GetName() == name) - return itr->second; - - return NULL; - } - - inline void _DeleteMemberFromDB(ObjectGuid::LowType lowguid) const - { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_MEMBER); - stmt->setUInt32(0, lowguid); - CharacterDatabase.Execute(stmt); - } - - // Creates log holders (either when loading or when creating guild) - void _CreateLogHolders(); - // Tries to create new bank tab - void _CreateNewBankTab(); - // Creates default guild ranks with names in given locale - void _CreateDefaultGuildRanks(LocaleConstant loc); - // Creates new rank - bool _CreateRank(std::string const& name, uint32 rights); - // Update account number when member added/removed from guild - void _UpdateAccountsNumber(); - bool _IsLeader(Player* player) const; - void _DeleteBankItems(SQLTransaction& trans, bool removeItemsFromDB = false); - bool _ModifyBankMoney(SQLTransaction& trans, uint64 amount, bool add); - void _SetLeaderGUID(Member* pLeader); - - void _SetRankBankMoneyPerDay(uint8 rankId, uint32 moneyPerDay); - void _SetRankBankTabRightsAndSlots(uint8 rankId, GuildBankRightsAndSlots rightsAndSlots, bool saveToDB = true); - int8 _GetRankBankTabRights(uint8 rankId, uint8 tabId) const; - uint32 _GetRankRights(uint8 rankId) const; - int32 _GetRankBankMoneyPerDay(uint8 rankId) const; - int32 _GetRankBankTabSlotsPerDay(uint8 rankId, uint8 tabId) const; - std::string _GetRankName(uint8 rankId) const; - - int32 _GetMemberRemainingSlots(Member const* member, uint8 tabId) const; - int32 _GetMemberRemainingMoney(Member const* member) const; - void _UpdateMemberWithdrawSlots(SQLTransaction& trans, ObjectGuid guid, uint8 tabId); - bool _MemberHasTabRights(ObjectGuid guid, uint8 tabId, uint32 rights) const; - - void _LogEvent(GuildEventLogTypes eventType, ObjectGuid::LowType playerGuid1, ObjectGuid::LowType playerGuid2 = 0, uint8 newRank = 0); - void _LogBankEvent(SQLTransaction& trans, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid::LowType playerGuid, uint32 itemOrMoney, uint16 itemStackCount = 0, uint8 destTabId = 0); - - Item* _GetItem(uint8 tabId, uint8 slotId) const; - void _RemoveItem(SQLTransaction& trans, uint8 tabId, uint8 slotId); - void _MoveItems(MoveItemData* pSrc, MoveItemData* pDest, uint32 splitedAmount); - bool _DoItemsMove(MoveItemData* pSrc, MoveItemData* pDest, bool sendError, uint32 splitedAmount = 0); - - void _SendBankContent(WorldSession* session, uint8 tabId) const; - void _SendBankMoneyUpdate(WorldSession* session) const; - void _SendBankContentUpdate(MoveItemData* pSrc, MoveItemData* pDest) const; - void _SendBankContentUpdate(uint8 tabId, SlotIds slots) const; - void _SendBankList(WorldSession* session = NULL, uint8 tabId = 0, bool sendFullSlots = false, SlotIds *slots = NULL) const; - - void _BroadcastEvent(GuildEvents guildEvent, ObjectGuid guid, const char* param1 = NULL, const char* param2 = NULL, const char* param3 = NULL) const; + // Creates log holders (either when loading or when creating guild) + void _CreateLogHolders(); + // Tries to create new bank tab + void _CreateNewBankTab(); + // Creates default guild ranks with names in given locale + void _CreateDefaultGuildRanks(SQLTransaction& trans, LocaleConstant loc); + // Creates new rank + bool _CreateRank(SQLTransaction& trans, std::string const& name, uint32 rights); + // Update account number when member added/removed from guild + void _UpdateAccountsNumber(); + bool _IsLeader(Player* player) const; + void _DeleteBankItems(SQLTransaction& trans, bool removeItemsFromDB = false); + bool _ModifyBankMoney(SQLTransaction& trans, uint64 amount, bool add); + void _SetLeaderGUID(Member* pLeader); + + void _SetRankBankMoneyPerDay(uint8 rankId, uint32 moneyPerDay); + void _SetRankBankTabRightsAndSlots(uint8 rankId, GuildBankRightsAndSlots rightsAndSlots, bool saveToDB = true); + int8 _GetRankBankTabRights(uint8 rankId, uint8 tabId) const; + uint32 _GetRankRights(uint8 rankId) const; + int32 _GetRankBankMoneyPerDay(uint8 rankId) const; + int32 _GetRankBankTabSlotsPerDay(uint8 rankId, uint8 tabId) const; + std::string _GetRankName(uint8 rankId) const; + + int32 _GetMemberRemainingSlots(Member const* member, uint8 tabId) const; + int32 _GetMemberRemainingMoney(Member const* member) const; + void _UpdateMemberWithdrawSlots(SQLTransaction& trans, ObjectGuid guid, uint8 tabId); + bool _MemberHasTabRights(ObjectGuid guid, uint8 tabId, uint32 rights) const; + + void _LogEvent(GuildEventLogTypes eventType, ObjectGuid::LowType playerGuid1, ObjectGuid::LowType playerGuid2 = 0, uint8 newRank = 0); + void _LogBankEvent(SQLTransaction& trans, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid::LowType playerGuid, uint32 itemOrMoney, uint16 itemStackCount = 0, uint8 destTabId = 0); + + Item* _GetItem(uint8 tabId, uint8 slotId) const; + void _RemoveItem(SQLTransaction& trans, uint8 tabId, uint8 slotId); + void _MoveItems(MoveItemData* pSrc, MoveItemData* pDest, uint32 splitedAmount); + bool _DoItemsMove(MoveItemData* pSrc, MoveItemData* pDest, bool sendError, uint32 splitedAmount = 0); + + void _SendBankContent(WorldSession* session, uint8 tabId) const; + void _SendBankMoneyUpdate(WorldSession* session) const; + void _SendBankContentUpdate(MoveItemData* pSrc, MoveItemData* pDest) const; + void _SendBankContentUpdate(uint8 tabId, SlotIds slots) const; + void _SendBankList(WorldSession* session = NULL, uint8 tabId = 0, bool sendFullSlots = false, SlotIds *slots = NULL) const; + + void _BroadcastEvent(GuildEvents guildEvent, ObjectGuid guid, const char* param1 = NULL, const char* param2 = NULL, const char* param3 = NULL) const; }; #endif 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/ChannelHandler.cpp b/src/server/game/Handlers/ChannelHandler.cpp index 9285f4247b2..71cdddcebf5 100644 --- a/src/server/game/Handlers/ChannelHandler.cpp +++ b/src/server/game/Handlers/ChannelHandler.cpp @@ -17,11 +17,15 @@ */ #include "ObjectMgr.h" // for normalizePlayerName +#include "Channel.h" #include "ChannelMgr.h" #include "Player.h" +#include "WorldSession.h" #include <cctype> +static size_t const MAX_CHANNEL_PASS_STR = 31; + void WorldSession::HandleJoinChannel(WorldPacket& recvPacket) { uint32 channelId; @@ -33,13 +37,13 @@ void WorldSession::HandleJoinChannel(WorldPacket& recvPacket) TC_LOG_DEBUG("chat.system", "CMSG_JOIN_CHANNEL %s Channel: %u, unk1: %u, unk2: %u, channel: %s, password: %s", GetPlayerInfo().c_str(), channelId, unknown1, unknown2, channelName.c_str(), password.c_str()); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(GetPlayer()->GetZoneId()); if (channelId) { ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(channelId); if (!channel) return; - AreaTableEntry const* zone = sAreaTableStore.LookupEntry(GetPlayer()->GetZoneId()); if (!zone || !GetPlayer()->CanJoinConstantChannelInZone(channel, zone)) return; } @@ -51,30 +55,42 @@ void WorldSession::HandleJoinChannel(WorldPacket& recvPacket) return; if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - { - cMgr->setTeam(GetPlayer()->GetTeam()); - if (Channel* channel = cMgr->GetJoinChannel(channelName, channelId)) + if (Channel* channel = cMgr->GetJoinChannel(channelId, channelName, zone)) channel->JoinChannel(GetPlayer(), password); - } } void WorldSession::HandleLeaveChannel(WorldPacket& recvPacket) { - uint32 unk; + uint32 channelId; std::string channelName; - recvPacket >> unk >> channelName; + recvPacket >> channelId >> channelName; - TC_LOG_DEBUG("chat.system", "CMSG_LEAVE_CHANNEL %s Channel: %s, unk1: %u", - GetPlayerInfo().c_str(), channelName.c_str(), unk); + TC_LOG_DEBUG("chat.system", "CMSG_LEAVE_CHANNEL %s Channel: %s, channelId: %u", + GetPlayerInfo().c_str(), channelName.c_str(), channelId); - if (channelName.empty()) + if (channelName.empty() && !channelId) return; + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(GetPlayer()->GetZoneId()); + if (channelId) + { + ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(channelId); + if (!channel) + return; + + if (!zone || !GetPlayer()->CanJoinConstantChannelInZone(channel, zone)) + return; + } + if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) { - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) + if (Channel* channel = cMgr->GetChannel(channelId, channelName, GetPlayer(), true, zone)) channel->LeaveChannel(GetPlayer(), true); - cMgr->LeftChannel(channelName); + + if (channelId) + cMgr->LeftChannel(channelId, zone); + else + cMgr->LeftChannel(channelName); } } @@ -87,9 +103,8 @@ void WorldSession::HandleChannelList(WorldPacket& recvPacket) recvPacket.GetOpcode() == CMSG_CHANNEL_DISPLAY_LIST ? "CMSG_CHANNEL_DISPLAY_LIST" : "CMSG_CHANNEL_LIST", GetPlayerInfo().c_str(), channelName.c_str()); - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->List(GetPlayer()); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->List(GetPlayer()); } void WorldSession::HandleChannelPassword(WorldPacket& recvPacket) @@ -103,9 +118,8 @@ void WorldSession::HandleChannelPassword(WorldPacket& recvPacket) if (password.length() > MAX_CHANNEL_PASS_STR) return; - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->Password(GetPlayer(), password); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->Password(GetPlayer(), password); } void WorldSession::HandleChannelSetOwner(WorldPacket& recvPacket) @@ -119,9 +133,8 @@ void WorldSession::HandleChannelSetOwner(WorldPacket& recvPacket) if (!normalizePlayerName(targetName)) return; - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->SetOwner(GetPlayer(), targetName); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->SetOwner(GetPlayer(), targetName); } void WorldSession::HandleChannelOwner(WorldPacket& recvPacket) @@ -132,9 +145,8 @@ void WorldSession::HandleChannelOwner(WorldPacket& recvPacket) TC_LOG_DEBUG("chat.system", "CMSG_CHANNEL_OWNER %s Channel: %s", GetPlayerInfo().c_str(), channelName.c_str()); - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->SendWhoOwner(GetPlayer()->GetGUID()); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->SendWhoOwner(GetPlayer()->GetGUID()); } void WorldSession::HandleChannelModerator(WorldPacket& recvPacket) @@ -148,9 +160,8 @@ void WorldSession::HandleChannelModerator(WorldPacket& recvPacket) if (!normalizePlayerName(targetName)) return; - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->SetModerator(GetPlayer(), targetName); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->SetModerator(GetPlayer(), targetName); } void WorldSession::HandleChannelUnmoderator(WorldPacket& recvPacket) @@ -164,9 +175,8 @@ void WorldSession::HandleChannelUnmoderator(WorldPacket& recvPacket) if (!normalizePlayerName(targetName)) return; - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->UnsetModerator(GetPlayer(), targetName); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->UnsetModerator(GetPlayer(), targetName); } void WorldSession::HandleChannelMute(WorldPacket& recvPacket) @@ -180,9 +190,8 @@ void WorldSession::HandleChannelMute(WorldPacket& recvPacket) if (!normalizePlayerName(targetName)) return; - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->SetMute(GetPlayer(), targetName); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->SetMute(GetPlayer(), targetName); } void WorldSession::HandleChannelUnmute(WorldPacket& recvPacket) @@ -196,9 +205,8 @@ void WorldSession::HandleChannelUnmute(WorldPacket& recvPacket) if (!normalizePlayerName(targetName)) return; - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->UnsetMute(GetPlayer(), targetName); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->UnsetMute(GetPlayer(), targetName); } void WorldSession::HandleChannelInvite(WorldPacket& recvPacket) @@ -212,9 +220,8 @@ void WorldSession::HandleChannelInvite(WorldPacket& recvPacket) if (!normalizePlayerName(targetName)) return; - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->Invite(GetPlayer(), targetName); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->Invite(GetPlayer(), targetName); } void WorldSession::HandleChannelKick(WorldPacket& recvPacket) @@ -228,9 +235,8 @@ void WorldSession::HandleChannelKick(WorldPacket& recvPacket) if (!normalizePlayerName(targetName)) return; - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->Kick(GetPlayer(), targetName); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->Kick(GetPlayer(), targetName); } void WorldSession::HandleChannelBan(WorldPacket& recvPacket) @@ -244,9 +250,8 @@ void WorldSession::HandleChannelBan(WorldPacket& recvPacket) if (!normalizePlayerName(targetName)) return; - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->Ban(GetPlayer(), targetName); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->Ban(GetPlayer(), targetName); } void WorldSession::HandleChannelUnban(WorldPacket& recvPacket) @@ -260,9 +265,8 @@ void WorldSession::HandleChannelUnban(WorldPacket& recvPacket) if (!normalizePlayerName(targetName)) return; - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->UnBan(GetPlayer(), targetName); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->UnBan(GetPlayer(), targetName); } void WorldSession::HandleChannelAnnouncements(WorldPacket& recvPacket) @@ -273,9 +277,8 @@ void WorldSession::HandleChannelAnnouncements(WorldPacket& recvPacket) TC_LOG_DEBUG("chat.system", "CMSG_CHANNEL_ANNOUNCEMENTS %s Channel: %s", GetPlayerInfo().c_str(), channelName.c_str()); - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->Announce(GetPlayer()); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->Announce(GetPlayer()); } void WorldSession::HandleChannelDisplayListQuery(WorldPacket &recvPacket) @@ -292,19 +295,17 @@ void WorldSession::HandleGetChannelMemberCount(WorldPacket &recvPacket) TC_LOG_DEBUG("chat.system", "CMSG_GET_CHANNEL_MEMBER_COUNT %s Channel: %s", GetPlayerInfo().c_str(), channelName.c_str()); - if (ChannelMgr* cMgr = ChannelMgr::forTeam(GetPlayer()->GetTeam())) + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) { - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - { - TC_LOG_DEBUG("chat.system", "SMSG_CHANNEL_MEMBER_COUNT %s Channel: %s Count: %u", - GetPlayerInfo().c_str(), channelName.c_str(), channel->GetNumPlayers()); - - WorldPacket data(SMSG_CHANNEL_MEMBER_COUNT, channel->GetName().size() + 1 + 4); - data << channel->GetName(); - data << uint8(channel->GetFlags()); - data << uint32(channel->GetNumPlayers()); - SendPacket(&data); - } + TC_LOG_DEBUG("chat.system", "SMSG_CHANNEL_MEMBER_COUNT %s Channel: %s Count: %u", + GetPlayerInfo().c_str(), channelName.c_str(), channel->GetNumPlayers()); + + std::string name = channel->GetName(GetSessionDbcLocale()); + WorldPacket data(SMSG_CHANNEL_MEMBER_COUNT, name.size() + 1 + 4); + data << name; + data << uint8(channel->GetFlags()); + data << uint32(channel->GetNumPlayers()); + SendPacket(&data); } } @@ -317,8 +318,7 @@ void WorldSession::HandleSetChannelWatch(WorldPacket &recvPacket) GetPlayerInfo().c_str(), channelName.c_str()); /* - if (ChannelMgr* cMgr = channelMgr(GetPlayer()->GetTeam())) - if (Channel* channel = cMgr->GetChannel(channelName, GetPlayer())) - channel->JoinNotify(GetPlayer()); + if (Channel* channel = ChannelMgr::GetChannelForPlayerByNamePart(channelName, GetPlayer())) + channel->JoinNotify(GetPlayer()); */ } diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 53c62858c04..157511afd96 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1843,13 +1843,10 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) { // Reset guild stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUILD_MEMBER); - stmt->setUInt32(0, lowGuid); - - PreparedQueryResult result = CharacterDatabase.Query(stmt); - if (result) + if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) if (Guild* guild = sGuildMgr->GetGuildById((result->Fetch()[0]).GetUInt32())) - guild->DeleteMember(factionChangeInfo.Guid, false, false, true); + guild->DeleteMember(trans, factionChangeInfo.Guid, false, false, true); Player::LeaveAllArenaTeams(factionChangeInfo.Guid); } diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp index 2b1660fef2b..ba5164f9be5 100644 --- a/src/server/game/Handlers/ChatHandler.cpp +++ b/src/server/game/Handlers/ChatHandler.cpp @@ -26,6 +26,7 @@ #include "DatabaseEnv.h" #include "CellImpl.h" #include "Chat.h" +#include "Channel.h" #include "ChannelMgr.h" #include "GridNotifiersImpl.h" #include "Group.h" @@ -461,13 +462,10 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) } } - if (ChannelMgr* cMgr = ChannelMgr::forTeam(sender->GetTeam())) + if (Channel* chn = ChannelMgr::GetChannelForPlayerByNamePart(channel, sender)) { - if (Channel* chn = cMgr->GetChannel(channel, sender)) - { - sScriptMgr->OnPlayerChat(sender, type, lang, msg, chn); - chn->Say(sender->GetGUID(), msg.c_str(), lang); - } + sScriptMgr->OnPlayerChat(sender, type, lang, msg, chn); + chn->Say(sender->GetGUID(), msg.c_str(), lang); } break; } 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/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index a0b3d46b38c..3ac638c246d 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -1408,7 +1408,7 @@ void WorldSession::HandleFarSightOpcode(WorldPacket& recvData) if (WorldObject* target = _player->GetViewpoint()) _player->SetSeer(target); else - TC_LOG_ERROR("network", "Player %s (%s) requests non-existing seer %s", _player->GetName().c_str(), _player->GetGUID().ToString().c_str(), _player->GetGuidValue(PLAYER_FARSIGHT).ToString().c_str()); + TC_LOG_DEBUG("network", "Player %s (%s) requests non-existing seer %s", _player->GetName().c_str(), _player->GetGUID().ToString().c_str(), _player->GetGuidValue(PLAYER_FARSIGHT).ToString().c_str()); } else { @@ -1741,9 +1741,9 @@ void WorldSession::HandleHearthAndResurrect(WorldPacket& /*recvData*/) if (_player->IsInFlight()) return; - if (/*Battlefield* bf = */sBattlefieldMgr->GetBattlefieldToZoneId(_player->GetZoneId())) + if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(_player->GetZoneId())) { - // bf->PlayerAskToLeave(_player); FIXME + bf->PlayerAskToLeave(_player); return; } diff --git a/src/server/game/Handlers/PetitionsHandler.cpp b/src/server/game/Handlers/PetitionsHandler.cpp index c0b5db65d90..a4d41bbff1f 100644 --- a/src/server/game/Handlers/PetitionsHandler.cpp +++ b/src/server/game/Handlers/PetitionsHandler.cpp @@ -849,12 +849,18 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket& recvData) Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, ERR_GUILD_COMMAND_SUCCESS, name); - // Add members from signatures - for (uint8 i = 0; i < signatures; ++i) { - Field* fields = result->Fetch(); - guild->AddMember(ObjectGuid(HighGuid::Player, fields[0].GetUInt32())); - result->NextRow(); + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + + // Add members from signatures + for (uint8 i = 0; i < signatures; ++i) + { + Field* fields = result->Fetch(); + guild->AddMember(trans, ObjectGuid(HighGuid::Player, fields[0].GetUInt32())); + result->NextRow(); + } + + CharacterDatabase.CommitTransaction(trans); } } else 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 260e7ff464f..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,14 +380,14 @@ 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 @@ -429,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; } } @@ -483,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); @@ -517,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; } } @@ -540,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) { @@ -553,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 @@ -578,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; @@ -657,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) @@ -717,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]; @@ -740,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; @@ -756,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; @@ -782,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); } @@ -802,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) @@ -815,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) @@ -828,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) @@ -976,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) @@ -1017,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) @@ -1035,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; } } @@ -1653,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 @@ -1678,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()) @@ -1741,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 a66b8f0b4c5..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; @@ -271,13 +271,13 @@ class TC_GAME_API LootTemplate LootGroups Groups; // groups have own (optimised) processing, grouped entries go there // Objects of this class must never be copied, we are storing pointers in container - LootTemplate(LootTemplate const&); - LootTemplate& operator=(LootTemplate const&); + LootTemplate(LootTemplate const&) = delete; + LootTemplate& operator=(LootTemplate const&) = delete; }; //===================================================== -class LootValidatorRef : public Reference<Loot, LootValidatorRef> +class LootValidatorRef : public Reference<Loot, LootValidatorRef> { public: LootValidatorRef() { } @@ -293,12 +293,9 @@ class LootValidatorRefManager : public RefManager<Loot, LootValidatorRef> typedef LinkedListHead::Iterator< LootValidatorRef > iterator; LootValidatorRef* getFirst() { return (LootValidatorRef*)RefManager<Loot, LootValidatorRef>::getFirst(); } - LootValidatorRef* getLast() { return (LootValidatorRef*)RefManager<Loot, LootValidatorRef>::getLast(); } iterator begin() { return iterator(getFirst()); } - iterator end() { return iterator(NULL); } - iterator rbegin() { return iterator(getLast()); } - iterator rend() { return iterator(NULL); } + iterator end() { return iterator(nullptr); } }; //===================================================== @@ -311,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; @@ -343,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(); @@ -380,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; @@ -388,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/Maps/MapRefManager.h b/src/server/game/Maps/MapRefManager.h index 09aa67d43eb..14d1d59bd36 100644 --- a/src/server/game/Maps/MapRefManager.h +++ b/src/server/game/Maps/MapRefManager.h @@ -26,20 +26,17 @@ class MapReference; class MapRefManager : public RefManager<Map, Player> { public: - typedef LinkedListHead::Iterator< MapReference > iterator; - typedef LinkedListHead::Iterator< MapReference const > const_iterator; + typedef LinkedListHead::Iterator<MapReference> iterator; + typedef LinkedListHead::Iterator<MapReference const> const_iterator; - MapReference* getFirst() { return (MapReference*)RefManager<Map, Player>::getFirst(); } + MapReference* getFirst() { return (MapReference*)RefManager<Map, Player>::getFirst(); } MapReference const* getFirst() const { return (MapReference const*)RefManager<Map, Player>::getFirst(); } - MapReference* getLast() { return (MapReference*)RefManager<Map, Player>::getLast(); } - MapReference const* getLast() const { return (MapReference const*)RefManager<Map, Player>::getLast(); } iterator begin() { return iterator(getFirst()); } - iterator end() { return iterator(NULL); } - iterator rbegin() { return iterator(getLast()); } - iterator rend() { return iterator(NULL); } + iterator end() { return iterator(nullptr); } + const_iterator begin() const { return const_iterator(getFirst()); } - const_iterator end() const { return const_iterator(NULL); } + const_iterator end() const { return const_iterator(nullptr); } }; #endif diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 7f77453d00b..bf913081fef 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) @@ -472,7 +472,7 @@ enum SpellAttr5 SPELL_ATTR5_HASTE_AFFECT_DURATION = 0x00002000, // 13 haste effects decrease duration of this SPELL_ATTR5_UNK14 = 0x00004000, // 14 SPELL_ATTR5_UNK15 = 0x00008000, // 15 Inflits on multiple targets? - SPELL_ATTR5_SPECIAL_ITEM_CLASS_CHECK = 0x00010000, // 16 this allows spells with EquippedItemClass to affect spells from other items if the required item is equipped + SPELL_ATTR5_UNK16 = 0x00010000, // 16 SPELL_ATTR5_USABLE_WHILE_FEARED = 0x00020000, // 17 usable while feared SPELL_ATTR5_USABLE_WHILE_CONFUSED = 0x00040000, // 18 usable while confused SPELL_ATTR5_DONT_TURN_DURING_CAST = 0x00080000, // 19 Blocks caster's turning when casting (client does not automatically turn caster's model to face UNIT_FIELD_TARGET) @@ -517,7 +517,7 @@ enum SpellAttr6 SPELL_ATTR6_UNK22 = 0x00400000, // 22 only 72054 SPELL_ATTR6_UNK23 = 0x00800000, // 23 SPELL_ATTR6_CAN_TARGET_UNTARGETABLE = 0x01000000, // 24 - SPELL_ATTR6_UNK25 = 0x02000000, // 25 Exorcism, Flash of Light + SPELL_ATTR6_NOT_RESET_SWING_IF_INSTANT = 0x02000000, // 25 Exorcism, Flash of Light SPELL_ATTR6_UNK26 = 0x04000000, // 26 related to player castable positive buff SPELL_ATTR6_UNK27 = 0x08000000, // 27 SPELL_ATTR6_UNK28 = 0x10000000, // 28 Death Grip @@ -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.] @@ -3114,7 +3115,7 @@ enum DiminishingReturnsType }; // Diminishing Return Groups -enum DiminishingGroup +enum DiminishingGroup : uint16 { DIMINISHING_NONE = 0, DIMINISHING_BANISH = 1, @@ -3136,7 +3137,9 @@ enum DiminishingGroup 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 d0079ad1524..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) @@ -1641,6 +1644,8 @@ bool ScriptMgr::CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureTemp ASSERT(actTemplate); CreatureTemplate const* baseTemplate = sObjectMgr->GetCreatureTemplate(entry); + if (!baseTemplate) + baseTemplate = actTemplate; GET_SCRIPT_RET(CreatureScript, baseTemplate->ScriptID, tmpscript, true); return tmpscript->CanSpawn(spawnId, entry, baseTemplate, actTemplate, cData, map); } 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 980926a476c..2b4e2e4177d 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -99,8 +99,8 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleAuraModSchoolImmunity, // 39 SPELL_AURA_SCHOOL_IMMUNITY &AuraEffect::HandleAuraModDmgImmunity, // 40 SPELL_AURA_DAMAGE_IMMUNITY &AuraEffect::HandleAuraModDispelImmunity, // 41 SPELL_AURA_DISPEL_IMMUNITY - &AuraEffect::HandleNoImmediateEffect, // 42 SPELL_AURA_PROC_TRIGGER_SPELL implemented in Unit::ProcDamageAndSpellFor and Unit::HandleProcTriggerSpell - &AuraEffect::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in Unit::ProcDamageAndSpellFor + &AuraEffect::HandleNoImmediateEffect, // 42 SPELL_AURA_PROC_TRIGGER_SPELL implemented in AuraEffect::HandleProc + &AuraEffect::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in AuraEffect::HandleProc &AuraEffect::HandleAuraTrackCreatures, // 44 SPELL_AURA_TRACK_CREATURES &AuraEffect::HandleAuraTrackResources, // 45 SPELL_AURA_TRACK_RESOURCES &AuraEffect::HandleNULL, // 46 SPELL_AURA_46 (used in test spells 54054 and 54058, and spell 48050) (3.0.8a) @@ -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 @@ -308,7 +308,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleNoImmediateEffect, //248 SPELL_AURA_MOD_COMBAT_RESULT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst &AuraEffect::HandleAuraConvertRune, //249 SPELL_AURA_CONVERT_RUNE &AuraEffect::HandleAuraModIncreaseHealth, //250 SPELL_AURA_MOD_INCREASE_HEALTH_2 - &AuraEffect::HandleNoImmediateEffect, //251 SPELL_AURA_MOD_ENEMY_DODGE + &AuraEffect::HandleNoImmediateEffect, //251 SPELL_AURA_MOD_ENEMY_DODGE implemented in Unit::GetUnitDodgeChance &AuraEffect::HandleModCombatSpeedPct, //252 SPELL_AURA_252 Is there any difference between this and SPELL_AURA_MELEE_SLOW ? maybe not stacking mod? &AuraEffect::HandleNoImmediateEffect, //253 SPELL_AURA_MOD_BLOCK_CRIT_CHANCE implemented in Unit::isBlockCritical &AuraEffect::HandleAuraModDisarm, //254 SPELL_AURA_MOD_DISARM_OFFHAND @@ -319,7 +319,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleNoImmediateEffect, //259 SPELL_AURA_MOD_HOT_PCT implemented in Unit::SpellHealingBonus &AuraEffect::HandleNoImmediateEffect, //260 SPELL_AURA_SCREEN_EFFECT (miscvalue = id in ScreenEffect.dbc) not required any code &AuraEffect::HandlePhase, //261 SPELL_AURA_PHASE - &AuraEffect::HandleNoImmediateEffect, //262 SPELL_AURA_ABILITY_IGNORE_AURASTATE implemented in spell::cancast + &AuraEffect::HandleNoImmediateEffect, //262 SPELL_AURA_ABILITY_IGNORE_AURASTATE implemented in Spell::CheckCast &AuraEffect::HandleAuraAllowOnlyAbility, //263 SPELL_AURA_ALLOW_ONLY_ABILITY player can use only abilities set in SpellClassMask &AuraEffect::HandleUnused, //264 unused (3.2.0) &AuraEffect::HandleUnused, //265 unused (3.2.0) @@ -494,7 +494,7 @@ int32 AuraEffect::CalculateAmount(Unit* caster) return amount; } -void AuraEffect::CalculatePeriodic(Unit* caster, bool create, bool load) +void AuraEffect::CalculatePeriodic(Unit* caster, bool resetPeriodicTimer /*= true*/, bool load /*= false*/) { m_amplitude = m_spellInfo->Effects[m_effIndex].Amplitude; @@ -528,14 +528,13 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool create, bool load) if (!m_isPeriodic) return; - Player* modOwner = caster ? caster->GetSpellModOwner() : NULL; - + Player* modOwner = caster ? caster->GetSpellModOwner() : nullptr; // Apply casting time mods if (m_amplitude) { // Apply periodic time mod if (modOwner) - modOwner->ApplySpellMod(GetId(), SPELLMOD_ACTIVATION_TIME, m_amplitude); + modOwner->ApplySpellMod<SPELLMOD_ACTIVATION_TIME>(GetId(), m_amplitude); if (caster) { @@ -558,12 +557,9 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool create, bool load) else // aura just created or reapplied { m_tickNumber = 0; - // reset periodic timer on aura create or on reapply when aura isn't dot - // possibly we should not reset periodic timers only when aura is triggered by proc - // or maybe there's a spell attribute somewhere - bool resetPeriodicTimer = create - || ((GetAuraType() != SPELL_AURA_PERIODIC_DAMAGE) && (GetAuraType() != SPELL_AURA_PERIODIC_DAMAGE_PERCENT)); + // reset periodic timer on aura create or reapply + // we don't reset periodic timers when aura is triggered by proc if (resetPeriodicTimer) { m_periodicTimer = 0; @@ -588,7 +584,6 @@ void AuraEffect::CalculateSpellMod() m_spellmod->type = SpellModType(uint32(GetAuraType())); // SpellModType value == spell aura types m_spellmod->spellId = GetId(); m_spellmod->mask = GetSpellInfo()->Effects[GetEffIndex()].SpellClassMask; - m_spellmod->charges = GetBase()->GetCharges(); } m_spellmod->value = GetAmount(); break; @@ -895,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 @@ -961,6 +954,80 @@ void AuraEffect::PeriodicTick(AuraApplication * aurApp, Unit* caster) const } } +bool AuraEffect::CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) const +{ + bool result = GetBase()->CallScriptCheckEffectProcHandlers(this, aurApp, eventInfo); + if (!result) + return false; + + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + switch (GetAuraType()) + { + case SPELL_AURA_MOD_CONFUSE: + case SPELL_AURA_MOD_FEAR: + case SPELL_AURA_MOD_STUN: + case SPELL_AURA_MOD_ROOT: + case SPELL_AURA_TRANSFORM: + { + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return false; + + // Spell own damage at apply won't break CC + if (spellInfo && spellInfo == GetSpellInfo()) + { + Aura* aura = GetBase(); + // called from spellcast, should not have ticked yet + if (aura->GetDuration() == aura->GetMaxDuration()) + return false; + } + break; + } + case SPELL_AURA_MECHANIC_IMMUNITY: + case SPELL_AURA_MOD_MECHANIC_RESISTANCE: + // compare mechanic + if (!spellInfo || static_cast<int32>(spellInfo->Mechanic) != GetMiscValue()) + return false; + break; + case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK: + // skip melee hits and instant cast spells + if (!spellInfo || !spellInfo->CalcCastTime()) + return false; + break; + case SPELL_AURA_MOD_DAMAGE_FROM_CASTER: + // Compare casters + if (GetCasterGUID() != eventInfo.GetActor()->GetGUID()) + return false; + break; + case SPELL_AURA_MOD_POWER_COST_SCHOOL: + case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT: + // Skip melee hits and spells with wrong school or zero cost + if (!spellInfo || (!spellInfo->ManaCost && !spellInfo->ManaCostPercentage) || // Cost Check + !(spellInfo->GetSchoolMask() & GetMiscValue())) // School Check + return false; + break; + case SPELL_AURA_REFLECT_SPELLS_SCHOOL: + // Skip melee hits and spells with wrong school + if (!spellInfo || !(spellInfo->GetSchoolMask() & GetMiscValue())) + return false; + break; + case SPELL_AURA_PROC_TRIGGER_SPELL: + case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE: + { + // Don't proc extra attacks while already processing extra attack spell + uint32 triggerSpellId = GetSpellInfo()->Effects[GetEffIndex()].TriggerSpell; + if (SpellInfo const* triggeredSpellInfo = sSpellMgr->GetSpellInfo(triggerSpellId)) + if (aurApp->GetTarget()->m_extraAttacks && triggeredSpellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) + return false; + break; + } + default: + break; + } + + return result; +} + void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) { bool prevented = GetBase()->CallScriptEffectProcHandlers(this, aurApp, eventInfo); @@ -969,6 +1036,16 @@ void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) switch (GetAuraType()) { + // CC Auras which use their amount to drop + // Are there any more auras which need this? + case SPELL_AURA_MOD_CONFUSE: + case SPELL_AURA_MOD_FEAR: + case SPELL_AURA_MOD_STUN: + case SPELL_AURA_MOD_ROOT: + case SPELL_AURA_TRANSFORM: + HandleBreakableCCAuraProc(aurApp, eventInfo); + break; + case SPELL_AURA_DUMMY: case SPELL_AURA_PROC_TRIGGER_SPELL: HandleProcTriggerSpellAuraProc(aurApp, eventInfo); break; @@ -1094,15 +1171,15 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const if (apply) { if (spellId) - target->CastSpell(target, spellId, true, NULL, this); + target->CastSpell(target, spellId, true, nullptr, this); if (spellId2) - target->CastSpell(target, spellId2, true, NULL, this); + target->CastSpell(target, spellId2, true, nullptr, this); if (target->GetTypeId() == TYPEID_PLAYER) { - const PlayerSpellMap& sp_list = target->ToPlayer()->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + PlayerSpellMap const& sp_list = target->ToPlayer()->GetSpellMap(); + for (auto itr = sp_list.begin(); itr != sp_list.end(); ++itr) { if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled) continue; @@ -1111,11 +1188,11 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); - if (!spellInfo || !(spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE) || spellInfo->HasAttribute(SPELL_ATTR0_HIDDEN_CLIENTSIDE))) + if (!spellInfo || !(spellInfo->IsPassive() || spellInfo->HasAttribute(SPELL_ATTR0_HIDDEN_CLIENTSIDE))) continue; if (spellInfo->Stances & (UI64LIT(1) << (GetMiscValue() - 1))) - target->CastSpell(target, itr->first, true, NULL, this); + target->CastSpell(target, itr->first, true, nullptr, this); } // Also do it for Glyphs @@ -1130,7 +1207,7 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const continue; if (spellInfo->Stances & (UI64LIT(1) << (GetMiscValue() - 1))) - target->CastSpell(target, glyph->SpellId, true, NULL, this); + target->CastSpell(target, glyph->SpellId, true, nullptr, this); } } } @@ -1140,7 +1217,7 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(24932); if (spellInfo && spellInfo->Stances & (UI64LIT(1) << (GetMiscValue() - 1))) - target->CastSpell(target, 24932, true, NULL, this); + target->CastSpell(target, 24932, true, nullptr, this); } // Improved Barkskin - apply/remove armor bonus due to shapeshift if (target->ToPlayer()->HasSpell(63410) || target->ToPlayer()->HasSpell(63411)) @@ -1153,14 +1230,14 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const if (HotWSpellId) { // hacky, but the only way as spell family is not SPELLFAMILY_DRUID Unit::AuraEffectList const& mModTotalStatPct = target->GetAuraEffectsByType(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE); - for (Unit::AuraEffectList::const_iterator i = mModTotalStatPct.begin(); i != mModTotalStatPct.end(); ++i) + for (AuraEffect const* aurEff : mModTotalStatPct) { // Heart of the Wild - if ((*i)->GetSpellInfo()->SpellIconID == 240 && (*i)->GetMiscValue() == 3) + if (aurEff->GetSpellInfo()->SpellIconID == 240 && aurEff->GetMiscValue() == 3) { - int32 HotWMod = (*i)->GetAmount() / 2; // For each 2% Intelligence, you get 1% stamina and 1% attack power. + int32 HotWMod = aurEff->GetAmount() / 2; // For each 2% Intelligence, you get 1% stamina and 1% attack power. - target->CastCustomSpell(target, HotWSpellId, &HotWMod, NULL, NULL, true, NULL, this); + target->CastCustomSpell(HotWSpellId, SPELLVALUE_BASE_POINT0, HotWMod, target, true, nullptr, this); break; } } @@ -1184,13 +1261,13 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const spellId3 = 47180; break; } - target->CastSpell(target, spellId3, true, NULL, this); + target->CastSpell(target, spellId3, true, nullptr, this); } // Master Shapeshifter - Cat if (AuraEffect const* aurEff = target->GetDummyAuraEffect(SPELLFAMILY_GENERIC, 2851, 0)) { int32 bp = aurEff->GetAmount(); - target->CastCustomSpell(target, 48420, &bp, NULL, NULL, true); + target->CastCustomSpell(48420, SPELLVALUE_BASE_POINT0, bp, target, true); } break; case FORM_DIREBEAR: @@ -1199,13 +1276,13 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const if (AuraEffect const* aurEff = target->GetDummyAuraEffect(SPELLFAMILY_GENERIC, 2851, 0)) { int32 bp = aurEff->GetAmount(); - target->CastCustomSpell(target, 48418, &bp, NULL, NULL, true); + target->CastCustomSpell(48418, SPELLVALUE_BASE_POINT0, bp, target, true); } // Survival of the Fittest if (AuraEffect const* aurEff = target->GetAuraEffect(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE, SPELLFAMILY_DRUID, 961, 0)) { int32 bp = aurEff->GetSpellInfo()->Effects[EFFECT_2].CalcValue(); - target->CastCustomSpell(target, 62069, &bp, NULL, NULL, true, 0, this); + target->CastCustomSpell(62069, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, this); } break; case FORM_MOONKIN: @@ -1213,7 +1290,7 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const if (AuraEffect const* aurEff = target->GetDummyAuraEffect(SPELLFAMILY_GENERIC, 2851, 0)) { int32 bp = aurEff->GetAmount(); - target->CastCustomSpell(target, 48421, &bp, NULL, NULL, true); + target->CastCustomSpell(48421, SPELLVALUE_BASE_POINT0, bp, target, true); } break; // Master Shapeshifter - Tree of Life @@ -1221,7 +1298,7 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const if (AuraEffect const* aurEff = target->GetDummyAuraEffect(SPELLFAMILY_GENERIC, 2851, 0)) { int32 bp = aurEff->GetAmount(); - target->CastCustomSpell(target, 48422, &bp, NULL, NULL, true); + target->CastCustomSpell(48422, SPELLVALUE_BASE_POINT0, bp, target, true); } break; } @@ -1235,7 +1312,7 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const target->RemoveOwnedAura(spellId2, target->GetGUID()); // Improved Barkskin - apply/remove armor bonus due to shapeshift - if (Player* player=target->ToPlayer()) + if (Player* player = target->ToPlayer()) { if (player->HasSpell(63410) || player->HasSpell(63411)) { @@ -1244,19 +1321,20 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const } } - const Unit::AuraEffectList& shapeshifts = target->GetAuraEffectsByType(SPELL_AURA_MOD_SHAPESHIFT); - AuraEffect* newAura = NULL; + Unit::AuraEffectList const& shapeshifts = target->GetAuraEffectsByType(SPELL_AURA_MOD_SHAPESHIFT); + AuraEffect const* newAura = nullptr; // Iterate through all the shapeshift auras that the target has, if there is another aura with SPELL_AURA_MOD_SHAPESHIFT, then this aura is being removed due to that one being applied - for (Unit::AuraEffectList::const_iterator itr = shapeshifts.begin(); itr != shapeshifts.end(); ++itr) + for (AuraEffect const* aurEff : shapeshifts) { - if ((*itr) != this) + if (aurEff != this) { - newAura = *itr; + newAura = aurEff; break; } } + Unit::AuraApplicationMap& tAuras = target->GetAppliedAuras(); - for (Unit::AuraApplicationMap::iterator itr = tAuras.begin(); itr != tAuras.end();) + for (auto itr = tAuras.begin(); itr != tAuras.end();) { // Use the new aura to see on what stance the target will be uint64 newStance = newAura ? (UI64LIT(1) << (newAura->GetMiscValue() - 1)) : 0; @@ -1697,7 +1775,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()); @@ -1769,11 +1852,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(); @@ -2225,11 +2307,9 @@ void AuraEffect::HandleAuraModDisarm(AuraApplication const* aurApp, uint8 mode, { uint8 attacktype = Player::GetAttackBySlot(slot); + player->ApplyItemDependentAuras(item, !apply); if (attacktype < MAX_ATTACK) - { player->_ApplyWeaponDamage(slot, item->GetTemplate(), NULL, !apply); - player->_ApplyWeaponDependentAuraMods(item, WeaponAttackType(attacktype), !apply); - } } } @@ -2987,249 +3067,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 @@ -3238,52 +3082,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 @@ -3292,8 +3091,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(); @@ -3315,11 +3113,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 @@ -3328,8 +3122,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) { @@ -3339,12 +3132,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); } @@ -3357,28 +3153,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 school_mask = GetMiscValue(); - Unit::AuraApplicationMap& Auras = target->GetAppliedAuras(); - for (Unit::AuraApplicationMap::iterator iter = Auras.begin(); iter != Auras.end();) - { - SpellInfo const* spell = iter->second->GetBase()->GetSpellInfo(); - if ((spell->GetSchoolMask() & school_mask)//Check for school mask - && GetSpellInfo()->CanDispelAura(spell) - && !iter->second->IsPositive() //Don't remove positive spells - && spell->Id != GetId()) //Don't remove self - { - target->RemoveAura(iter); - } - else - ++iter; - } - } } void AuraEffect::HandleAuraModDmgImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3387,8 +3161,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 @@ -3397,8 +3170,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); } /*********************************************************/ @@ -3994,29 +3766,14 @@ void AuraEffect::HandleAuraModWeaponCritPercent(AuraApplication const* aurApp, u if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) return; - Unit* target = aurApp->GetTarget(); + Player* target = aurApp->GetTarget()->ToPlayer(); - if (target->GetTypeId() != TYPEID_PLAYER) + if (!target) return; - for (int i = 0; i < MAX_ATTACK; ++i) - if (Item* pItem = target->ToPlayer()->GetWeaponForAttack(WeaponAttackType(i), true)) - target->ToPlayer()->_ApplyWeaponDependentAuraCritMod(pItem, WeaponAttackType(i), this, apply); - - // mods must be applied base at equipped weapon class and subclass comparison - // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask - // GetMiscValue() comparison with item generated damage types - - if (GetSpellInfo()->EquippedItemClass == -1) - { - target->ToPlayer()->HandleBaseModValue(CRIT_PERCENTAGE, FLAT_MOD, float (GetAmount()), apply); - target->ToPlayer()->HandleBaseModValue(OFFHAND_CRIT_PERCENTAGE, FLAT_MOD, float (GetAmount()), apply); - target->ToPlayer()->HandleBaseModValue(RANGED_CRIT_PERCENTAGE, FLAT_MOD, float (GetAmount()), apply); - } - else - { - // done in Player::_ApplyWeaponDependentAuraMods - } + target->HandleBaseModValue(CRIT_PERCENTAGE, FLAT_MOD, float(GetAmount()), apply); + target->HandleBaseModValue(OFFHAND_CRIT_PERCENTAGE, FLAT_MOD, float(GetAmount()), apply); + target->HandleBaseModValue(RANGED_CRIT_PERCENTAGE, FLAT_MOD, float(GetAmount()), apply); } void AuraEffect::HandleModHitChance(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -4317,6 +4074,7 @@ void AuraEffect::HandleAuraModAttackPowerOfArmor(AuraApplication const* aurApp, if (target->GetTypeId() == TYPEID_PLAYER) target->ToPlayer()->UpdateAttackPowerAndDamage(false); } + /********************************/ /*** DAMAGE BONUS ***/ /********************************/ @@ -4327,79 +4085,22 @@ void AuraEffect::HandleModDamageDone(AuraApplication const* aurApp, uint8 mode, Unit* target = aurApp->GetTarget(); - // apply item specific bonuses for already equipped weapon - if (target->GetTypeId() == TYPEID_PLAYER) - { - for (int i = 0; i < MAX_ATTACK; ++i) - if (Item* pItem = target->ToPlayer()->GetWeaponForAttack(WeaponAttackType(i), true)) - target->ToPlayer()->_ApplyWeaponDependentAuraDamageMod(pItem, WeaponAttackType(i), this, apply); - } - - // GetMiscValue() is bitmask of spell schools - // 1 (0-bit) - normal school damage (SPELL_SCHOOL_MASK_NORMAL) - // 126 - full bitmask all magic damages (SPELL_SCHOOL_MASK_MAGIC) including wands - // 127 - full bitmask any damages - // - // mods must be applied base at equipped weapon class and subclass comparison - // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask - // GetMiscValue() comparison with item generated damage types - - if ((GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) != 0) - { - // apply generic physical damage bonuses including wand case - if (GetSpellInfo()->EquippedItemClass == -1 || target->GetTypeId() != TYPEID_PLAYER) - { - target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(GetAmount()), apply); - target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(GetAmount()), apply); - target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(GetAmount()), apply); - - if (target->GetTypeId() == TYPEID_PLAYER) - { - if (GetAmount() > 0) - target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS, GetAmount(), apply); - else - target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG, GetAmount(), apply); - } - } - else - { - // done in Player::_ApplyWeaponDependentAuraMods - } - } - - // Skip non magic case for speedup - if ((GetMiscValue() & SPELL_SCHOOL_MASK_MAGIC) == 0) - return; - - if (GetSpellInfo()->EquippedItemClass != -1 || GetSpellInfo()->EquippedItemInventoryTypeMask != 0) + if (GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) { - // wand magic case (skip generic to all item spell bonuses) - // done in Player::_ApplyWeaponDependentAuraMods - - // Skip item specific requirements for not wand magic damage - return; + target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(GetAmount()), apply); + target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(GetAmount()), apply); + target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(GetAmount()), apply); } - // Magic damage modifiers implemented in Unit::SpellDamageBonus + // Magic damage modifiers implemented in Unit::SpellBaseDamageBonusDone // This information for client side use only if (target->GetTypeId() == TYPEID_PLAYER) { - if (GetAmount() > 0) - { - for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++) - { - if ((GetMiscValue() & (1<<i)) != 0) - target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, GetAmount(), apply); - } - } - else - { - for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++) - { - if ((GetMiscValue() & (1<<i)) != 0) - target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG+i, GetAmount(), apply); - } - } + uint16 baseField = GetAmount() >= 0 ? PLAYER_FIELD_MOD_DAMAGE_DONE_POS : PLAYER_FIELD_MOD_DAMAGE_DONE_NEG; + for (uint16 i = 0; i < MAX_SPELL_SCHOOL; ++i) + if (GetMiscValue() & (1 << i)) + target->ApplyModUInt32Value(baseField + i, GetAmount(), apply); + if (Guardian* pet = target->ToPlayer()->GetGuardianPet()) pet->UpdateAttackPowerAndDamage(); } @@ -4415,14 +4116,7 @@ void AuraEffect::HandleModDamagePercentDone(AuraApplication const* aurApp, uint8 if (abs(spellGroupVal) >= abs(GetAmount())) return; - if (target->GetTypeId() == TYPEID_PLAYER) - { - for (int i = 0; i < MAX_ATTACK; ++i) - if (Item* item = target->ToPlayer()->GetWeaponForAttack(WeaponAttackType(i), false)) - target->ToPlayer()->_ApplyWeaponDependentAuraDamageMod(item, WeaponAttackType(i), this, apply); - } - - if ((GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) && (GetSpellInfo()->EquippedItemClass == -1 || target->GetTypeId() != TYPEID_PLAYER)) + if (GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) { if (spellGroupVal) { @@ -4430,22 +4124,25 @@ void AuraEffect::HandleModDamagePercentDone(AuraApplication const* aurApp, uint8 target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(spellGroupVal), !apply); target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT, float(spellGroupVal), !apply); } + target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, float(GetAmount()), apply); target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(GetAmount()), apply); target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT, float(GetAmount()), apply); + } - if (Player* player = target->ToPlayer()) + if (target->GetTypeId() == TYPEID_PLAYER) + { + for (uint16 i = 0; i < MAX_SPELL_SCHOOL; ++i) { - if (spellGroupVal) - player->ApplyPercentModFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT, float(spellGroupVal), !apply); + if (GetMiscValue() & (1 << i)) + { + if (spellGroupVal) + target->ApplyPercentModFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT + i, float(spellGroupVal), !apply); - player->ApplyPercentModFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT, float(GetAmount()), apply); + target->ApplyPercentModFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT + i, float(GetAmount()), apply); + } } } - else - { - // done in Player::_ApplyWeaponDependentAuraMods for SPELL_SCHOOL_MASK_NORMAL && EquippedItemClass != -1 and also for wand case - } } void AuraEffect::HandleModOffhandDamagePercent(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -4672,10 +4369,6 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool if (target->GetTypeId() == TYPEID_PLAYER) target->ToPlayer()->RemoveAmmo(); // not use ammo and not allow use break; - case 71563: - if (Aura* newAura = target->AddAura(71564, target)) - newAura->SetStackAmount(newAura->GetSpellInfo()->StackAmount); - break; } } // AT REMOVE @@ -5373,13 +5066,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; @@ -5475,8 +5166,10 @@ void AuraEffect::HandlePeriodicTriggerSpellAuraTick(Unit* target, Unit* caster) if (caster) { int32 heal = caster->CountPctFromMaxHealth(10); - caster->HealBySpell(target, auraSpellInfo, heal); + HealInfo healInfo(caster, target, heal, auraSpellInfo, auraSpellInfo->GetSchoolMask()); + caster->HealBySpell(healInfo); + /// @todo: should proc other auras? if (int32 mana = caster->GetMaxPower(POWER_MANA)) { mana /= 10; @@ -5774,8 +5467,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. @@ -5794,7 +5485,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const damage = std::max(int32(damage * GetDonePct()), 0); if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetSpellInfo()->Id, SPELLMOD_DOT, damage); + modOwner->ApplySpellMod<SPELLMOD_DOT>(GetSpellInfo()->Id, damage); damage = target->SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount()); @@ -5852,12 +5543,14 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const damage = uint32(target->CountPctFromMaxHealth(damage)); if (!m_spellInfo->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) + { if (m_spellInfo->Effects[m_effIndex].IsTargetingArea() || isAreaAura) { damage = int32(float(damage) * target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE, m_spellInfo->SchoolMask)); if (caster->GetTypeId() != TYPEID_PLAYER) damage = int32(float(damage) * target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CREATURE_AOE_DAMAGE_AVOIDANCE, m_spellInfo->SchoolMask)); } + } bool crit = false; @@ -5869,11 +5562,15 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const 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); @@ -5882,10 +5579,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 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_DOT; - 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) @@ -5894,7 +5593,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const SpellPeriodicAuraLogInfo pInfo(this, damage, overkill, absorb, resist, 0.0f, crit); target->SendPeriodicAuraLog(&pInfo); - caster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo()); + 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); } @@ -5914,8 +5613,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); @@ -5930,6 +5627,9 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c else damage = std::max(int32(damage * GetDonePct()), 0); + if (Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod<SPELLMOD_DOT>(GetSpellInfo()->Id, damage); + damage = target->SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount()); // Calculate armor mitigation @@ -5941,12 +5641,14 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c } if (!m_spellInfo->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) + { if (m_spellInfo->Effects[m_effIndex].IsTargetingArea() || isAreaAura) { damage = uint32(float(damage) * target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE, m_spellInfo->SchoolMask)); if (caster->GetTypeId() != TYPEID_PLAYER) damage = uint32(float(damage) * target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CREATURE_AOE_DAMAGE_AVOIDANCE, m_spellInfo->SchoolMask)); } + } bool crit = false; @@ -5958,25 +5660,34 @@ 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 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_DOT; - 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()) - caster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo()); + 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()) { @@ -5985,8 +5696,11 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c uint32 heal = uint32(caster->SpellHealingBonusDone(caster, GetSpellInfo(), uint32(new_damage * gainMultiplier), DOT, GetBase()->GetStackAmount())); heal = uint32(caster->SpellHealingBonusTaken(caster, GetSpellInfo(), heal, DOT, GetBase()->GetStackAmount())); - int32 gain = caster->HealBySpell(caster, GetSpellInfo(), heal); - caster->getHostileRefManager().threatAssist(caster, gain * 0.5f, GetSpellInfo()); + HealInfo healInfo(caster, caster, heal, GetSpellInfo(), GetSpellInfo()->GetSchoolMask()); + caster->HealBySpell(healInfo); + + caster->getHostileRefManager().threatAssist(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo()); + caster->ProcSkillsAndAuras(caster, PROC_FLAG_DONE_PERIODIC, PROC_FLAG_TAKEN_PERIODIC, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_NONE, hitMask, nullptr, nullptr, &healInfo); } } @@ -6015,7 +5729,9 @@ void AuraEffect::HandlePeriodicHealthFunnelAuraTick(Unit* target, Unit* caster) damage = int32(damage * gainMultiplier); - caster->HealBySpell(target, GetSpellInfo(), damage); + HealInfo healInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask()); + caster->HealBySpell(healInfo); + caster->ProcSkillsAndAuras(target, PROC_FLAG_DONE_PERIODIC, PROC_FLAG_TAKEN_PERIODIC, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_NONE, PROC_HIT_NORMAL, nullptr, nullptr, &healInfo); } void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const @@ -6097,7 +5813,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const damage = std::max(int32(damage * GetDonePct()), 0); if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetSpellInfo()->Id, SPELLMOD_DOT, damage); + modOwner->ApplySpellMod<SPELLMOD_DOT>(GetSpellInfo()->Id, damage); damage = target->SpellHealingBonusTaken(caster, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount()); } @@ -6113,15 +5829,16 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s heal of %s for %u health inflicted by %u", GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), damage, GetId()); - uint32 absorb = 0; uint32 heal = damage; - caster->CalcHealAbsorb(target, GetSpellInfo(), heal, absorb); - int32 gain = caster->DealHeal(target, heal); - SpellPeriodicAuraLogInfo pInfo(this, heal, heal - gain, absorb, 0, 0.0f, crit); + HealInfo healInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask()); + caster->CalcHealAbsorb(healInfo); + caster->DealHeal(healInfo); + + SpellPeriodicAuraLogInfo pInfo(this, heal, heal - healInfo.GetEffectiveHeal(), healInfo.GetAbsorb(), 0, 0.0f, crit); target->SendPeriodicAuraLog(&pInfo); - target->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f, GetSpellInfo()); + target->getHostileRefManager().threatAssist(caster, float(healInfo.GetEffectiveHeal()) * 0.5f, GetSpellInfo()); bool haveCastItem = !GetBase()->GetCastItemGUID().IsEmpty(); @@ -6131,8 +5848,8 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const { uint32 funnelDamage = GetSpellInfo()->ManaPerSecond; // damage is not affected by spell power - if ((int32)funnelDamage > gain && gain > 0) - funnelDamage = gain; + if (funnelDamage > healInfo.GetEffectiveHeal() && healInfo.GetEffectiveHeal()) + funnelDamage = healInfo.GetEffectiveHeal(); uint32 funnelAbsorb = 0; caster->DealDamageMods(caster, funnelDamage, &funnelAbsorb); @@ -6144,10 +5861,10 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; - uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_HOT; + uint32 hitMask = crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL; // ignore item heals if (!haveCastItem) - caster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo()); + caster->ProcSkillsAndAuras(target, procAttacker, procVictim, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_NONE, hitMask, nullptr, nullptr, &healInfo); } void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) const @@ -6326,15 +6043,30 @@ void AuraEffect::HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) con // Set trigger flag uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; - uint32 procEx = createProcExtendMask(&damageInfo, SPELL_MISS_NONE) | PROC_EX_INTERNAL_DOT; + uint32 hitMask = createProcHitMask(&damageInfo, SPELL_MISS_NONE); + uint32 spellTypeMask = PROC_SPELL_TYPE_NO_DMG_HEAL; if (damageInfo.damage) + { procVictim |= PROC_FLAG_TAKEN_DAMAGE; + spellTypeMask |= PROC_SPELL_TYPE_DAMAGE; + } - caster->ProcDamageAndSpell(damageInfo.target, procAttacker, procVictim, procEx, damageInfo.damage, BASE_ATTACK, spellProto); + DamageInfo dotDamageInfo(damageInfo, DOT, BASE_ATTACK, hitMask); + caster->ProcSkillsAndAuras(target, procAttacker, procVictim, spellTypeMask, PROC_SPELL_PHASE_NONE, hitMask, nullptr, &dotDamageInfo, nullptr); caster->DealSpellDamage(&damageInfo, true); } +void AuraEffect::HandleBreakableCCAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) +{ + int32 const damageLeft = GetAmount() - static_cast<int32>(eventInfo.GetDamageInfo()->GetDamage()); + + if (damageLeft <= 0) + aurApp->GetTarget()->RemoveAura(aurApp); + else + SetAmount(damageLeft); +} + void AuraEffect::HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) { Unit* triggerCaster = aurApp->GetTarget(); @@ -6347,7 +6079,7 @@ void AuraEffect::HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEve triggerCaster->CastSpell(triggerTarget, triggeredSpellInfo, true, NULL, this); } else - TC_LOG_DEBUG("spells","AuraEffect::HandleProcTriggerSpellAuraProc: Could not trigger spell %u from aura %u proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId()); + TC_LOG_ERROR("spells","AuraEffect::HandleProcTriggerSpellAuraProc: Could not trigger spell %u from aura %u proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId()); } void AuraEffect::HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) @@ -6363,7 +6095,7 @@ void AuraEffect::HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp triggerCaster->CastCustomSpell(triggerTarget, triggerSpellId, &basepoints0, NULL, NULL, true, NULL, this); } else - TC_LOG_DEBUG("spells","AuraEffect::HandleProcTriggerSpellWithValueAuraProc: Could not trigger spell %u from aura %u proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId()); + TC_LOG_ERROR("spells","AuraEffect::HandleProcTriggerSpellWithValueAuraProc: Could not trigger spell %u from aura %u proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId()); } void AuraEffect::HandleProcTriggerDamageAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) @@ -6373,6 +6105,12 @@ void AuraEffect::HandleProcTriggerDamageAuraProc(AuraApplication* aurApp, ProcEv Unit* target = aurApp->GetTarget(); Unit* triggerTarget = eventInfo.GetProcTarget(); + if (triggerTarget->HasUnitState(UNIT_STATE_ISOLATED) || triggerTarget->IsImmunedToDamage(GetSpellInfo())) + { + SendTickImmune(triggerTarget, target); + return; + } + SpellNonMeleeDamage damageInfo(target, triggerTarget, GetId(), GetSpellInfo()->SchoolMask); uint32 damage = target->SpellDamageBonusDone(triggerTarget, GetSpellInfo(), GetAmount(), SPELL_DIRECT_DAMAGE); damage = triggerTarget->SpellDamageBonusTaken(target, GetSpellInfo(), damage, SPELL_DIRECT_DAMAGE); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h index 32a7b97fff2..05bfe7c0534 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.h +++ b/src/server/game/Spells/Auras/SpellAuraEffects.h @@ -29,8 +29,8 @@ typedef void(AuraEffect::*pAuraEffectHandler)(AuraApplication const* aurApp, uin class TC_GAME_API AuraEffect { - friend void Aura::_InitEffects(uint8 effMask, Unit* caster, int32 *baseAmount); - friend Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 effMask, Unit* caster, int32* baseAmount, Item* castItem, ObjectGuid casterGUID); + friend void Aura::_InitEffects(uint8 effMask, Unit* caster, int32* baseAmount); + friend Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 effMask, Unit* caster, int32* baseAmount, Item* castItem, ObjectGuid casterGUID, bool resetPeriodicTimer); friend Aura::~Aura(); private: ~AuraEffect(); @@ -41,7 +41,6 @@ class TC_GAME_API AuraEffect Aura* GetBase() const { return m_base; } void GetTargetList(std::list<Unit*> & targetList) const; void GetApplicationList(std::list<AuraApplication*> & applicationList) const; - SpellModifier* GetSpellModifier() const { return m_spellmod; } SpellInfo const* GetSpellInfo() const { return m_spellInfo; } uint32 GetId() const { return m_spellInfo->Id; } @@ -59,7 +58,7 @@ class TC_GAME_API AuraEffect void SetPeriodicTimer(int32 periodicTimer) { m_periodicTimer = periodicTimer; } int32 CalculateAmount(Unit* caster); - void CalculatePeriodic(Unit* caster, bool create = false, bool load = false); + void CalculatePeriodic(Unit* caster, bool resetPeriodicTimer = true, bool load = false); void CalculateSpellMod(); void ChangeAmount(int32 newAmount, bool mark = true, bool onStackOrReapply = false); void RecalculateAmount() { if (!CanBeRecalculated()) return; ChangeAmount(CalculateAmount(GetCaster()), false); } @@ -90,8 +89,9 @@ class TC_GAME_API AuraEffect bool HasSpellClassMask() const { return m_spellInfo->Effects[m_effIndex].SpellClassMask; } void SendTickImmune(Unit* target, Unit* caster) const; - void PeriodicTick(AuraApplication * aurApp, Unit* caster) const; + void PeriodicTick(AuraApplication* aurApp, Unit* caster) const; + bool CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) const; void HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); void CleanupTriggeredSpells(Unit* target); @@ -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; @@ -305,6 +305,7 @@ class TC_GAME_API AuraEffect void HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) const; // aura effect proc handlers + void HandleBreakableCCAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); void HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); void HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); void HandleProcTriggerDamageAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 23ebc741b37..45ea44f1dfb 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -221,7 +221,7 @@ void AuraApplication::ClientUpdate(bool remove) _target->SendMessageToSet(&data, true); } -uint8 Aura::BuildEffectMaskForOwner(SpellInfo const* spellProto, uint8 avalibleEffectMask, WorldObject* owner) +uint8 Aura::BuildEffectMaskForOwner(SpellInfo const* spellProto, uint8 availableEffectMask, WorldObject* owner) { ASSERT(spellProto); ASSERT(owner); @@ -230,26 +230,28 @@ uint8 Aura::BuildEffectMaskForOwner(SpellInfo const* spellProto, uint8 avalibleE { case TYPEID_UNIT: case TYPEID_PLAYER: - for (uint8 i = 0; i< MAX_SPELL_EFFECTS; ++i) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { if (spellProto->Effects[i].IsUnitOwnedAuraEffect()) effMask |= 1 << i; } break; case TYPEID_DYNAMICOBJECT: - for (uint8 i = 0; i< MAX_SPELL_EFFECTS; ++i) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { if (spellProto->Effects[i].Effect == SPELL_EFFECT_PERSISTENT_AREA_AURA) effMask |= 1 << i; } break; default: + ABORT(); break; } - return effMask & avalibleEffectMask; + + return effMask & availableEffectMask; } -Aura* Aura::TryRefreshStackOrCreate(SpellInfo const* spellproto, uint8 tryEffMask, WorldObject* owner, Unit* caster, int32* baseAmount /*= NULL*/, Item* castItem /*= NULL*/, ObjectGuid casterGUID /*= ObjectGuid::Empty*/, bool* refresh /*= NULL*/) +Aura* Aura::TryRefreshStackOrCreate(SpellInfo const* spellproto, uint8 tryEffMask, WorldObject* owner, Unit* caster, int32* baseAmount /*= nullptr*/, Item* castItem /*= nullptr*/, ObjectGuid casterGUID /*= ObjectGuid::Empty*/, bool* refresh /*= nullptr*/, bool resetPeriodicTimer /*= true*/) { ASSERT(spellproto); ASSERT(owner); @@ -257,25 +259,28 @@ Aura* Aura::TryRefreshStackOrCreate(SpellInfo const* spellproto, uint8 tryEffMas ASSERT(tryEffMask <= MAX_EFFECT_MASK); if (refresh) *refresh = false; + uint8 effMask = Aura::BuildEffectMaskForOwner(spellproto, tryEffMask, owner); if (!effMask) - return NULL; - if (Aura* foundAura = owner->ToUnit()->_TryStackingOrRefreshingExistingAura(spellproto, effMask, caster, baseAmount, castItem, casterGUID)) + return nullptr; + + if (Aura* foundAura = owner->ToUnit()->_TryStackingOrRefreshingExistingAura(spellproto, effMask, caster, baseAmount, castItem, casterGUID, resetPeriodicTimer)) { // we've here aura, which script triggered removal after modding stack amount // check the state here, so we won't create new Aura object if (foundAura->IsRemoved()) - return NULL; + return nullptr; if (refresh) *refresh = true; + return foundAura; } else return Create(spellproto, effMask, owner, caster, baseAmount, castItem, casterGUID); } -Aura* Aura::TryCreate(SpellInfo const* spellproto, uint8 tryEffMask, WorldObject* owner, Unit* caster, int32* baseAmount /*= NULL*/, Item* castItem /*= NULL*/, ObjectGuid casterGUID /*= ObjectGuid::Empty*/) +Aura* Aura::TryCreate(SpellInfo const* spellproto, uint8 tryEffMask, WorldObject* owner, Unit* caster, int32* baseAmount /*= nullptr*/, Item* castItem /*= nullptr*/, ObjectGuid casterGUID /*= ObjectGuid::Empty*/) { ASSERT(spellproto); ASSERT(owner); @@ -283,7 +288,8 @@ Aura* Aura::TryCreate(SpellInfo const* spellproto, uint8 tryEffMask, WorldObject ASSERT(tryEffMask <= MAX_EFFECT_MASK); uint8 effMask = Aura::BuildEffectMaskForOwner(spellproto, tryEffMask, owner); if (!effMask) - return NULL; + return nullptr; + return Create(spellproto, effMask, owner, caster, baseAmount, castItem, casterGUID); } @@ -310,9 +316,9 @@ Aura* Aura::Create(SpellInfo const* spellproto, uint8 effMask, WorldObject* owne if (!owner->IsInWorld() || ((Unit*)owner)->IsDuringRemoveFromWorld()) // owner not in world so don't allow to own not self cast single target auras if (casterGUID != owner->GetGUID() && spellproto->IsSingleTarget()) - return NULL; + return nullptr; - Aura* aura = NULL; + Aura* aura = nullptr; switch (owner->GetTypeId()) { case TYPEID_UNIT: @@ -324,11 +330,12 @@ Aura* Aura::Create(SpellInfo const* spellproto, uint8 effMask, WorldObject* owne break; default: ABORT(); - return NULL; + return nullptr; } + // aura can be removed in Unit::_AddAura call if (aura->IsRemoved()) - return NULL; + return nullptr; return aura; } @@ -353,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; @@ -374,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 @@ -483,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 @@ -523,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 @@ -577,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 @@ -595,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)); @@ -730,7 +734,7 @@ int32 Aura::CalcMaxDuration(Unit* caster) const // IsPermanent() checks max duration (which we are supposed to calculate here) if (maxDuration != -1 && modOwner) - modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, maxDuration); + modOwner->ApplySpellMod<SPELLMOD_DURATION>(GetId(), maxDuration); return maxDuration; } @@ -740,7 +744,7 @@ void Aura::SetDuration(int32 duration, bool withMods) if (withMods) if (Unit* caster = GetCaster()) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, duration); + modOwner->ApplySpellMod<SPELLMOD_DURATION>(GetId(), duration); m_duration = duration; SetNeedClientUpdateForTargets(); @@ -766,14 +770,15 @@ void Aura::RefreshDuration(bool withMods) m_timeCla = 1 * IN_MILLISECONDS; } -void Aura::RefreshTimers() +void Aura::RefreshTimers(bool resetPeriodicTimer) { m_maxDuration = CalcMaxDuration(); RefreshDuration(); + Unit* caster = GetCaster(); for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (HasEffect(i)) - GetEffect(i)->CalculatePeriodic(caster, false, false); + if (AuraEffect* aurEff = m_effects[i]) + aurEff->CalculatePeriodic(caster, resetPeriodicTimer, false); } void Aura::SetCharges(uint8 charges) @@ -794,7 +799,7 @@ uint8 Aura::CalcMaxCharges(Unit* caster) const if (caster) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), SPELLMOD_CHARGES, maxProcCharges); + modOwner->ApplySpellMod<SPELLMOD_CHARGES>(GetId(), maxProcCharges); return maxProcCharges; } @@ -868,7 +873,7 @@ void Aura::SetStackAmount(uint8 stackAmount) SetNeedClientUpdateForTargets(); } -bool Aura::ModStackAmount(int32 num, AuraRemoveMode removeMode) +bool Aura::ModStackAmount(int32 num, AuraRemoveMode removeMode /*= AURA_REMOVE_BY_DEFAULT*/, bool resetPeriodicTimer /*= true*/) { int32 stackAmount = m_stackAmount + num; @@ -896,16 +901,10 @@ bool Aura::ModStackAmount(int32 num, AuraRemoveMode removeMode) if (refresh) { RefreshSpellMods(); - RefreshTimers(); + RefreshTimers(resetPeriodicTimer); // reset charges SetCharges(CalcMaxCharges()); - // FIXME: not a best way to synchronize charges, but works - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (AuraEffect* aurEff = GetEffect(i)) - if (aurEff->GetAuraType() == SPELL_AURA_ADD_FLAT_MODIFIER || aurEff->GetAuraType() == SPELL_AURA_ADD_PCT_MODIFIER) - if (SpellModifier* mod = aurEff->GetSpellModifier()) - mod->charges = GetCharges(); } SetNeedClientUpdateForTargets(); @@ -986,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; } @@ -1037,7 +1040,7 @@ int32 Aura::CalcDispelChance(Unit* auraTarget, bool offensive) const // Apply dispel mod from aura caster if (Unit* caster = GetCaster()) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), SPELLMOD_RESIST_DISPEL_CHANCE, resistChance); + modOwner->ApplySpellMod<SPELLMOD_RESIST_DISPEL_CHANCE>(GetId(), resistChance); // Dispel resistance from target SPELL_AURA_MOD_DISPEL_RESIST // Only affects offensive dispels @@ -1057,14 +1060,16 @@ void Aura::SetLoadedState(int32 maxduration, int32 duration, int32 charges, uint m_stackAmount = stackamount; Unit* caster = GetCaster(); for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (m_effects[i]) + { + if (AuraEffect* aurEff = m_effects[i]) { - m_effects[i]->SetAmount(amount[i]); - m_effects[i]->SetCanBeRecalculated((recalculateMask & (1 << i)) != 0); - m_effects[i]->CalculatePeriodic(caster, false, true); - m_effects[i]->CalculateSpellMod(); - m_effects[i]->RecalculateAmount(caster); + aurEff->SetAmount(amount[i]); + aurEff->SetCanBeRecalculated((recalculateMask & (1 << i)) != 0); + aurEff->CalculatePeriodic(caster, false, true); + aurEff->CalculateSpellMod(); + aurEff->RecalculateAmount(caster); } + } } bool Aura::HasEffectType(AuraType type) const @@ -1094,7 +1099,7 @@ void Aura::HandleAllEffects(AuraApplication * aurApp, uint8 mode, bool apply) m_effects[i]->HandleEffect(aurApp, mode, apply); } -void Aura::GetApplicationList(std::list<AuraApplication*> & applicationList) const +void Aura::GetApplicationList(Unit::AuraApplicationList& applicationList) const { for (Aura::ApplicationMap::const_iterator appIter = m_applications.begin(); appIter != m_applications.end(); ++appIter) { @@ -1278,17 +1283,9 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b } break; case 44544: // Fingers of Frost - { - // See if we already have the indicator aura. If not, create one. - if (Aura* aur = target->GetAura(74396)) - { - // Aura already there. Refresh duration and set original charges - aur->SetCharges(2); - aur->RefreshDuration(); - } - else - target->AddAura(74396, target); - } + // Refresh or add visual aura + target->CastCustomSpell(74396, SPELLVALUE_AURA_STACK, sSpellMgr->AssertSpellInfo(74396)->StackAmount, (Unit*)nullptr, true); + break; default: break; } @@ -1296,20 +1293,27 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b case SPELLFAMILY_PRIEST: if (!caster) break; + // Devouring Plague - if (GetSpellInfo()->SpellFamilyFlags[0] & 0x02000000 && GetEffect(0)) + if (GetSpellInfo()->SpellFamilyFlags[0] & 0x02000000) { + AuraEffect const* devouringPlague = GetEffect(EFFECT_0); + if (!devouringPlague) + break; + // Improved Devouring Plague if (AuraEffect const* aurEff = caster->GetDummyAuraEffect(SPELLFAMILY_PRIEST, 3790, 1)) { - uint32 damage = caster->SpellDamageBonusDone(target, GetSpellInfo(), GetEffect(0)->GetAmount(), DOT); - damage *= caster->SpellDamagePctDone(target, GetSpellInfo(), SPELL_DIRECT_DAMAGE); + int32 damage = (devouringPlague->GetAmount() + devouringPlague->GetBonusAmount()) * devouringPlague->GetDonePct(); + if (Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod<SPELLMOD_DOT>(GetSpellInfo()->Id, damage); + damage = target->SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DOT); - int32 basepoints0 = aurEff->GetAmount() * GetEffect(0)->GetTotalTicks() * int32(damage) / 100; - int32 heal = int32(CalculatePct(basepoints0, 15)); - caster->CastCustomSpell(target, 63675, &basepoints0, NULL, NULL, true, NULL, GetEffect(0)); - caster->CastCustomSpell(caster, 75999, &heal, NULL, NULL, true, NULL, GetEffect(0)); + int32 basepoints0 = CalculatePct(devouringPlague->GetTotalTicks() * static_cast<int32>(damage), aurEff->GetAmount()); + int32 heal = CalculatePct(basepoints0, 15); + caster->CastCustomSpell(63675, SPELLVALUE_BASE_POINT0, basepoints0, target, true, nullptr, devouringPlague); + caster->CastCustomSpell(75999, SPELLVALUE_BASE_POINT0, heal, (Unit*)nullptr, true, nullptr, devouringPlague); } } // Power Word: Shield @@ -1402,10 +1406,6 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b target->CastSpell(target, 32612, true, NULL, GetEffect(1)); target->CombatStop(); break; - case 74396: // Fingers of Frost - // Remove the IGNORE_AURASTATE aura - target->RemoveAurasDueToSpell(44544); - break; default: break; } @@ -1600,6 +1600,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); } @@ -1610,7 +1613,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; } @@ -1868,49 +1876,90 @@ void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInf } SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId()); - ASSERT(procEntry); // cooldowns should be added to the whole aura (see 51698 area aura) AddProcCooldown(now + procEntry->Cooldown); } -bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const +uint8 Aura::GetProcEffectMask(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const { SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId()); // only auras with spell proc entry can trigger proc if (!procEntry) - return false; + return 0; + + // check spell triggering us + if (Spell const* spell = eventInfo.GetProcSpell()) + { + // Do not allow auras to proc from effect triggered from itself + if (spell->IsTriggeredByAura(m_spellInfo)) + return 0; + + // check if aura can proc when spell is triggered (exception for hunter auto shot & wands) + if (spell->IsTriggered() && !(procEntry->AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC) && !(eventInfo.GetTypeMask() & AUTO_ATTACK_PROC_FLAG_MASK)) + if (!GetSpellInfo()->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) + return 0; + } + + // 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 we have charges to proc with - if (IsUsingCharges() && !GetCharges()) - return false; + if (IsUsingCharges()) + { + if (!GetCharges()) + return 0; + + if (procEntry->AttributesMask & PROC_ATTR_REQ_SPELLMOD) + if (Spell const* spell = eventInfo.GetProcSpell()) + if (!spell->m_appliedMods.count(const_cast<Aura*>(this))) + return 0; + } // check proc cooldown if (IsProcOnCooldown(now)) - return false; - - /// @todo - // something about triggered spells triggering, and add extra attack effect + return 0; // do checks against db data - if (!sSpellMgr->CanSpellTriggerProcOnEvent(*procEntry, eventInfo)) - return false; + if (!SpellMgr::CanSpellTriggerProcOnEvent(*procEntry, eventInfo)) + return 0; // do checks using conditions table if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_SPELL_PROC, GetId(), eventInfo.GetActor(), eventInfo.GetActionTarget())) - return false; + return 0; // AuraScript Hook bool check = const_cast<Aura*>(this)->CallScriptCheckProcHandlers(aurApp, eventInfo); if (!check) - return false; + return 0; + + // At least one effect has to pass checks to proc aura + uint8 procEffectMask = aurApp->GetEffectMask(); + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (procEffectMask & (1 << i)) + if ((procEntry->AttributesMask & (PROC_ATTR_DISABLE_EFF_0 << i)) || !GetEffect(i)->CheckEffectProc(aurApp, eventInfo)) + procEffectMask &= ~(1 << i); + + if (!procEffectMask) + return 0; /// @todo // do allow additional requirements for procs // this is needed because this is the last moment in which you can prevent aura charge drop on proc // and possibly a way to prevent default checks (if there're going to be any) + // Aura added by spell can't trigger from self (prevent drop charges/do triggers) + // But except periodic and kill triggers (can triggered from self) + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + if (spellInfo->Id == GetId() && !(eventInfo.GetTypeMask() & (PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_KILL))) + return 0; + // Check if current equipment meets aura requirements // do that only for passive spells /// @todo this needs to be unified for all kinds of auras @@ -1920,12 +1969,12 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI if (GetSpellInfo()->EquippedItemClass == ITEM_CLASS_WEAPON) { if (target->ToPlayer()->IsInFeralForm()) - return false; + return 0; - if (eventInfo.GetDamageInfo()) + if (DamageInfo* damageInfo = eventInfo.GetDamageInfo()) { - WeaponAttackType attType = eventInfo.GetDamageInfo()->GetAttackType(); - Item* item = NULL; + WeaponAttackType attType = damageInfo->GetAttackType(); + Item* item = nullptr; if (attType == BASE_ATTACK) item = target->ToPlayer()->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); else if (attType == OFF_ATTACK) @@ -1933,20 +1982,23 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI else item = target->ToPlayer()->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); - if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetTemplate()->SubClass) & GetSpellInfo()->EquippedItemSubClassMask)) - return false; + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1 << item->GetTemplate()->SubClass) & GetSpellInfo()->EquippedItemSubClassMask)) + return 0; } } else if (GetSpellInfo()->EquippedItemClass == ITEM_CLASS_ARMOR) { // Check if player is wearing shield Item* item = target->ToPlayer()->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1<<item->GetTemplate()->SubClass) & GetSpellInfo()->EquippedItemSubClassMask)) - return false; + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1 << item->GetTemplate()->SubClass) & GetSpellInfo()->EquippedItemSubClassMask)) + return 0; } } - return roll_chance_f(CalcProcChance(*procEntry, eventInfo)); + if (roll_chance_f(CalcProcChance(*procEntry, eventInfo))) + return procEffectMask; + + return 0; } float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const @@ -1964,21 +2016,28 @@ float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& event } // apply chance modifer aura, applies also to ppm chance (see improved judgement of light spell) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), SPELLMOD_CHANCE_OF_SUCCESS, chance); + modOwner->ApplySpellMod<SPELLMOD_CHANCE_OF_SUCCESS>(GetId(), chance); } return chance; } -void Aura::TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) +void Aura::TriggerProcOnEvent(uint8 procEffectMask, AuraApplication* aurApp, ProcEventInfo& eventInfo) { - CallScriptProcHandlers(aurApp, eventInfo); + bool prevented = CallScriptProcHandlers(aurApp, eventInfo); + if (!prevented) + { + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (!(procEffectMask & (1 << i))) + continue; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (aurApp->HasEffect(i)) // OnEffectProc / AfterEffectProc hooks handled in AuraEffect::HandleProc() - GetEffect(i)->HandleProc(aurApp, eventInfo); + if (aurApp->HasEffect(i)) + GetEffect(i)->HandleProc(aurApp, eventInfo); + } - CallScriptAfterProcHandlers(aurApp, eventInfo); + CallScriptAfterProcHandlers(aurApp, eventInfo); + } // Remove aura if we've used last charge to proc if (IsUsingCharges() && !GetCharges()) @@ -1996,30 +2055,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); @@ -2030,10 +2080,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); @@ -2043,10 +2093,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); @@ -2057,10 +2107,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); @@ -2077,10 +2127,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); @@ -2095,10 +2145,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); @@ -2109,10 +2159,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); @@ -2124,10 +2174,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); @@ -2143,10 +2193,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); @@ -2157,10 +2207,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); @@ -2171,10 +2221,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); @@ -2185,10 +2235,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); @@ -2199,10 +2249,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())) @@ -2217,10 +2267,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); @@ -2231,10 +2281,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); @@ -2245,10 +2295,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); @@ -2259,10 +2309,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); @@ -2274,10 +2324,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); @@ -2290,10 +2340,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); @@ -2309,10 +2359,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); @@ -2325,10 +2375,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); @@ -2336,13 +2386,30 @@ void Aura::CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventI } } +bool Aura::CallScriptCheckEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo) +{ + bool result = true; + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + { + (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC, aurApp); + 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); + + (*scritr)->_FinishScriptCall(); + } + + return result; +} + 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); @@ -2357,10 +2424,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); @@ -2403,18 +2470,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); @@ -2426,48 +2492,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; } } } @@ -2490,7 +2556,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(); @@ -2499,28 +2565,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 a147957f258..1533746893a 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -84,13 +84,13 @@ class TC_GAME_API AuraApplication class TC_GAME_API Aura { - friend Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 effMask, Unit* caster, int32 *baseAmount, Item* castItem, ObjectGuid casterGUID); + friend Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 effMask, Unit* caster, int32 *baseAmount, Item* castItem, ObjectGuid casterGUID, bool resetPeriodicTimer); public: typedef std::map<ObjectGuid, AuraApplication*> ApplicationMap; - static uint8 BuildEffectMaskForOwner(SpellInfo const* spellProto, uint8 avalibleEffectMask, WorldObject* owner); - static Aura* TryRefreshStackOrCreate(SpellInfo const* spellproto, uint8 tryEffMask, WorldObject* owner, Unit* caster, int32* baseAmount = NULL, Item* castItem = NULL, ObjectGuid casterGUID = ObjectGuid::Empty, bool* refresh = NULL); - static Aura* TryCreate(SpellInfo const* spellproto, uint8 effMask, WorldObject* owner, Unit* caster, int32 *baseAmount = NULL, Item* castItem = NULL, ObjectGuid casterGUID = ObjectGuid::Empty); + static uint8 BuildEffectMaskForOwner(SpellInfo const* spellProto, uint8 availableEffectMask, WorldObject* owner); + static Aura* TryRefreshStackOrCreate(SpellInfo const* spellproto, uint8 tryEffMask, WorldObject* owner, Unit* caster, int32* baseAmount = nullptr, Item* castItem = nullptr, ObjectGuid casterGUID = ObjectGuid::Empty, bool* refresh = nullptr, bool resetPeriodicTimer = true); + static Aura* TryCreate(SpellInfo const* spellproto, uint8 effMask, WorldObject* owner, Unit* caster, int32* baseAmount = nullptr, Item* castItem = nullptr, ObjectGuid casterGUID = ObjectGuid::Empty); static Aura* Create(SpellInfo const* spellproto, uint8 effMask, WorldObject* owner, Unit* caster, int32* baseAmount, Item* castItem, ObjectGuid casterGUID); explicit Aura(SpellInfo const* spellproto, WorldObject* owner, Unit* caster, Item* castItem, ObjectGuid casterGUID); void _InitEffects(uint8 effMask, Unit* caster, int32 *baseAmount); @@ -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);} @@ -131,7 +131,7 @@ class TC_GAME_API Aura int32 GetDuration() const { return m_duration; } void SetDuration(int32 duration, bool withMods = false); void RefreshDuration(bool withMods = false); - void RefreshTimers(); + void RefreshTimers(bool resetPeriodicTimer); bool IsExpired() const { return !GetDuration() && !m_dropEvent; } bool IsPermanent() const { return GetMaxDuration() == -1; } @@ -146,7 +146,7 @@ class TC_GAME_API Aura uint8 GetStackAmount() const { return m_stackAmount; } void SetStackAmount(uint8 num); - bool ModStackAmount(int32 num, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT); + bool ModStackAmount(int32 num, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT, bool resetPeriodicTimer = true); void RefreshSpellMods(); @@ -199,18 +199,14 @@ class TC_GAME_API Aura bool CheckAreaTarget(Unit* target); bool CanStackWith(Aura const* existingAura) const; - // Proc system - // this subsystem is not yet in use - the core of it is functional, but still some research has to be done - // and some dependant problems fixed before it can replace old proc system (for example cooldown handling) - // currently proc system functionality is implemented in Unit::ProcDamageAndSpell bool IsProcOnCooldown(std::chrono::steady_clock::time_point now) const; void AddProcCooldown(std::chrono::steady_clock::time_point cooldownEnd); bool IsUsingCharges() const { return m_isUsingCharges; } void SetUsingCharges(bool val) { m_isUsingCharges = val; } void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now); - bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const; + uint8 GetProcEffectMask(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const; float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; - void TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo); + void TriggerProcOnEvent(uint8 procEffectMask, AuraApplication* aurApp, ProcEventInfo& eventInfo); // AuraScript void LoadScripts(); @@ -233,6 +229,7 @@ class TC_GAME_API Aura void CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & splitAmount); // Spell Proc Hooks bool CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); + bool CallScriptCheckEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo); bool CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); bool CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); void CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); @@ -241,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; @@ -286,14 +285,14 @@ 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; } DiminishingGroup GetDiminishGroup() const { return m_AuraDRGroup; } private: - DiminishingGroup m_AuraDRGroup:8; // Diminishing + DiminishingGroup m_AuraDRGroup; // Diminishing }; class TC_GAME_API DynObjAura : public Aura @@ -304,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 8332219cff1..c082c0a7584 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -586,13 +586,11 @@ 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; m_procVictim = 0; - m_procEx = 0; + m_hitMask = 0; focusObject = NULL; m_cast_count = 0; m_glyphIndex = 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); @@ -1481,7 +1490,7 @@ void Spell::SelectImplicitChainTargets(SpellEffIndex effIndex, SpellImplicitTarg { uint32 maxTargets = m_spellInfo->Effects[effIndex].ChainTarget; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, maxTargets, this); + modOwner->ApplySpellMod<SPELLMOD_JUMP_TARGETS>(m_spellInfo->Id, maxTargets, this); if (maxTargets > 1) { @@ -1947,12 +1956,11 @@ GameObject* Spell::SearchSpellFocus() return focus; } -void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/) +void Spell::prepareDataForTriggerSystem() { //========================================================================================== // Now fill data for trigger system, need know: - // can spell trigger another or not (m_canTrigger) - // Create base triggers flags for Attacker and Victim (m_procAttacker, m_procVictim and m_procEx) + // Create base triggers flags for Attacker and Victim (m_procAttacker, m_procVictim and m_hitMask) //========================================================================================== m_procVictim = m_procAttacker = 0; @@ -1982,7 +1990,7 @@ void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/) break; default: if (m_spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON && - m_spellInfo->EquippedItemSubClassMask & (1<<ITEM_SUBCLASS_WEAPON_WAND) + m_spellInfo->EquippedItemSubClassMask & (1 << ITEM_SUBCLASS_WEAPON_WAND) && m_spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) // Wands auto attack { m_procAttacker = PROC_FLAG_DONE_RANGED_AUTO_ATTACK; @@ -1991,17 +1999,20 @@ void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/) // For other spells trigger procflags are set in Spell::DoAllEffectOnTarget // Because spell positivity is dependant on target } - m_procEx = PROC_EX_NONE; + m_hitMask = PROC_HIT_NONE; // 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; - /* Effects which are result of aura proc from triggered spell cannot proc - to prevent chain proc of these spells */ + // 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) @@ -2009,20 +2020,6 @@ void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/) m_procAttacker = PROC_FLAG_DONE_PERIODIC; m_procVictim = PROC_FLAG_TAKEN_PERIODIC; } - - // Ranged autorepeat attack is set as triggered spell - ignore it - if (!(m_procAttacker & PROC_FLAG_DONE_RANGED_AUTO_ATTACK)) - { - if (_triggeredCastFlags & TRIGGERED_DISALLOW_PROC_EVENTS && - (m_spellInfo->HasAttribute(SPELL_ATTR2_TRIGGERED_CAN_TRIGGER_PROC) || - m_spellInfo->HasAttribute(SPELL_ATTR3_TRIGGERED_CAN_TRIGGER_PROC_2))) - m_procEx |= PROC_EX_INTERNAL_CANT_PROC; - else if (_triggeredCastFlags & TRIGGERED_DISALLOW_PROC_EVENTS) - m_procEx |= PROC_EX_INTERNAL_TRIGGERED; - } - // Totem casts require spellfamilymask defined in spell_proc_event to proc - if (m_originalCaster && m_caster != m_originalCaster && m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsTotem() && m_caster->IsControlledByPlayer()) - m_procEx |= PROC_EX_INTERNAL_REQ_FAMILY; } void Spell::CleanupTargetList() @@ -2033,6 +2030,32 @@ void Spell::CleanupTargetList() m_delayMoment = 0; } +class ProcReflectDelayed : public BasicEvent +{ + public: + ProcReflectDelayed(Unit* owner, ObjectGuid casterGuid) : _victim(owner), _casterGuid(casterGuid) { } + + bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override + { + Unit* caster = ObjectAccessor::GetUnit(*_victim, _casterGuid); + if (!caster) + return true; + + uint32 const typeMaskActor = PROC_FLAG_NONE; + uint32 const typeMaskActionTarget = PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG; + uint32 const spellTypeMask = PROC_SPELL_TYPE_DAMAGE | PROC_SPELL_TYPE_NO_DMG_HEAL; + uint32 const spellPhaseMask = PROC_SPELL_PHASE_NONE; + uint32 const hitMask = PROC_HIT_REFLECT; + + caster->ProcSkillsAndAuras(_victim, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, nullptr, nullptr, nullptr); + return true; + } + + private: + Unit* _victim; + ObjectGuid _casterGuid; +}; + void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= true*/, bool implicit /*= true*/, Position const* losPosition /*= nullptr*/) { for (uint32 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) @@ -2044,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 @@ -2112,20 +2135,20 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= targetInfo.timeDelay = (uint64)std::floor(dist / m_spellInfo->Speed * 1000.0f); // Calculate minimum incoming time - if (m_delayMoment == 0 || m_delayMoment > targetInfo.timeDelay) + if (!m_delayMoment || m_delayMoment > targetInfo.timeDelay) m_delayMoment = targetInfo.timeDelay; } else - targetInfo.timeDelay = 0LL; + targetInfo.timeDelay = 0ULL; // If target reflect spell back to caster if (targetInfo.missCondition == SPELL_MISS_REFLECT) { // Calculate reflected spell result on caster - targetInfo.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, m_canReflect); + targetInfo.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, false); // can't reflect twice - if (targetInfo.reflectResult == SPELL_MISS_REFLECT) // Impossible reflect again, so simply deflect spell - targetInfo.reflectResult = SPELL_MISS_PARRY; + // Proc spell reflect aura when missile hits the original target + target->m_Events.AddEvent(new ProcReflectDelayed(target, m_originalCasterGUID), target->m_Events.CalculateTime(targetInfo.timeDelay)); // Increase time interval for reflected spells by 1.5 targetInfo.timeDelay += targetInfo.timeDelay >> 1; @@ -2147,14 +2170,14 @@ void Spell::AddGOTarget(GameObject* go, uint32 effectMask) { switch (m_spellInfo->Effects[effIndex].Effect) { - case SPELL_EFFECT_GAMEOBJECT_DAMAGE: - case SPELL_EFFECT_GAMEOBJECT_REPAIR: - case SPELL_EFFECT_GAMEOBJECT_SET_DESTRUCTION_STATE: - if (go->GetGoType() != GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING) - effectMask &= ~(1 << effIndex); - break; - default: - break; + case SPELL_EFFECT_GAMEOBJECT_DAMAGE: + case SPELL_EFFECT_GAMEOBJECT_REPAIR: + case SPELL_EFFECT_GAMEOBJECT_SET_DESTRUCTION_STATE: + if (go->GetGoType() != GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING) + effectMask &= ~(1 << effIndex); + break; + default: + break; } } } @@ -2189,7 +2212,7 @@ void Spell::AddGOTarget(GameObject* go, uint32 effectMask) if (dist < 5.0f) dist = 5.0f; target.timeDelay = uint64(floor(dist / m_spellInfo->Speed * 1000.0f)); - if (m_delayMoment == 0 || m_delayMoment > target.timeDelay) + if (!m_delayMoment || m_delayMoment > target.timeDelay) m_delayMoment = target.timeDelay; } else @@ -2297,13 +2320,13 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) // Fill base trigger info uint32 procAttacker = m_procAttacker; uint32 procVictim = m_procVictim; - uint32 procEx = m_procEx; + uint32 hitMask = m_hitMask; - m_spellAura = NULL; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied + m_spellAura = nullptr; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied - //Spells with this flag cannot trigger if effect is cast on self - bool canEffectTrigger = !m_spellInfo->HasAttribute(SPELL_ATTR3_CANT_TRIGGER_PROC) && unitTarget->CanProc() && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE || missInfo == SPELL_MISS_IMMUNE2); - Unit* spellHitTarget = NULL; + // Spells with this flag cannot trigger if effect is cast on self + bool const canEffectTrigger = !m_spellInfo->HasAttribute(SPELL_ATTR3_CANT_TRIGGER_PROC) && unitTarget->CanProc() && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE || missInfo == SPELL_MISS_IMMUNE2); + Unit* spellHitTarget = nullptr; if (missInfo == SPELL_MISS_NONE) // In case spell hit target, do all effect on that target spellHitTarget = unit; @@ -2331,30 +2354,34 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) if (missInfo2 != SPELL_MISS_MISS) m_caster->SendSpellMiss(unit, m_spellInfo->Id, missInfo2); m_damage = 0; - spellHitTarget = NULL; + spellHitTarget = nullptr; } } // Do not take combo points on dodge and miss - if (missInfo != SPELL_MISS_NONE && m_needComboPoints && - m_targets.GetUnitTargetGUID() == target->targetGUID) - { + if (missInfo != SPELL_MISS_NONE && m_needComboPoints && m_targets.GetUnitTargetGUID() == target->targetGUID) m_needComboPoints = false; - // Restore spell mods for a miss/dodge/parry Cold Blood - /// @todo check how broad this rule should be - if (m_caster->GetTypeId() == TYPEID_PLAYER && (missInfo == SPELL_MISS_MISS || - missInfo == SPELL_MISS_DODGE || missInfo == SPELL_MISS_PARRY)) - m_caster->ToPlayer()->RestoreSpellMods(this, 14177); - } - // Trigger info was not filled in spell::preparedatafortriggersystem - we do it now + // Trigger info was not filled in Spell::prepareDataForTriggerSystem - we do it now if (canEffectTrigger && !procAttacker && !procVictim) { bool positive = true; 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) { @@ -2394,19 +2421,20 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) uint32 addhealth = m_healing; if (crit) { - procEx |= PROC_EX_CRITICAL_HIT; - addhealth = caster->SpellCriticalHealingBonus(m_spellInfo, addhealth, NULL); + hitMask |= PROC_HIT_CRITICAL; + addhealth = caster->SpellCriticalHealingBonus(m_spellInfo, addhealth, nullptr); } else - procEx |= PROC_EX_NORMAL_HIT; + hitMask |= PROC_HIT_NORMAL; - int32 gain = caster->HealBySpell(unitTarget, m_spellInfo, addhealth, crit); - unitTarget->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f, m_spellInfo); - m_healing = gain; + HealInfo healInfo(caster, unitTarget, addhealth, m_spellInfo, m_spellInfo->GetSchoolMask()); + caster->HealBySpell(healInfo, crit); + unitTarget->getHostileRefManager().threatAssist(caster, float(healInfo.GetEffectiveHeal()) * 0.5f, m_spellInfo); + m_healing = healInfo.GetEffectiveHeal(); - // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) - if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT) - caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, addhealth, m_attackType, m_spellInfo, m_triggeredByAuraSpell); + // Do triggers for unit + if (canEffectTrigger) + caster->ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_HIT, hitMask, this, nullptr, &healInfo); } // Do damage and triggers else if (m_damage > 0) @@ -2414,38 +2442,57 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) // Fill base damage struct (unitTarget - is real spell target) SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask); - // Add bonuses and fill damageInfo struct - caster->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit); - caster->DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); + // Check damage immunity + if (unitTarget->IsImmunedToDamage(m_spellInfo)) + { + hitMask = PROC_HIT_IMMUNE; + m_damage = 0; - // Send log damage message to client - caster->SendSpellNonMeleeDamageLog(&damageInfo); + // no packet found in sniffs + } + else + { + // Add bonuses and fill damageInfo struct + caster->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit); + caster->DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); - procEx |= createProcExtendMask(&damageInfo, missInfo); - procVictim |= PROC_FLAG_TAKEN_DAMAGE; + // Send log damage message to client + caster->SendSpellNonMeleeDamageLog(&damageInfo); - // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) - if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT) - { - caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell); - if (caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET) == 0 && - (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) - caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, procEx); + hitMask |= createProcHitMask(&damageInfo, missInfo); + procVictim |= PROC_FLAG_TAKEN_DAMAGE; + + m_damage = damageInfo.damage; + caster->DealSpellDamage(&damageInfo, true); } - m_damage = damageInfo.damage; + // Do triggers for unit + if (canEffectTrigger) + { + DamageInfo spellDamageInfo(damageInfo, SPELL_DIRECT_DAMAGE, m_attackType, hitMask); + caster->ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_HIT, hitMask, this, &spellDamageInfo, nullptr); - caster->DealSpellDamage(&damageInfo, true); + if (caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET) && + (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) + caster->ToPlayer()->CastItemCombatSpell(spellDamageInfo); + } } // Passive spell hits/misses or active spells only misses (only triggers) else { // Fill base damage struct (unitTarget - is real spell target) SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask); - procEx |= createProcExtendMask(&damageInfo, missInfo); - // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) - if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT) - caster->ProcDamageAndSpell(unit, procAttacker, procVictim, procEx, 0, m_attackType, m_spellInfo, m_triggeredByAuraSpell); + hitMask |= createProcHitMask(&damageInfo, missInfo); + // Do triggers for unit + if (canEffectTrigger) + { + DamageInfo spellNoDamageInfo(damageInfo, NODAMAGE, m_attackType, hitMask); + caster->ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, PROC_SPELL_TYPE_NO_DMG_HEAL, PROC_SPELL_PHASE_HIT, hitMask, this, &spellNoDamageInfo, nullptr); + + if (caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET) && + (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) + caster->ToPlayer()->CastItemCombatSpell(spellNoDamageInfo); + } // Failed Pickpocket, reveal rogue if (missInfo == SPELL_MISS_RESIST && m_spellInfo->HasAttribute(SPELL_ATTR0_CU_PICKPOCKET) && unitTarget->GetTypeId() == TYPEID_UNIT) @@ -2494,7 +2541,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA return SPELL_MISS_EVADE; // For delayed spells immunity may be applied between missile launch and hit - check immunity for that case - if (m_spellInfo->Speed && (unit->IsImmunedToDamage(m_spellInfo) || unit->IsImmunedToSpell(m_spellInfo))) + if (m_spellInfo->Speed && unit->IsImmunedToSpell(m_spellInfo)) return SPELL_MISS_IMMUNE; // disable effects to which unit is immune @@ -2507,15 +2554,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; + } } } } @@ -2546,12 +2590,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 @@ -2581,16 +2620,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) @@ -2617,8 +2659,9 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA if (m_originalCaster) { bool refresh = false; + bool const resetPeriodicTimer = !(_triggeredCastFlags & TRIGGERED_DONT_RESET_PERIODIC_TIMER); m_spellAura = Aura::TryRefreshStackOrCreate(aurSpellInfo, effectMask, unit, - m_originalCaster, (aurSpellInfo == m_spellInfo) ? &m_spellValue->EffectBasePoints[0] : &basePoints[0], m_CastItem, ObjectGuid::Empty, &refresh); + m_originalCaster, (aurSpellInfo == m_spellInfo) ? &m_spellValue->EffectBasePoints[0] : &basePoints[0], m_CastItem, ObjectGuid::Empty, &refresh, resetPeriodicTimer); if (m_spellAura) { // Set aura stack amount to desired value @@ -2632,8 +2675,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) @@ -2648,7 +2690,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())) @@ -2712,8 +2754,8 @@ void Spell::DoTriggersOnSpellHit(Unit* unit, uint8 effMask) // info confirmed with retail sniffs of permafrost and shadow weaving if (!m_hitTriggerSpells.empty()) { - int _duration = 0; - for (HitTriggerSpellList::const_iterator i = m_hitTriggerSpells.begin(); i != m_hitTriggerSpells.end(); ++i) + int32 _duration = 0; + for (auto i = m_hitTriggerSpells.begin(); i != m_hitTriggerSpells.end(); ++i) { if (CanExecuteTriggersOnHit(effMask, i->triggeredByAura) && roll_chance_i(i->chance)) { @@ -2815,7 +2857,7 @@ bool Spell::UpdateChanneledTargetList() { range = m_spellInfo->GetMaxRange(m_spellInfo->IsPositive()); if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); + modOwner->ApplySpellMod<SPELLMOD_RANGE>(m_spellInfo->Id, range, this); } for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) @@ -2925,7 +2967,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 @@ -2946,14 +2989,17 @@ 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; } // Prepare data for triggers - prepareDataForTriggerSystem(triggeredByAura); + prepareDataForTriggerSystem(); if (Player* player = m_caster->ToPlayer()) { @@ -2971,7 +3017,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()); @@ -2984,8 +3030,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); @@ -3061,10 +3107,6 @@ void Spell::cancel() SendInterrupted(0); SendCastResult(SPELL_FAILED_INTERRUPTED); - // spell is canceled-take mods and clear list - if (m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->ToPlayer()->RemoveSpellMods(this); - m_appliedMods.clear(); break; @@ -3137,10 +3179,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) @@ -3299,6 +3342,25 @@ void Spell::cast(bool skipCheck) if (Creature* creatureCaster = m_caster->ToCreature()) creatureCaster->ReleaseFocus(this); + + if (!m_originalCaster) + return; + + // Handle procs on cast + uint32 procAttacker = m_procAttacker; + if (!procAttacker) + { + if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC) + procAttacker = m_spellInfo->IsPositive() ? PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG; + else + procAttacker = m_spellInfo->IsPositive() ? PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG; + } + + uint32 hitMask = m_hitMask; + if (!(hitMask & PROC_HIT_CRITICAL)) + hitMask |= PROC_HIT_NORMAL; + + m_originalCaster->ProcSkillsAndAuras(nullptr, procAttacker, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_CAST, hitMask, this, nullptr, nullptr); } void Spell::handle_immediate() @@ -3312,7 +3374,7 @@ void Spell::handle_immediate() // First mod_duration then haste - see Missile Barrage // Apply duration mod if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); + modOwner->ApplySpellMod<SPELLMOD_DURATION>(m_spellInfo->Id, duration); // Apply haste mods m_caster->ModSpellDurationTime(m_spellInfo, duration, this); @@ -3434,9 +3496,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(); @@ -3455,22 +3514,8 @@ void Spell::_handle_immediate_phase() } // process items - for (std::list<ItemTargetInfo>::iterator ihit= m_UniqueItemInfo.begin(); ihit != m_UniqueItemInfo.end(); ++ihit) + for (std::list<ItemTargetInfo>::iterator ihit = m_UniqueItemInfo.begin(); ihit != m_UniqueItemInfo.end(); ++ihit) DoAllEffectOnTarget(&(*ihit)); - - if (!m_originalCaster) - return; - // Handle procs on cast - /// @todo finish new proc system:P - if (m_UniqueTargetInfo.empty() && m_targets.HasDst()) - { - uint32 procAttacker = m_procAttacker; - if (!procAttacker) - procAttacker |= PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS; - - // Proc the spells that have DEST target - m_originalCaster->ProcDamageAndSpell(NULL, procAttacker, 0, m_procEx | PROC_EX_NORMAL_HIT, 0, BASE_ATTACK, m_spellInfo, m_triggeredByAuraSpell); - } } void Spell::_handle_finish_phase() @@ -3494,7 +3539,24 @@ void Spell::_handle_finish_phase() m_caster->m_extraAttacks = 0; } - /// @todo trigger proc phase finish here + // Handle procs on finish + if (!m_originalCaster) + return; + + uint32 procAttacker = m_procAttacker; + if (!procAttacker) + { + if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC) + procAttacker = m_spellInfo->IsPositive() ? PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG; + else + procAttacker = m_spellInfo->IsPositive() ? PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG; + } + + uint32 hitMask = m_hitMask; + if (!(hitMask & PROC_HIT_CRITICAL)) + hitMask |= PROC_HIT_NORMAL; + + m_originalCaster->ProcSkillsAndAuras(nullptr, procAttacker, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_FINISH, hitMask, this, nullptr, nullptr); } void Spell::SendSpellCooldown() @@ -3525,7 +3587,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 @@ -3547,7 +3609,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; @@ -3560,8 +3622,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) @@ -3641,6 +3707,7 @@ void Spell::finish(bool ok) break; } } + if (!found && !m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS)) { m_caster->resetAttackTimer(BASE_ATTACK); @@ -3657,24 +3724,12 @@ void Spell::finish(bool ok) m_caster->ToPlayer()->UpdatePotionCooldown(this); } - if (Player* modOwner = m_caster->GetSpellModOwner()) - { - // triggered spell pointer can be not set in some cases - // this is needed for proper apply of triggered spell mods - modOwner->SetSpellModTakingSpell(this, true); - - // Take mods after trigger spell (needed for 14177 to affect 48664) - // mods are taken only on succesfull cast and independantly from targets of the spell - modOwner->RemoveSpellMods(this); - modOwner->SetSpellModTakingSpell(this, false); - } - // Stop Attack for some spells if (m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET)) 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); @@ -3682,115 +3737,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; @@ -3798,13 +3923,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) @@ -4386,7 +4511,7 @@ void Spell::TakePower() hit = false; //lower spell cost on fail (by talent aura) if (Player* modOwner = m_caster->ToPlayer()->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost); + modOwner->ApplySpellMod<SPELLMOD_SPELL_COST_REFUND_ON_FAIL>(m_spellInfo->Id, m_powerCost); } break; } @@ -4453,7 +4578,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; @@ -4478,7 +4603,7 @@ SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) { runeCost[i] = src->RuneCost[i]; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, runeCost[i], this); + modOwner->ApplySpellMod<SPELLMOD_COST>(m_spellInfo->Id, runeCost[i], const_cast<Spell*>(this)); } runeCost[RUNE_DEATH] = MAX_RUNES; // calculated later @@ -4518,7 +4643,7 @@ void Spell::TakeRunePower(bool didHit) { runeCost[i] = runeCostData->RuneCost[i]; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, runeCost[i], this); + modOwner->ApplySpellMod<SPELLMOD_COST>(m_spellInfo->Id, runeCost[i], this); } // Let's say we use a skill that requires a Frost rune. This is the order: @@ -4598,7 +4723,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(); @@ -4677,7 +4802,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 @@ -4688,7 +4813,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) @@ -4714,10 +4839,10 @@ 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->HasAttribute(SPELL_ATTR0_PASSIVE) && !(m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell))) + if (!m_caster->IsAlive() && !m_spellInfo->IsPassive() && !(m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell))) return SPELL_FAILED_CASTER_DEAD; // check cooldowns to prevent cheating @@ -4777,13 +4902,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 @@ -4876,17 +5003,21 @@ SpellCastResult Spell::CheckCast(bool strict) // those spells may have incorrect target entries or not filled at all (for example 15332) // such spells when learned are not targeting anyone using targeting system, they should apply directly to caster instead // also, such casts shouldn't be sent to client - if (!(m_spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE) && (!m_targets.GetUnitTarget() || m_targets.GetUnitTarget() == m_caster))) + 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; @@ -4987,7 +5118,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; } @@ -5007,7 +5138,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURAS)) { - castResult = CheckCasterAuras(); + castResult = CheckCasterAuras(param1); if (castResult != SPELL_CAST_OK) return castResult; } @@ -5021,6 +5152,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)) @@ -5036,6 +5168,7 @@ SpellCastResult Spell::CheckCast(bool strict) hasNonDispelEffect = true; break; } + } if (!hasNonDispelEffect && !hasDispellableAura && dispelMask && !IsTriggered()) { @@ -5150,7 +5283,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()) @@ -5619,132 +5752,114 @@ 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 only mechanic stun auras, another stun types must prevent cast spell - if (usableInStun) - { - 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) - { - if ((*i)->GetSpellInfo()->GetAllEffectsMechanicMask() && !((*i)->GetSpellInfo()->GetAllEffectsMechanicMask() & (1<<MECHANIC_STUN))) - { - foundNotStun = true; - break; - } - } - if (foundNotStun && m_spellInfo->Id != 22812) - 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()) + { + if (Unit* charmer = m_caster->GetCharmer()) + if (charmer->GetUnitBeingMoved() != m_caster && 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) @@ -5803,7 +5918,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) @@ -5833,20 +5948,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); @@ -5888,14 +6003,14 @@ std::pair<float, float> Spell::GetMinMaxRange(bool strict) maxRange *= ranged->GetTemplate()->RangedModRange * 0.01f; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, 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) @@ -5931,12 +6046,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) @@ -6012,10 +6130,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 @@ -6027,7 +6146,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 @@ -6065,7 +6184,11 @@ SpellCastResult Spell::CheckItems() } } if (!player->HasItemCount(itemid, itemcount)) + { + if (param1) + *param1 = itemid; return SPELL_FAILED_REAGENTS; + } } } @@ -6149,7 +6272,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); @@ -6269,21 +6392,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; @@ -6292,21 +6423,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; @@ -6457,7 +6596,7 @@ void Spell::Delayed() // only called in DealDamage() //check pushback reduce int32 delaytime = 500; // spellcasting delay is normally 500ms int32 delayReduce = 100; // must be initialized to 100 for percent modifiers - m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); + m_caster->ToPlayer()->ApplySpellMod<SPELLMOD_NOT_LOSE_CASTING_TIME>(m_spellInfo->Id, delayReduce, this); delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -6495,7 +6634,7 @@ void Spell::DelayedChannel() int32 delaytime = CalculatePct(duration, 25); // channeling delay is normally 25% of its time per hit int32 delayReduce = 100; // must be initialized to 100 for percent modifiers - m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); + m_caster->ToPlayer()->ApplySpellMod<SPELLMOD_NOT_LOSE_CASTING_TIME>(m_spellInfo->Id, delayReduce, this); delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -6578,14 +6717,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 @@ -6664,15 +6803,16 @@ 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? - return !IsTriggered() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT); + if (IsTriggered() || !(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT)) + return false; + + if (!m_casttime && m_spellInfo->HasAttribute(SPELL_ATTR6_NOT_RESET_SWING_IF_INSTANT)) + return false; + + return true; } bool Spell::IsNeedSendToClient() const @@ -6850,9 +6990,11 @@ void Spell::HandleLaunchPhase() for (Unit::AuraEffectList::const_iterator j = Auras.begin(); j != Auras.end(); ++j) { if ((*j)->IsAffectedOnSpell(m_spellInfo)) - usesAmmo=false; + usesAmmo = false; } + PrepareTargetProcessing(); + for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) { TargetInfo& target = *ihit; @@ -6886,8 +7028,11 @@ void Spell::HandleLaunchPhase() break; } } + DoAllEffectOnLaunchTarget(target, multiplier); } + + FinishTargetProcessing(); } void Spell::DoAllEffectOnLaunchTarget(TargetInfo& targetInfo, float* multiplier) @@ -7035,7 +7180,7 @@ void Spell::SetSpellValue(SpellValueMod mod, int32 value) void Spell::PrepareTargetProcessing() { - CheckEffectExecuteData(); + AssertEffectExecuteData(); } void Spell::FinishTargetProcessing() @@ -7060,7 +7205,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]); @@ -7068,29 +7213,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); @@ -7100,10 +7236,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); @@ -7113,10 +7249,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); @@ -7127,10 +7263,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); @@ -7145,7 +7281,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(); } @@ -7153,9 +7289,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) { @@ -7199,10 +7335,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); @@ -7212,10 +7348,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); @@ -7225,10 +7361,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); @@ -7238,10 +7374,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); @@ -7251,10 +7387,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); @@ -7265,10 +7401,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); @@ -7279,10 +7415,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); @@ -7297,15 +7433,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))) @@ -7355,25 +7491,24 @@ void Spell::PrepareTriggersExecutedOnHit() // save auras which were present on spell caster on cast, to prevent triggered auras from affecting caster // and to correctly calculate proc chance when combopoints are present Unit::AuraEffectList const& targetTriggers = m_caster->GetAuraEffectsByType(SPELL_AURA_ADD_TARGET_TRIGGER); - for (Unit::AuraEffectList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i) + for (AuraEffect const* aurEff : targetTriggers) { - if (!(*i)->IsAffectedOnSpell(m_spellInfo)) + if (!aurEff->IsAffectedOnSpell(m_spellInfo)) continue; - SpellInfo const* auraSpellInfo = (*i)->GetSpellInfo(); - uint32 auraSpellIdx = (*i)->GetEffIndex(); + + SpellInfo const* auraSpellInfo = aurEff->GetSpellInfo(); + uint32 auraSpellIdx = aurEff->GetEffIndex(); if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(auraSpellInfo->Effects[auraSpellIdx].TriggerSpell)) { // calculate the chance using spell base amount, because aura amount is not updated on combo-points change // this possibly needs fixing - int32 auraBaseAmount = (*i)->GetBaseAmount(); + int32 auraBaseAmount = aurEff->GetBaseAmount(); // proc chance is stored in effect amount - int32 chance = m_caster->CalculateSpellDamage(NULL, auraSpellInfo, auraSpellIdx, &auraBaseAmount); + int32 chance = m_caster->CalculateSpellDamage(nullptr, auraSpellInfo, auraSpellIdx, &auraBaseAmount); + chance *= aurEff->GetBase()->GetStackAmount(); + // build trigger and add to the list - HitTriggerSpell spellTriggerInfo; - spellTriggerInfo.triggeredSpell = spellInfo; - spellTriggerInfo.triggeredByAura = auraSpellInfo; - spellTriggerInfo.chance = chance * (*i)->GetBase()->GetStackAmount(); - m_hitTriggerSpells.push_back(spellTriggerInfo); + m_hitTriggerSpells.emplace_back(spellInfo, auraSpellInfo, chance); } } } @@ -7414,15 +7549,12 @@ void Spell::TriggerGlobalCooldown() if (m_spellInfo->StartRecoveryTime >= MIN_GCD && m_spellInfo->StartRecoveryTime <= MAX_GCD) { // gcd modifier auras are applied only to own spells and only players have such mods - if (m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_GLOBAL_COOLDOWN, gcd, this); + if (Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod<SPELLMOD_GLOBAL_COOLDOWN>(m_spellInfo->Id, gcd, this); // Apply haste rating gcd = int32(float(gcd) * m_caster->GetFloatValue(UNIT_MOD_CAST_SPEED)); - if (gcd < MIN_GCD) - gcd = MIN_GCD; - else if (gcd > MAX_GCD) - gcd = MAX_GCD; + RoundToInterval<int32>(gcd, MIN_GCD, MAX_GCD); } m_caster->GetSpellHistory()->AddGlobalCooldown(m_spellInfo, gcd); diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 016af275141..aa3b6ca358a 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -352,7 +352,7 @@ class TC_GAME_API Spell void EffectCastButtons(SpellEffIndex effIndex); void EffectRechargeManaGem(SpellEffIndex effIndex); - typedef std::set<Aura*> UsedSpellMods; + typedef std::unordered_set<Aura*> UsedSpellMods; Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID = ObjectGuid::Empty, bool skipCheck = false); ~Spell(); @@ -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,12 +481,14 @@ 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; + bool IsTriggeredByAura(SpellInfo const* auraSpellInfo) const { return (auraSpellInfo == m_triggeredByAuraSpell); } + bool IsDeletable() const { return !m_referencedFromCurrentSpell && !m_executedCurrently; } void SetReferencedFromCurrent(bool yes) { m_referencedFromCurrentSpell = yes; } bool IsInterruptable() const { return !m_executedCurrently; } @@ -507,7 +517,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 +571,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; @@ -577,8 +583,8 @@ class TC_GAME_API Spell // ****************************************** uint32 m_procAttacker; // Attacker trigger flags uint32 m_procVictim; // Victim trigger flags - uint32 m_procEx; - void prepareDataForTriggerSystem(AuraEffect const* triggeredByAura); + uint32 m_hitMask; + void prepareDataForTriggerSystem(); // ***************************************** // Spell target subsystem @@ -640,7 +646,7 @@ class TC_GAME_API Spell // spell execution log void InitEffectExecuteData(uint8 effIndex); - void CheckEffectExecuteData(); + void AssertEffectExecuteData() const; // Scripting system void LoadScripts(); @@ -658,10 +664,13 @@ 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 { + HitTriggerSpell(SpellInfo const* spellInfo, SpellInfo const* auraSpellInfo, int32 procChance) : + triggeredSpell(spellInfo), triggeredByAura(auraSpellInfo), chance(procChance) { } + SpellInfo const* triggeredSpell; SpellInfo const* triggeredByAura; // uint8 triggeredByEffIdx This might be needed at a later stage - No need known for now @@ -670,7 +679,7 @@ class TC_GAME_API Spell bool CanExecuteTriggersOnHit(uint8 effMask, SpellInfo const* triggeredByAura = NULL) const; void PrepareTriggersExecutedOnHit(); - typedef std::list<HitTriggerSpell> HitTriggerSpellList; + typedef std::vector<HitTriggerSpell> HitTriggerSpellList; HitTriggerSpellList m_hitTriggerSpells; // effect helpers diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index d8feaf22c6f..7afcf52172a 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) @@ -395,9 +399,9 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) // Conflagrate - consumes Immolate or Shadowflame else if (m_spellInfo->TargetAuraState == AURA_STATE_CONFLAGRATE) { - AuraEffect const* aura = NULL; // found req. aura for damage calculation + AuraEffect const* aura = nullptr; // found req. aura for damage calculation - Unit::AuraEffectList const &mPeriodic = unitTarget->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE); + Unit::AuraEffectList const& mPeriodic = unitTarget->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE); for (Unit::AuraEffectList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i) { // for caster applied auras only @@ -420,15 +424,22 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) // found Immolate or Shadowflame if (aura) { - uint32 pdamage = uint32(std::max(aura->GetAmount(), 0)); - pdamage = m_caster->SpellDamageBonusDone(unitTarget, aura->GetSpellInfo(), pdamage, DOT, aura->GetBase()->GetStackAmount()); - pdamage = unitTarget->SpellDamageBonusTaken(m_caster, aura->GetSpellInfo(), pdamage, DOT, aura->GetBase()->GetStackAmount()); - uint32 pct_dir = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, (effIndex + 1)); - uint8 baseTotalTicks = uint8(m_caster->CalcSpellDuration(aura->GetSpellInfo()) / aura->GetSpellInfo()->Effects[EFFECT_0].Amplitude); - damage += int32(CalculatePct(pdamage * baseTotalTicks, pct_dir)); + // Calculate damage of Immolate/Shadowflame tick + int32 pdamage = (aura->GetAmount() + aura->GetBonusAmount()) * aura->GetDonePct(); + if (Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod<SPELLMOD_DOT>(GetSpellInfo()->Id, pdamage); + + pdamage = unitTarget->SpellDamageBonusTaken(m_caster, aura->GetSpellInfo(), pdamage, DOT); + + // And multiply by amount of ticks to get damage potential + pdamage *= aura->GetSpellInfo()->GetMaxTicks(); - uint32 pct_dot = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, (effIndex + 2)) / 3; - m_spellValue->EffectBasePoints[1] = m_spellInfo->Effects[EFFECT_1].CalcBaseValue(int32(CalculatePct(pdamage * baseTotalTicks, pct_dot))); + int32 pct_dir = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_1); + damage += CalculatePct(pdamage, pct_dir); + + int32 pct_dot = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_2); + int32 const dotBasePoints = CalculatePct(pdamage, pct_dot); + m_spellValue->EffectBasePoints[EFFECT_1] = dotBasePoints / m_spellInfo->GetMaxTicks(); apply_direct_bonus = false; // Glyph of Conflagrate @@ -1393,9 +1404,12 @@ void Spell::EffectHeal(SpellEffIndex /*effIndex*/) return; } - int32 tickheal = targetAura->GetAmount(); - if (Unit* auraCaster = targetAura->GetCaster()) - tickheal = auraCaster->SpellHealingBonusDone(unitTarget, targetAura->GetSpellInfo(), tickheal, DOT); + int32 tickheal = (targetAura->GetAmount() + targetAura->GetBonusAmount()) * targetAura->GetDonePct(); + if (Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod<SPELLMOD_DOT>(targetAura->GetId(), tickheal); + + unitTarget->SpellHealingBonusTaken(m_caster, targetAura->GetSpellInfo(), tickheal, DOT); + //int32 tickheal = targetAura->GetSpellInfo()->EffectBasePoints[idx] + 1; //It is said that talent bonus should not be included @@ -1507,7 +1521,8 @@ void Spell::EffectHealthLeech(SpellEffIndex effIndex) healthGain = m_caster->SpellHealingBonusDone(m_caster, m_spellInfo, healthGain, HEAL); healthGain = m_caster->SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, HEAL); - m_caster->HealBySpell(m_caster, m_spellInfo, uint32(healthGain)); + HealInfo healInfo(m_caster, m_caster, healthGain, m_spellInfo, m_spellSchoolMask); + m_caster->HealBySpell(healInfo); } } @@ -2022,7 +2037,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) @@ -2102,6 +2117,8 @@ void Spell::EffectSummonChangeItem(SpellEffIndex effIndex) m_castItemEntry = 0; player->StoreItem(dest, pNewItem, true); + player->SendNewItem(pNewItem, 1, true, false); + player->ItemAddedQuestCheck(newitemid, 1); return; } } @@ -2146,6 +2163,8 @@ void Spell::EffectSummonChangeItem(SpellEffIndex effIndex) player->EquipItem(dest, pNewItem, true); player->AutoUnequipOffhandIfNeed(); + player->SendNewItem(pNewItem, 1, true, false); + player->ItemAddedQuestCheck(newitemid, 1); return; } } @@ -2197,7 +2216,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) int32 duration = m_spellInfo->GetDuration(); if (Player* modOwner = m_originalCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); + modOwner->ApplySpellMod<SPELLMOD_DURATION>(m_spellInfo->Id, duration); TempSummon* summon = NULL; @@ -2685,7 +2704,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; @@ -3361,8 +3380,10 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) } } - // apply to non-weapon bonus weapon total pct effect, weapon total flat effect included in weapon damage - if (fixed_bonus || spell_bonus) + // if (addPctMods) { percent mods are added in Unit::CalculateDamage } else { percent mods are added in Unit::MeleeDamageBonusDone } + // this distinction is neccessary to properly inform the client about his autoattack damage values from Script_UnitDamage + bool addPctMods = !m_spellInfo->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS) && (m_spellSchoolMask & SPELL_SCHOOL_MASK_NORMAL); + if (addPctMods) { UnitMods unitMod; switch (m_attackType) @@ -3373,17 +3394,14 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break; } - float weapon_total_pct = 1.0f; - if (m_spellInfo->SchoolMask & SPELL_SCHOOL_MASK_NORMAL) - weapon_total_pct = m_caster->GetModifierValue(unitMod, TOTAL_PCT); - + float weapon_total_pct = m_caster->GetModifierValue(unitMod, TOTAL_PCT); if (fixed_bonus) fixed_bonus = int32(fixed_bonus * weapon_total_pct); if (spell_bonus) spell_bonus = int32(spell_bonus * weapon_total_pct); } - int32 weaponDamage = m_caster->CalculateDamage(m_attackType, normalized, true); + int32 weaponDamage = m_caster->CalculateDamage(m_attackType, normalized, addPctMods); // Sequence is important for (int j = 0; j < MAX_SPELL_EFFECTS; ++j) @@ -3398,17 +3416,14 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) weaponDamage += fixed_bonus; break; case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: - weaponDamage = int32(weaponDamage* weaponDamagePercentMod); + weaponDamage = int32(weaponDamage * weaponDamagePercentMod); default: break; // not weapon damage effect, just skip } } - if (spell_bonus) - weaponDamage += spell_bonus; - - if (totalDamagePercentMod != 1.0f) - weaponDamage = int32(weaponDamage* totalDamagePercentMod); + weaponDamage += spell_bonus; + weaponDamage = int32(weaponDamage * totalDamagePercentMod); // prevent negative damage uint32 eff_damage(std::max(weaponDamage, 0)); @@ -3454,7 +3469,7 @@ void Spell::EffectHealMaxHealth(SpellEffIndex /*effIndex*/) void Spell::EffectInterruptCast(SpellEffIndex effIndex) { - if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) + if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET) return; if (!unitTarget || !unitTarget->IsAlive()) @@ -3530,21 +3545,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); - - ExecuteLogEffectSummonObject(effIndex, linkedGO); + linkedTrap->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0); + linkedTrap->SetSpellId(m_spellInfo->Id); - // Wild object not have owner and check clickable by players - map->AddToMap(linkedGO); - } - else - delete linkedGO; + ExecuteLogEffectSummonObject(effIndex, linkedTrap); } } @@ -4578,8 +4584,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)) // never rewarded before - player->CompleteQuest(questId); // quest not in log - for internal use + 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. } } @@ -4756,7 +4762,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) @@ -5136,26 +5142,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); } } @@ -5168,7 +5162,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) @@ -5193,7 +5187,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) @@ -5286,8 +5280,8 @@ void Spell::EffectStealBeneficialBuff(SpellEffIndex effIndex) // The charges / stack amounts don't count towards the total number of auras that can be dispelled. // Ie: A dispel on a target with 5 stacks of Winters Chill and a Polymorph has 1 / (1 + 1) -> 50% chance to dispell // Polymorph instead of 1 / (5 + 1) -> 16%. - bool dispel_charges = aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES); - uint8 charges = dispel_charges ? aura->GetCharges() : aura->GetStackAmount(); + bool dispelCharges = aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES); + uint8 charges = dispelCharges ? aura->GetCharges() : aura->GetStackAmount(); if (charges > 0) steal_list.push_back(std::make_pair(aura, charges)); } @@ -5442,18 +5436,8 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) m_runesState = m_caster->ToPlayer()->GetRunesState(); uint32 count = damage; - if (count == 0) count = 1; - for (uint32 j = 0; j < MAX_RUNES && count > 0; ++j) - { - if (player->GetRuneCooldown(j) && player->GetCurrentRune(j) == RuneType(m_spellInfo->Effects[effIndex].MiscValue)) - { - if (m_spellInfo->Id == 45529) - if (player->GetBaseRune(j) != RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) - continue; - player->SetRuneCooldown(j, 0); - --count; - } - } + if (count == 0) + count = 1; // Blood Tap if (m_spellInfo->Id == 45529 && count > 0) @@ -5461,11 +5445,12 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) for (uint32 l = 0; l + 1 < MAX_RUNES && count > 0; ++l) { // Check if both runes are on cd as that is the only time when this needs to come into effect - if ((player->GetRuneCooldown(l) && player->GetBaseRune(l) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) && (player->GetRuneCooldown(l+1) && player->GetBaseRune(l+1) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB))) + if ((player->GetRuneCooldown(l) && player->GetBaseRune(l) == RUNE_BLOOD) && (player->GetRuneCooldown(l + 1) && player->GetBaseRune(l + 1) == RUNE_BLOOD)) { // Should always update the rune with the lowest cd - if (l + 1 < MAX_RUNES && player->GetRuneCooldown(l) >= player->GetRuneCooldown(l+1)) - l++; + if (l + 1 < MAX_RUNES && player->GetRuneCooldown(l) >= player->GetRuneCooldown(l + 1)) + ++l; + player->SetRuneCooldown(l, 0); --count; // is needed to push through to the client that the rune is active @@ -5476,6 +5461,15 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) } } + for (uint32 j = 0; j < MAX_RUNES && count > 0; ++j) + { + if (player->GetRuneCooldown(j) && player->GetCurrentRune(j) == RuneType(m_spellInfo->Effects[effIndex].MiscValue)) + { + player->SetRuneCooldown(j, 0); + --count; + } + } + // Empower rune weapon if (m_spellInfo->Id == 47568) { @@ -5485,7 +5479,7 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) for (uint32 i = 0; i < MAX_RUNES; ++i) { - if (player->GetRuneCooldown(i) && (player->GetCurrentRune(i) == RUNE_FROST || player->GetCurrentRune(i) == RUNE_DEATH)) + if (player->GetRuneCooldown(i) && (player->GetBaseRune(i) == RUNE_FROST)) player->SetRuneCooldown(i, 0); } } @@ -5531,13 +5525,13 @@ void Spell::EffectDiscoverTaxi(SpellEffIndex effIndex) unitTarget->ToPlayer()->GetSession()->SendDiscoverNewTaxiNode(nodeid); } -void Spell::EffectTitanGrip(SpellEffIndex /*effIndex*/) +void Spell::EffectTitanGrip(SpellEffIndex effIndex) { if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; if (m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->ToPlayer()->SetCanTitanGrip(true); + m_caster->ToPlayer()->SetCanTitanGrip(true, m_spellInfo->Effects[effIndex].MiscValue); } void Spell::EffectRedirectThreat(SpellEffIndex /*effIndex*/) @@ -5614,7 +5608,7 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* int32 duration = m_spellInfo->GetDuration(); if (Player* modOwner = m_originalCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); + modOwner->ApplySpellMod<SPELLMOD_DURATION>(m_spellInfo->Id, duration); //TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN; Map* map = caster->GetMap(); diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp index 31490bea29b..3925ffe552e 100644 --- a/src/server/game/Spells/SpellHistory.cpp +++ b/src/server/game/Spells/SpellHistory.cpp @@ -307,10 +307,10 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel if (Player* modOwner = _owner->GetSpellModOwner()) { if (cooldown > 0) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown, spell); + modOwner->ApplySpellMod<SPELLMOD_COOLDOWN>(spellInfo->Id, cooldown, spell); if (categoryCooldown > 0 && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS)) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, categoryCooldown, spell); + modOwner->ApplySpellMod<SPELLMOD_COOLDOWN>(spellInfo->Id, categoryCooldown, spell); } if (int32 cooldownMod = _owner->GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN)) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index eba950a3233..c0fbdf51889 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" @@ -519,7 +520,7 @@ float SpellEffectInfo::CalcValueMultiplier(Unit* caster, Spell* spell) const { float multiplier = ValueMultiplier; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL)) - modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_VALUE_MULTIPLIER, multiplier, spell); + modOwner->ApplySpellMod<SPELLMOD_VALUE_MULTIPLIER>(_spellInfo->Id, multiplier, spell); return multiplier; } @@ -527,7 +528,7 @@ float SpellEffectInfo::CalcDamageMultiplier(Unit* caster, Spell* spell) const { float multiplierPercent = DamageMultiplier * 100.0f; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL)) - modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_DAMAGE_MULTIPLIER, multiplierPercent, spell); + modOwner->ApplySpellMod<SPELLMOD_DAMAGE_MULTIPLIER>(_spellInfo->Id, multiplierPercent, spell); return multiplierPercent / 100.0f; } @@ -547,7 +548,7 @@ float SpellEffectInfo::CalcRadius(Unit* caster, Spell* spell) const radius += RadiusEntry->RadiusPerLevel * caster->getLevel(); radius = std::min(radius, RadiusEntry->RadiusMax); if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_RADIUS, radius, spell); + modOwner->ApplySpellMod<SPELLMOD_RADIUS>(_spellInfo->Id, radius, spell); } return radius; @@ -855,6 +856,9 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry) ChainEntry = NULL; ExplicitTargetMask = 0; + + _spellSpecific = SPELL_SPECIFIC_NORMAL; + _auraState = AURA_STATE_NONE; } SpellInfo::~SpellInfo() @@ -891,6 +895,31 @@ bool SpellInfo::HasAreaAuraEffect() const return false; } +bool SpellInfo::HasOnlyDamageEffects() const +{ + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (Effects[i].IsEffect()) + { + switch (Effects[i].Effect) + { + case SPELL_EFFECT_WEAPON_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: + case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: + case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: + case SPELL_EFFECT_SCHOOL_DAMAGE: + case SPELL_EFFECT_ENVIRONMENTAL_DAMAGE: + case SPELL_EFFECT_HEALTH_LEECH: + continue; + default: + return false; + } + } + } + + return true; +} + bool SpellInfo::IsExplicitDiscovery() const { return ((Effects[0].Effect == SPELL_EFFECT_CREATE_RANDOM_ITEM @@ -1055,7 +1084,7 @@ bool SpellInfo::IsPassive() const bool SpellInfo::IsAutocastable() const { - if (HasAttribute(SPELL_ATTR0_PASSIVE)) + if (IsPassive()) return false; if (HasAttribute(SPELL_ATTR1_UNAUTOCASTABLE_BY_PET)) return false; @@ -1113,7 +1142,10 @@ bool SpellInfo::IsStackableOnOneSlotWithDifferentCasters() const bool SpellInfo::IsCooldownStartedOnEvent() const { - return HasAttribute(SPELL_ATTR0_DISABLED_WHILE_ACTIVE) || (CategoryEntry && CategoryEntry->Flags & SPELL_CATEGORY_FLAG_COOLDOWN_STARTS_ON_EVENT); + if (HasAttribute(SPELL_ATTR0_DISABLED_WHILE_ACTIVE)) + return true; + + return CategoryEntry && CategoryEntry->Flags & SPELL_CATEGORY_FLAG_COOLDOWN_STARTS_ON_EVENT; } bool SpellInfo::IsDeathPersistent() const @@ -1175,12 +1207,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 @@ -1204,6 +1246,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); @@ -1215,42 +1271,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 || 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; @@ -1405,9 +1460,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; } @@ -1604,7 +1661,7 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta else return SPELL_CAST_OK; // corpseOwner and unit specific target checks - if (HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS) && !unitTarget->ToPlayer()) + if (HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS) && unitTarget->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_TARGET_NOT_PLAYER; if (!IsAllowingDeadTarget() && !unitTarget->IsAlive()) @@ -1618,7 +1675,7 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta //if (!HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) // return SPELL_FAILED_BAD_TARGETS; - //if (!HasAttribute(SPELL_ATTR6_CAN_TARGET_POSSESSED_FRIENDS) + //if (!HasAttribute(SPELL_ATTR6_CAN_TARGET_POSSESSED_FRIENDS)) if (!CheckTargetCreatureType(unitTarget)) { @@ -1639,7 +1696,7 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta } // not allow casting on flying player - if (unitTarget->HasUnitState(UNIT_STATE_IN_FLIGHT) && !(AttributesCu & SPELL_ATTR0_CU_ALLOW_INFLIGHT_TARGET)) + if (unitTarget->HasUnitState(UNIT_STATE_IN_FLIGHT) && !HasAttribute(SPELL_ATTR0_CU_ALLOW_INFLIGHT_TARGET)) return SPELL_FAILED_BAD_TARGETS; /* TARGET_UNIT_MASTER gets blocked here for passengers, because the whole idea of this check is to @@ -1851,239 +1908,1037 @@ uint32 SpellInfo::GetExplicitTargetMask() const AuraStateType SpellInfo::GetAuraState() const { - // Seals - if (GetSpellSpecific() == SPELL_SPECIFIC_SEAL) - return AURA_STATE_JUDGEMENT; - - // Conflagrate aura state on Immolate and Shadowflame - if (SpellFamilyName == SPELLFAMILY_WARLOCK && - // Immolate - ((SpellFamilyFlags[0] & 4) || - // Shadowflame - (SpellFamilyFlags[2] & 2))) - return AURA_STATE_CONFLAGRATE; - - // Faerie Fire (druid versions) - if (SpellFamilyName == SPELLFAMILY_DRUID && SpellFamilyFlags[0] & 0x400) - return AURA_STATE_FAERIE_FIRE; - - // Sting (hunter's pet ability) - if (GetCategory() == 1133) - return AURA_STATE_FAERIE_FIRE; - - // Victorious - if (SpellFamilyName == SPELLFAMILY_WARRIOR && SpellFamilyFlags[1] & 0x00040000) - return AURA_STATE_WARRIOR_VICTORY_RUSH; - - // Swiftmend state on Regrowth & Rejuvenation - if (SpellFamilyName == SPELLFAMILY_DRUID && SpellFamilyFlags[0] & 0x50) - return AURA_STATE_SWIFTMEND; - - // Deadly poison aura state - if (SpellFamilyName == SPELLFAMILY_ROGUE && SpellFamilyFlags[0] & 0x10000) - return AURA_STATE_DEADLY_POISON; - - // Enrage aura state - if (Dispel == DISPEL_ENRAGE) - return AURA_STATE_ENRAGE; - - // Bleeding aura state - if (GetAllEffectsMechanicMask() & 1<<MECHANIC_BLEED) - return AURA_STATE_BLEEDING; - - if (GetSchoolMask() & SPELL_SCHOOL_MASK_FROST) - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (Effects[i].IsAura() && (Effects[i].ApplyAuraName == SPELL_AURA_MOD_STUN - || Effects[i].ApplyAuraName == SPELL_AURA_MOD_ROOT)) - return AURA_STATE_FROZEN; + return _auraState; +} - switch (Id) +void SpellInfo::_LoadAuraState() +{ + _auraState = [this]() -> AuraStateType { - case 71465: // Divine Surge - case 50241: // Evasive Charges - return AURA_STATE_UNKNOWN22; - default: - break; - } + // Seals + if (GetSpellSpecific() == SPELL_SPECIFIC_SEAL) + return AURA_STATE_JUDGEMENT; + + // Conflagrate aura state on Immolate and Shadowflame + if (SpellFamilyName == SPELLFAMILY_WARLOCK && + // Immolate + ((SpellFamilyFlags[0] & 4) || + // Shadowflame + (SpellFamilyFlags[2] & 2))) + return AURA_STATE_CONFLAGRATE; + + // Faerie Fire (druid versions) + if (SpellFamilyName == SPELLFAMILY_DRUID && SpellFamilyFlags[0] & 0x400) + return AURA_STATE_FAERIE_FIRE; + + // Sting (hunter's pet ability) + if (GetCategory() == 1133) + return AURA_STATE_FAERIE_FIRE; + + // Victorious + if (SpellFamilyName == SPELLFAMILY_WARRIOR && SpellFamilyFlags[1] & 0x00040000) + return AURA_STATE_WARRIOR_VICTORY_RUSH; + + // Swiftmend state on Regrowth & Rejuvenation + if (SpellFamilyName == SPELLFAMILY_DRUID && SpellFamilyFlags[0] & 0x50) + return AURA_STATE_SWIFTMEND; + + // Deadly poison aura state + if (SpellFamilyName == SPELLFAMILY_ROGUE && SpellFamilyFlags[0] & 0x10000) + return AURA_STATE_DEADLY_POISON; + + // Enrage aura state + if (Dispel == DISPEL_ENRAGE) + return AURA_STATE_ENRAGE; + + // Bleeding aura state + if (GetAllEffectsMechanicMask() & 1 << MECHANIC_BLEED) + return AURA_STATE_BLEEDING; + + if (GetSchoolMask() & SPELL_SCHOOL_MASK_FROST) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (Effects[i].IsAura() && (Effects[i].ApplyAuraName == SPELL_AURA_MOD_STUN + || Effects[i].ApplyAuraName == SPELL_AURA_MOD_ROOT)) + return AURA_STATE_FROZEN; + + switch (Id) + { + case 71465: // Divine Surge + case 50241: // Evasive Charges + return AURA_STATE_UNKNOWN22; + default: + break; + } - return AURA_STATE_NONE; + return AURA_STATE_NONE; + }(); } SpellSpecificType SpellInfo::GetSpellSpecific() const { - switch (SpellFamilyName) + return _spellSpecific; +} + +void SpellInfo::_LoadSpellSpecific() +{ + _spellSpecific = [this]() -> SpellSpecificType { - case SPELLFAMILY_GENERIC: + switch (SpellFamilyName) { - // Food / Drinks (mostly) - if (AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) + case SPELLFAMILY_GENERIC: { - bool food = false; - bool drink = false; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + // Food / Drinks (mostly) + if (AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) { - if (!Effects[i].IsAura()) - continue; - switch (Effects[i].ApplyAuraName) + bool food = false; + bool drink = false; + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - // Food - case SPELL_AURA_MOD_REGEN: - case SPELL_AURA_OBS_MOD_HEALTH: - food = true; - break; - // Drink - case SPELL_AURA_MOD_POWER_REGEN: - case SPELL_AURA_OBS_MOD_POWER: - drink = true; + if (!Effects[i].IsAura()) + continue; + switch (Effects[i].ApplyAuraName) + { + // Food + case SPELL_AURA_MOD_REGEN: + case SPELL_AURA_OBS_MOD_HEALTH: + food = true; + break; + // Drink + case SPELL_AURA_MOD_POWER_REGEN: + case SPELL_AURA_OBS_MOD_POWER: + drink = true; + break; + default: + break; + } + } + + if (food && drink) + return SPELL_SPECIFIC_FOOD_AND_DRINK; + else if (food) + return SPELL_SPECIFIC_FOOD; + else if (drink) + return SPELL_SPECIFIC_DRINK; + } + // scrolls effects + else + { + SpellInfo const* firstRankSpellInfo = GetFirstRankSpell(); + switch (firstRankSpellInfo->Id) + { + case 8118: // Strength + case 8099: // Stamina + case 8112: // Spirit + case 8096: // Intellect + 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; + } + case SPELLFAMILY_MAGE: + { + // family flags 18(Molten), 25(Frost/Ice), 28(Mage) + if (SpellFamilyFlags[0] & 0x12040000) + return SPELL_SPECIFIC_MAGE_ARMOR; - if (food && drink) - return SPELL_SPECIFIC_FOOD_AND_DRINK; - else if (food) - return SPELL_SPECIFIC_FOOD; - else if (drink) - return SPELL_SPECIFIC_DRINK; + // Arcane brillance and Arcane intelect (normal check fails because of flags difference) + if (SpellFamilyFlags[0] & 0x400) + return SPELL_SPECIFIC_MAGE_ARCANE_BRILLANCE; + + if ((SpellFamilyFlags[0] & 0x1000000) && Effects[0].ApplyAuraName == SPELL_AURA_MOD_CONFUSE) + return SPELL_SPECIFIC_MAGE_POLYMORPH; + + break; } - // scrolls effects - else + case SPELLFAMILY_WARRIOR: + { + if (Id == 12292) // Death Wish + return SPELL_SPECIFIC_WARRIOR_ENRAGE; + + break; + } + case SPELLFAMILY_WARLOCK: + { + // only warlock curses have this + if (Dispel == DISPEL_CURSE) + return SPELL_SPECIFIC_CURSE; + + // Warlock (Demon Armor | Demon Skin | Fel Armor) + if (SpellFamilyFlags[1] & 0x20000020 || SpellFamilyFlags[2] & 0x00000010) + return SPELL_SPECIFIC_WARLOCK_ARMOR; + + //seed of corruption and corruption + if (SpellFamilyFlags[1] & 0x10 || SpellFamilyFlags[0] & 0x2) + return SPELL_SPECIFIC_WARLOCK_CORRUPTION; + break; + } + case SPELLFAMILY_PRIEST: { - SpellInfo const* firstRankSpellInfo = GetFirstRankSpell(); - switch (firstRankSpellInfo->Id) + // Divine Spirit and Prayer of Spirit + if (SpellFamilyFlags[0] & 0x20) + return SPELL_SPECIFIC_PRIEST_DIVINE_SPIRIT; + + break; + } + case SPELLFAMILY_HUNTER: + { + // only hunter stings have this + if (Dispel == DISPEL_POISON) + return SPELL_SPECIFIC_STING; + + // only hunter aspects have this (but not all aspects in hunter family) + if (SpellFamilyFlags.HasFlag(0x00380000, 0x00440000, 0x00001010)) + return SPELL_SPECIFIC_ASPECT; + + break; + } + case SPELLFAMILY_PALADIN: + { + // Collection of all the seal family flags. No other paladin spell has any of those. + if (SpellFamilyFlags[1] & 0x26000C00 + || SpellFamilyFlags[0] & 0x0A000000) + return SPELL_SPECIFIC_SEAL; + + if (SpellFamilyFlags[0] & 0x00002190) + return SPELL_SPECIFIC_HAND; + + // Judgement of Wisdom, Judgement of Light, Judgement of Justice + if (Id == 20184 || Id == 20185 || Id == 20186) + return SPELL_SPECIFIC_JUDGEMENT; + + // only paladin auras have this (for palaldin class family) + if (SpellFamilyFlags[2] & 0x00000020) + return SPELL_SPECIFIC_AURA; + + break; + } + case SPELLFAMILY_SHAMAN: + { + // family flags 10 (Lightning), 42 (Earth), 37 (Water), proc shield from T2 8 pieces bonus + if (SpellFamilyFlags[1] & 0x420 + || SpellFamilyFlags[0] & 0x00000400 + || Id == 23552) + return SPELL_SPECIFIC_ELEMENTAL_SHIELD; + + break; + } + case SPELLFAMILY_DEATHKNIGHT: + if (Id == 48266 || Id == 48263 || Id == 48265) + return SPELL_SPECIFIC_PRESENCE; + break; + } + + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) + { + switch (Effects[i].ApplyAuraName) { - case 8118: // Strength - case 8099: // Stamina - case 8112: // Spirit - case 8096: // Intellect - case 8115: // Agility - case 8091: // Armor - return SPELL_SPECIFIC_SCROLL; - case 12880: // Enrage (Enrage) - case 57518: // Enrage (Wrecking Crew) - return SPELL_SPECIFIC_WARRIOR_ENRAGE; + case SPELL_AURA_MOD_CHARM: + case SPELL_AURA_MOD_POSSESS_PET: + case SPELL_AURA_MOD_POSSESS: + case SPELL_AURA_AOE_CHARM: + return SPELL_SPECIFIC_CHARM; + case SPELL_AURA_TRACK_CREATURES: + /// @workaround For non-stacking tracking spells (We need generic solution) + if (Id == 30645) // Gas Cloud Tracking + return SPELL_SPECIFIC_NORMAL; + case SPELL_AURA_TRACK_RESOURCES: + case SPELL_AURA_TRACK_STEALTHED: + return SPELL_SPECIFIC_TRACKER; } } - break; } - case SPELLFAMILY_MAGE: - { - // family flags 18(Molten), 25(Frost/Ice), 28(Mage) - if (SpellFamilyFlags[0] & 0x12040000) - return SPELL_SPECIFIC_MAGE_ARMOR; - // Arcane brillance and Arcane intelect (normal check fails because of flags difference) - if (SpellFamilyFlags[0] & 0x400) - return SPELL_SPECIFIC_MAGE_ARCANE_BRILLANCE; + return SPELL_SPECIFIC_NORMAL; + }(); +} - if ((SpellFamilyFlags[0] & 0x1000000) && Effects[0].ApplyAuraName == SPELL_AURA_MOD_CONFUSE) - return SPELL_SPECIFIC_MAGE_POLYMORPH; +void SpellInfo::_LoadSpellDiminishInfo() +{ + auto diminishingGroupCompute = [this](bool triggered) -> DiminishingGroup + { + if (IsPositive()) + return DIMINISHING_NONE; - break; - } - case SPELLFAMILY_WARRIOR: + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - if (Id == 12292) // Death Wish - return SPELL_SPECIFIC_WARRIOR_ENRAGE; - - break; + if (Effects[i].ApplyAuraName == SPELL_AURA_MOD_TAUNT) + return DIMINISHING_TAUNT; } - case SPELLFAMILY_WARLOCK: + + // Explicit Diminishing Groups + switch (SpellFamilyName) { - // only warlock curses have this - if (Dispel == DISPEL_CURSE) - return SPELL_SPECIFIC_CURSE; + 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; + } - // Warlock (Demon Armor | Demon Skin | Fel Armor) - if (SpellFamilyFlags[1] & 0x20000020 || SpellFamilyFlags[2] & 0x00000010) - return SPELL_SPECIFIC_WARLOCK_ARMOR; + // 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; + } + }; - //seed of corruption and corruption - if (SpellFamilyFlags[1] & 0x10 || SpellFamilyFlags[0] & 0x2) - return SPELL_SPECIFIC_WARLOCK_CORRUPTION; - break; + auto diminishingMaxLevelCompute = [](DiminishingGroup group) -> DiminishingLevels + { + switch (group) + { + case DIMINISHING_TAUNT: + return DIMINISHING_LEVEL_TAUNT_IMMUNE; + default: + return DIMINISHING_LEVEL_IMMUNE; } - case SPELLFAMILY_PRIEST: + }; + + auto diminishingLimitDurationCompute = [this](DiminishingGroup group) -> int32 + { + auto isGroupDurationLimited = [group]() -> bool { - // Divine Spirit and Prayer of Spirit - if (SpellFamilyFlags[0] & 0x20) - return SPELL_SPECIFIC_PRIEST_DIVINE_SPIRIT; + 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; + } + }; - break; + 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; } - case SPELLFAMILY_HUNTER: + + 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) { - // only hunter stings have this - if (Dispel == DISPEL_POISON) - return SPELL_SPECIFIC_STING; + 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; - // only hunter aspects have this (but not all aspects in hunter family) - if (SpellFamilyFlags.HasFlag(0x00380000, 0x00440000, 0x00001010)) - return SPELL_SPECIFIC_ASPECT; + 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; - break; + 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; } - case SPELLFAMILY_PALADIN: - { - // Collection of all the seal family flags. No other paladin spell has any of those. - if (SpellFamilyFlags[1] & 0x26000C00 - || SpellFamilyFlags[0] & 0x0A000000) - return SPELL_SPECIFIC_SEAL; - if (SpellFamilyFlags[0] & 0x00002190) - return SPELL_SPECIFIC_HAND; + 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(); + } +} - // Judgement of Wisdom, Judgement of Light, Judgement of Justice - if (Id == 20184 || Id == 20185 || Id == 20186) - return SPELL_SPECIFIC_JUDGEMENT; +void SpellInfo::ApplyAllSpellImmunitiesTo(Unit* target, uint8 effIndex, bool apply) const +{ + ImmunityInfo const* immuneInfo = _immunityInfo + effIndex; - // only paladin auras have this (for palaldin class family) - if (SpellFamilyFlags[2] & 0x00000020) - return SPELL_SPECIFIC_AURA; + if (uint32 schoolImmunity = immuneInfo->SchoolImmuneMask) + { + target->ApplySpellImmune(Id, IMMUNITY_SCHOOL, schoolImmunity, apply); - break; + 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 + }); } - case SPELLFAMILY_SHAMAN: + } + + 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)) { - // family flags 10 (Lightning), 42 (Earth), 37 (Water), proc shield from T2 8 pieces bonus - if (SpellFamilyFlags[1] & 0x420 - || SpellFamilyFlags[0] & 0x00000400 - || Id == 23552) - return SPELL_SPECIFIC_ELEMENTAL_SHIELD; + target->RemoveAppliedAuras([dispelImmunity](AuraApplication const* aurApp) -> bool + { + SpellInfo const* spellInfo = aurApp->GetBase()->GetSpellInfo(); + if (spellInfo->Dispel == dispelImmunity) + return true; - break; + return false; + }); } - case SPELLFAMILY_DEATHKNIGHT: - if (Id == 48266 || Id == 48263 || Id == 48265) - return SPELL_SPECIFIC_PRESENCE; - break; } + 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) { - if (Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) + ImmunityInfo const* immuneInfo = _immunityInfo + i; + + if (!auraSpellInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) && !auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) { - switch (Effects[i].ApplyAuraName) + 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()) { - case SPELL_AURA_MOD_CHARM: - case SPELL_AURA_MOD_POSSESS_PET: - case SPELL_AURA_MOD_POSSESS: - case SPELL_AURA_AOE_CHARM: - return SPELL_SPECIFIC_CHARM; - case SPELL_AURA_TRACK_CREATURES: - /// @workaround For non-stacking tracking spells (We need generic solution) - if (Id == 30645) // Gas Cloud Tracking - return SPELL_SPECIFIC_NORMAL; - case SPELL_AURA_TRACK_RESOURCES: - case SPELL_AURA_TRACK_STEALTHED: - return SPELL_SPECIFIC_TRACKER; + 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 SPELL_SPECIFIC_NORMAL; + return false; } float SpellInfo::GetMinRange(bool positive) const @@ -2106,7 +2961,7 @@ float SpellInfo::GetMaxRange(bool positive, Unit* caster, Spell* spell) const range = RangeEntry->maxRangeHostile; if (caster) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(Id, SPELLMOD_RANGE, range, spell); + modOwner->ApplySpellMod<SPELLMOD_RANGE>(Id, range, spell); return range; } @@ -2252,7 +3107,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) c // Apply cost mod by spell if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(Id, SPELLMOD_COST, powerCost); + modOwner->ApplySpellMod<SPELLMOD_COST>(Id, powerCost); if (!caster->IsControlledByPlayer()) { @@ -2430,9 +3285,12 @@ bool SpellInfo::_IsPositiveEffect(uint8 effIndex, bool deep) const // Amplify Magic, Dampen Magic if (SpellFamilyFlags[0] == 0x00002000) return true; - // Ignite + // Impact if (SpellIconID == 45) return true; + // Arcane Missiles + if (SpellFamilyFlags[0] == 0x00000800) + return false; break; case SPELLFAMILY_PRIEST: switch (Id) @@ -2462,6 +3320,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; @@ -2482,8 +3343,16 @@ bool SpellInfo::_IsPositiveEffect(uint8 effIndex, bool deep) const // Special case: effects which determine positivity of whole spell for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - if (Effects[i].IsAura() && Effects[i].ApplyAuraName == SPELL_AURA_MOD_STEALTH) - return true; + if (Effects[i].IsAura()) + { + switch (Effects[i].ApplyAuraName) + { + case SPELL_AURA_MOD_STEALTH: + return true; + case SPELL_AURA_CHANNEL_DEATH_ITEM: + return false; + } + } } switch (Effects[effIndex].Effect) diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 589ed16e409..d7b48ddb4d2 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,201 +294,252 @@ private: static StaticData _data[TOTAL_SPELL_EFFECTS]; }; +struct TC_GAME_API SpellDiminishInfo +{ + DiminishingGroup DiminishGroup = DIMINISHING_NONE; + DiminishingReturnsType DiminishReturnType = DRTYPE_NONE; + DiminishingLevels DiminishMaxLevel = DIMINISHING_LEVEL_IMMUNE; + int32 DiminishDurationLimit = 0; +}; + +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 { -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); - - // unloading helpers - void _UnloadImplicitTargetConditionLists(); + 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; + bool HasOnlyDamageEffects() 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 ccdda800bc0..bd13f1b2c7b 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -18,6 +18,7 @@ #include "SpellMgr.h" #include "SpellInfo.h" +#include "Spell.h" #include "ObjectMgr.h" #include "SpellAuraDefines.h" #include "SharedDefines.h" @@ -50,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() @@ -779,172 +484,6 @@ SpellGroupStackRule SpellMgr::GetSpellGroupStackRule(SpellGroup group) const return SPELL_GROUP_STACK_RULE_DEFAULT; } -SpellProcEventEntry const* SpellMgr::GetSpellProcEvent(uint32 spellId) const -{ - SpellProcEventMap::const_iterator itr = mSpellProcEventMap.find(spellId); - if (itr != mSpellProcEventMap.end()) - return &itr->second; - return NULL; -} - -bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellInfo const* procSpell, uint32 procFlags, uint32 procExtra, bool active) const -{ - // No extra req need - uint32 procEvent_procEx = PROC_EX_NONE; - - // check prockFlags for condition - if ((procFlags & EventProcFlag) == 0) - return false; - - bool hasFamilyMask = false; - - /** - - * @brief Check auras procced by periodics - - *Only damaging Dots can proc auras with PROC_FLAG_TAKEN_DAMAGE - - *Only Dots can proc if ONLY has PROC_FLAG_DONE_PERIODIC or PROC_FLAG_TAKEN_PERIODIC. - - *Hots can proc if ONLY has PROC_FLAG_DONE_PERIODIC and spellfamily != 0 - - *Only Dots can proc auras with PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG or PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG - - *Only Hots can proc auras with PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS or PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS - - *Only Dots can proc auras with PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG or PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG - - *Only Hots can proc auras with PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS or PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_POS - - * @param procSpell the spell proccing the aura - * @param procFlags proc_flags of spellProc - * @param procExtra proc_EX of procSpell - * @param EventProcFlag proc_flags of aura to be procced - * @param spellProto SpellInfo of aura to be procced - - */ - - /// Quick Check - If PROC_FLAG_TAKEN_DAMAGE is set for aura and procSpell dealt damage, proc no matter what kind of spell that deals the damage. - if (procFlags & PROC_FLAG_TAKEN_DAMAGE && EventProcFlag & PROC_FLAG_TAKEN_DAMAGE) - return true; - - if (procFlags & PROC_FLAG_DONE_PERIODIC && EventProcFlag & PROC_FLAG_DONE_PERIODIC) - { - if (procExtra & PROC_EX_INTERNAL_HOT) - { - if (EventProcFlag == PROC_FLAG_DONE_PERIODIC) - { - /// no aura with only PROC_FLAG_DONE_PERIODIC and spellFamilyName == 0 can proc from a HOT. - if (!spellProto->SpellFamilyName) - return false; - } - /// Aura must have positive procflags for a HOT to proc - else if (!(EventProcFlag & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS))) - return false; - } - /// Aura must have negative or neutral(PROC_FLAG_DONE_PERIODIC only) procflags for a DOT to proc - /// Traps are negative spells but not always do damage (only hunter traps set PROC_FLAG_DONE_TRAP_ACTIVATION) - else if (EventProcFlag != PROC_FLAG_DONE_PERIODIC) - if (!(EventProcFlag & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG | PROC_FLAG_DONE_TRAP_ACTIVATION))) - return false; - } - - if (procFlags & PROC_FLAG_TAKEN_PERIODIC && EventProcFlag & PROC_FLAG_TAKEN_PERIODIC) - { - if (procExtra & PROC_EX_INTERNAL_HOT) - { - /// No aura that only has PROC_FLAG_TAKEN_PERIODIC can proc from a HOT. - if (EventProcFlag == PROC_FLAG_TAKEN_PERIODIC) - return false; - /// Aura must have positive procflags for a HOT to proc - if (!(EventProcFlag & (PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_POS))) - return false; - } - /// Aura must have negative or neutral(PROC_FLAG_TAKEN_PERIODIC only) procflags for a DOT to proc - else if (EventProcFlag != PROC_FLAG_TAKEN_PERIODIC) - if (!(EventProcFlag & (PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG))) - return false; - } - // Trap casts are active by default - if (procFlags & PROC_FLAG_DONE_TRAP_ACTIVATION) - active = true; - - // Always trigger for this - if (procFlags & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH)) - return true; - - if (spellProcEvent) // Exist event data - { - // Store extra req - procEvent_procEx = spellProcEvent->procEx; - - // For melee triggers - if (procSpell == NULL) - { - // Check (if set) for school (melee attack has Normal school) - if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & SPELL_SCHOOL_MASK_NORMAL) == 0) - return false; - } - else // For spells need check school/spell family/family mask - { - // Check (if set) for school - if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & procSpell->SchoolMask) == 0) - return false; - - // Check (if set) for spellFamilyName - if (spellProcEvent->spellFamilyName && (spellProcEvent->spellFamilyName != procSpell->SpellFamilyName)) - return false; - - // spellFamilyName is Ok need check for spellFamilyMask if present - if (spellProcEvent->spellFamilyMask) - { - if (!(spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags)) - return false; - hasFamilyMask = true; - // Some spells are not considered as active even with spellfamilyflags set - if (!(procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL)) - active = true; - } - } - } - - if (procExtra & (PROC_EX_INTERNAL_REQ_FAMILY)) - { - if (!hasFamilyMask) - return false; - } - - // Check for extra req (if none) and hit/crit - if (procEvent_procEx == PROC_EX_NONE) - { - // No extra req, so can trigger only for hit/crit - spell has to be active - if ((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) && active) - return true; - } - else // Passive spells hits here only if resist/reflect/immune/evade - { - if (procExtra & AURA_SPELL_PROC_EX_MASK) - { - // if spell marked as procing only from not active spells - if (active && procEvent_procEx & PROC_EX_NOT_ACTIVE_SPELL) - return false; - // if spell marked as procing only from active spells - if (!active && procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL) - return false; - // Exist req for PROC_EX_EX_TRIGGER_ALWAYS - if (procEvent_procEx & PROC_EX_EX_TRIGGER_ALWAYS) - return true; - // PROC_EX_NOT_ACTIVE_SPELL and PROC_EX_ONLY_ACTIVE_SPELL flags handle: if passed checks before - if ((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) && ((procEvent_procEx & (AURA_SPELL_PROC_EX_MASK)) == 0)) - return true; - } - // Check Extra Requirement like (hit/crit/miss/resist/parry/dodge/block/immune/reflect/absorb and other) - if (procEvent_procEx & procExtra) - return true; - } - return false; -} - SpellProcEntry const* SpellMgr::GetSpellProcEntry(uint32 spellId) const { SpellProcMap::const_iterator itr = mSpellProcMap.find(spellId); @@ -953,7 +492,7 @@ SpellProcEntry const* SpellMgr::GetSpellProcEntry(uint32 spellId) const return NULL; } -bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const +bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) { // proc type doesn't match if (!(eventInfo.GetTypeMask() & procEntry.ProcFlags)) @@ -965,29 +504,44 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE if (eventInfo.GetActionTarget() && !actor->isHonorOrXPTarget(eventInfo.GetActionTarget())) return false; + // check mana requirement + if (procEntry.AttributesMask & PROC_ATTR_REQ_MANA_COST) + if (SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo()) + if (!eventSpellInfo->ManaCost && !eventSpellInfo->ManaCostPercentage) + return false; + // always trigger for these types if (eventInfo.GetTypeMask() & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH)) return true; + // do triggered cast checks + // 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()) + { + if (spell->IsTriggered()) + { + SpellInfo const* spellInfo = spell->GetSpellInfo(); + if (!spellInfo->HasAttribute(SPELL_ATTR3_TRIGGERED_CAN_TRIGGER_PROC_2) && + !spellInfo->HasAttribute(SPELL_ATTR2_TRIGGERED_CAN_TRIGGER_PROC)) + return false; + } + } + } + // check school mask (if set) for other trigger types if (procEntry.SchoolMask && !(eventInfo.GetSchoolMask() & procEntry.SchoolMask)) 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; } @@ -1487,29 +1041,38 @@ void SpellMgr::LoadSpellLearnSkills() // search auto-learned skills and add its to map also for use in unlearn spells/talents uint32 dbc_count = 0; - for (uint32 spell = 0; spell < GetSpellInfoStoreSize(); ++spell) + for (SpellInfo const* entry : mSpellInfoMap) { - SpellInfo const* entry = GetSpellInfo(spell); - if (!entry) continue; for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - if (entry->Effects[i].Effect == SPELL_EFFECT_SKILL) + SpellLearnSkillNode dbc_node; + switch (entry->Effects[i].Effect) { - SpellLearnSkillNode dbc_node; - dbc_node.skill = entry->Effects[i].MiscValue; - dbc_node.step = entry->Effects[i].CalcValue(); - if (dbc_node.skill != SKILL_RIDING) + case SPELL_EFFECT_SKILL: + dbc_node.skill = entry->Effects[i].MiscValue; + dbc_node.step = entry->Effects[i].CalcValue(); + if (dbc_node.skill != SKILL_RIDING) + dbc_node.value = 1; + else + dbc_node.value = dbc_node.step * 75; + dbc_node.maxvalue = dbc_node.step * 75; + break; + case SPELL_EFFECT_DUAL_WIELD: + dbc_node.skill = SKILL_DUAL_WIELD; + dbc_node.step = 1; dbc_node.value = 1; - else - dbc_node.value = dbc_node.step * 75; - dbc_node.maxvalue = dbc_node.step * 75; - mSpellLearnSkills[spell] = dbc_node; - ++dbc_count; - break; + dbc_node.maxvalue = 1; + break; + default: + continue; } + + mSpellLearnSkills[entry->Id] = dbc_node; + ++dbc_count; + break; } } @@ -1842,224 +1405,301 @@ void SpellMgr::LoadSpellGroupStackRules() TC_LOG_INFO("server.loading", ">> Loaded %u spell group stack rules in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } -void SpellMgr::LoadSpellProcEvents() +void SpellMgr::LoadSpellProcs() { uint32 oldMSTime = getMSTime(); - mSpellProcEventMap.clear(); // need for reload case + mSpellProcMap.clear(); // need for reload case - // 0 1 2 3 4 5 6 7 8 9 10 - QueryResult result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event"); - if (!result) - { - TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc event conditions. DB table `spell_proc_event` is empty."); - return; - } + // 0 1 2 3 4 5 + QueryResult result = WorldDatabase.Query("SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, " + // 6 7 8 9 10 11 12 13 14 + "ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc"); uint32 count = 0; - - do + if (result) { - Field* fields = result->Fetch(); - - int32 spellId = fields[0].GetInt32(); - - bool allRanks = false; - if (spellId < 0) - { - allRanks = true; - spellId = -spellId; - } - - SpellInfo const* spellInfo = GetSpellInfo(spellId); - if (!spellInfo) + do { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` does not exist.", spellId); - continue; - } + Field* fields = result->Fetch(); - if (allRanks) - { - if (!spellInfo->IsRanked()) - TC_LOG_ERROR("sql.sql", "The spell %u is listed in `spell_proc_event` with all ranks, but spell has no ranks.", spellId); + int32 spellId = fields[0].GetInt32(); - if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId)) + bool allRanks = false; + if (spellId < 0) { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` is not first rank of spell.", spellId); - continue; + allRanks = true; + spellId = -spellId; } - } - - SpellProcEventEntry spellProcEvent; - spellProcEvent.schoolMask = fields[1].GetInt8(); - spellProcEvent.spellFamilyName = fields[2].GetUInt16(); - spellProcEvent.spellFamilyMask[0] = fields[3].GetUInt32(); - spellProcEvent.spellFamilyMask[1] = fields[4].GetUInt32(); - spellProcEvent.spellFamilyMask[2] = fields[5].GetUInt32(); - spellProcEvent.procFlags = fields[6].GetUInt32(); - spellProcEvent.procEx = fields[7].GetUInt32(); - spellProcEvent.ppmRate = fields[8].GetFloat(); - spellProcEvent.customChance = fields[9].GetFloat(); - spellProcEvent.cooldown = fields[10].GetUInt32(); - - while (spellInfo) - { - if (mSpellProcEventMap.find(spellInfo->Id) != mSpellProcEventMap.end()) + SpellInfo const* spellInfo = GetSpellInfo(spellId); + if (!spellInfo) { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` already has its first rank in table.", spellInfo->Id); - break; + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` does not exist", spellId); + continue; } - if (!spellInfo->ProcFlags && !spellProcEvent.procFlags) - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` is probably not a triggered spell.", spellInfo->Id); - - mSpellProcEventMap[spellInfo->Id] = spellProcEvent; - if (allRanks) - spellInfo = spellInfo->GetNextRankSpell(); - else - break; - } + { + if (!spellInfo->IsRanked()) + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` with all ranks, but spell has no ranks.", spellId); - ++count; - } - while (result->NextRow()); + if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId)) + { + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` is not the first rank of the spell.", spellId); + continue; + } + } - TC_LOG_INFO("server.loading", ">> Loaded %u extra spell proc event conditions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); -} + SpellProcEntry baseProcEntry; + + baseProcEntry.SchoolMask = fields[1].GetInt8(); + baseProcEntry.SpellFamilyName = fields[2].GetUInt16(); + baseProcEntry.SpellFamilyMask[0] = fields[3].GetUInt32(); + baseProcEntry.SpellFamilyMask[1] = fields[4].GetUInt32(); + baseProcEntry.SpellFamilyMask[2] = fields[5].GetUInt32(); + baseProcEntry.ProcFlags = fields[6].GetUInt32(); + baseProcEntry.SpellTypeMask = fields[7].GetUInt32(); + baseProcEntry.SpellPhaseMask = fields[8].GetUInt32(); + baseProcEntry.HitMask = fields[9].GetUInt32(); + baseProcEntry.AttributesMask = fields[10].GetUInt32(); + baseProcEntry.ProcsPerMinute = fields[11].GetFloat(); + baseProcEntry.Chance = fields[12].GetFloat(); + baseProcEntry.Cooldown = Milliseconds(fields[13].GetUInt32()); + baseProcEntry.Charges = fields[14].GetUInt8(); + + while (spellInfo) + { + if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end()) + { + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` already has its first rank in the table.", spellInfo->Id); + break; + } -void SpellMgr::LoadSpellProcs() -{ - uint32 oldMSTime = getMSTime(); + SpellProcEntry procEntry = SpellProcEntry(baseProcEntry); + + // take defaults from dbcs + if (!procEntry.ProcFlags) + procEntry.ProcFlags = spellInfo->ProcFlags; + if (!procEntry.Charges) + procEntry.Charges = spellInfo->ProcCharges; + if (!procEntry.Chance && !procEntry.ProcsPerMinute) + procEntry.Chance = float(spellInfo->ProcChance); + + // validate data + if (procEntry.SchoolMask & ~SPELL_SCHOOL_MASK_ALL) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SchoolMask` set: %u", spellInfo->Id, procEntry.SchoolMask); + if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName < SPELLFAMILY_MAGE || procEntry.SpellFamilyName > SPELLFAMILY_PET || procEntry.SpellFamilyName == 14 || procEntry.SpellFamilyName == 16)) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellFamilyName` set: %u", spellInfo->Id, procEntry.SpellFamilyName); + if (procEntry.Chance < 0) + { + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `Chance` field", spellInfo->Id); + procEntry.Chance = 0; + } + if (procEntry.ProcsPerMinute < 0) + { + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ProcsPerMinute` field", spellInfo->Id); + procEntry.ProcsPerMinute = 0; + } + if (!procEntry.ProcFlags) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `ProcFlags` value defined, proc will not be triggered.", spellInfo->Id); + if (procEntry.SpellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellTypeMask` set: %u", spellInfo->Id, procEntry.SpellTypeMask); + if (procEntry.SpellTypeMask && !(procEntry.ProcFlags & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `SpellTypeMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); + if (!procEntry.SpellPhaseMask && procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `SpellPhaseMask` value defined, but it is required for the defined `ProcFlags` value. Proc will not be triggered.", spellInfo->Id); + if (procEntry.SpellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `SpellPhaseMask` set: %u", spellInfo->Id, procEntry.SpellPhaseMask); + if (procEntry.SpellPhaseMask && !(procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK)) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `SpellPhaseMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); + if (procEntry.HitMask & ~PROC_HIT_MASK_ALL) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `HitMask` set: %u", spellInfo->Id, procEntry.HitMask); + if (procEntry.HitMask && !(procEntry.ProcFlags & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.ProcFlags & DONE_HIT_PROC_FLAG_MASK && (!procEntry.SpellPhaseMask || procEntry.SpellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH))))) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `HitMask` value defined, but it will not be used for defined `ProcFlags` and `SpellPhaseMask` values.", spellInfo->Id); + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if ((procEntry.AttributesMask & (PROC_ATTR_DISABLE_EFF_0 << i)) && !spellInfo->Effects[i].IsAura()) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has Attribute PROC_ATTR_DISABLE_EFF_%u, but effect %u is not an aura effect", spellInfo->Id, static_cast<uint32>(i), static_cast<uint32>(i)); + + mSpellProcMap[spellInfo->Id] = procEntry; + + if (allRanks) + spellInfo = spellInfo->GetNextRankSpell(); + else + break; + } + ++count; + } while (result->NextRow()); + } + else + TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty."); - mSpellProcMap.clear(); // need for reload case + TC_LOG_INFO("server.loading", ">> Loaded %u spell proc conditions and data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); - // 0 1 2 3 4 5 - QueryResult result = WorldDatabase.Query("SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, " - // 6 7 8 9 10 11 12 13 14 - "ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc"); + // 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; + oldMSTime = getMSTime(); - if (!result) + for (SpellInfo const* spellInfo : mSpellInfoMap) { - TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty."); - return; - } + if (!spellInfo) + continue; - uint32 count = 0; - do - { - Field* fields = result->Fetch(); + // Data already present in DB, overwrites default proc + if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end()) + continue; - int32 spellId = fields[0].GetInt32(); + // Nothing to do if no flags set + if (!spellInfo->ProcFlags) + continue; - bool allRanks = false; - if (spellId < 0) + bool addTriggerFlag = false; + uint32 procSpellTypeMask = PROC_SPELL_TYPE_NONE; + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - allRanks = true; - spellId = -spellId; - } + if (!spellInfo->Effects[i].IsEffect()) + continue; - SpellInfo const* spellInfo = GetSpellInfo(spellId); - if (!spellInfo) - { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` does not exist", spellId); - continue; - } + uint32 auraName = spellInfo->Effects[i].ApplyAuraName; + if (!auraName) + continue; - if (allRanks) - { - if (!spellInfo->IsRanked()) - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` with all ranks, but spell has no ranks.", spellId); + if (!isTriggerAura[auraName]) + continue; + + procSpellTypeMask |= spellTypeMask[auraName]; + if (isAlwaysTriggeredAura[auraName]) + addTriggerFlag = true; - if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId)) + // 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) { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` is not the first rank of the spell.", spellId); - continue; + switch (auraName) + { + case SPELL_AURA_PROC_TRIGGER_SPELL: + case SPELL_AURA_PROC_TRIGGER_DAMAGE: + addTriggerFlag = true; + break; + default: + break; + } } + break; } - SpellProcEntry baseProcEntry; + if (!procSpellTypeMask) + continue; + + SpellProcEntry procEntry; + procEntry.SchoolMask = 0; + 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; + + if (procEntry.SpellFamilyMask) + procEntry.SpellFamilyName = spellInfo->SpellFamilyName; - baseProcEntry.SchoolMask = fields[1].GetInt8(); - baseProcEntry.SpellFamilyName = fields[2].GetUInt16(); - baseProcEntry.SpellFamilyMask[0] = fields[3].GetUInt32(); - baseProcEntry.SpellFamilyMask[1] = fields[4].GetUInt32(); - baseProcEntry.SpellFamilyMask[2] = fields[5].GetUInt32(); - baseProcEntry.ProcFlags = fields[6].GetUInt32(); - baseProcEntry.SpellTypeMask = fields[7].GetUInt32(); - baseProcEntry.SpellPhaseMask = fields[8].GetUInt32(); - baseProcEntry.HitMask = fields[9].GetUInt32(); - baseProcEntry.AttributesMask = fields[10].GetUInt32(); - baseProcEntry.ProcsPerMinute = fields[11].GetFloat(); - baseProcEntry.Chance = fields[12].GetFloat(); - baseProcEntry.Cooldown = Milliseconds(fields[13].GetUInt32()); - baseProcEntry.Charges = fields[14].GetUInt8(); + procEntry.SpellTypeMask = procSpellTypeMask; + procEntry.SpellPhaseMask = PROC_SPELL_PHASE_HIT; + procEntry.HitMask = PROC_HIT_NONE; // uses default proc @see SpellMgr::CanSpellTriggerProcOnEvent - while (spellInfo) + // Reflect auras should only proc off reflects + for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end()) + if (spellInfo->Effects[i].IsAura(SPELL_AURA_REFLECT_SPELLS) || spellInfo->Effects[i].IsAura(SPELL_AURA_REFLECT_SPELLS_SCHOOL)) { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` already has its first rank in the table.", spellInfo->Id); + procEntry.HitMask = PROC_HIT_REFLECT; break; } + } - SpellProcEntry procEntry = SpellProcEntry(baseProcEntry); - - // take defaults from dbcs - if (!procEntry.ProcFlags) - procEntry.ProcFlags = spellInfo->ProcFlags; - if (!procEntry.Charges) - procEntry.Charges = spellInfo->ProcCharges; - if (!procEntry.Chance && !procEntry.ProcsPerMinute) - procEntry.Chance = float(spellInfo->ProcChance); - - // validate data - if (procEntry.SchoolMask & ~SPELL_SCHOOL_MASK_ALL) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SchoolMask` set: %u", spellInfo->Id, procEntry.SchoolMask); - if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName < 3 || procEntry.SpellFamilyName > 17 || procEntry.SpellFamilyName == 14 || procEntry.SpellFamilyName == 16)) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellFamilyName` set: %u", spellInfo->Id, procEntry.SpellFamilyName); - if (procEntry.Chance < 0) - { - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `Chance` field", spellInfo->Id); - procEntry.Chance = 0; - } - if (procEntry.ProcsPerMinute < 0) - { - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ProcsPerMinute` field", spellInfo->Id); - procEntry.ProcsPerMinute = 0; - } - if (procEntry.Chance == 0 && procEntry.ProcsPerMinute == 0) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u doesn't have any `Chance` and `ProcsPerMinute` values defined, proc will not be triggered", spellInfo->Id); - if (!procEntry.ProcFlags) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `ProcFlags` value defined, proc will not be triggered.", spellInfo->Id); - if (procEntry.SpellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellTypeMask` set: %u", spellInfo->Id, procEntry.SpellTypeMask); - if (procEntry.SpellTypeMask && !(procEntry.ProcFlags & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `SpellTypeMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); - if (!procEntry.SpellPhaseMask && procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `SpellPhaseMask` value defined, but it is required for the defined `ProcFlags` value. Proc will not be triggered.", spellInfo->Id); - if (procEntry.SpellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `SpellPhaseMask` set: %u", spellInfo->Id, procEntry.SpellPhaseMask); - if (procEntry.SpellPhaseMask && !(procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK)) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `SpellPhaseMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); - if (procEntry.HitMask & ~PROC_HIT_MASK_ALL) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `HitMask` set: %u", spellInfo->Id, procEntry.HitMask); - if (procEntry.HitMask && !(procEntry.ProcFlags & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.ProcFlags & DONE_HIT_PROC_FLAG_MASK && (!procEntry.SpellPhaseMask || procEntry.SpellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH))))) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `HitMask` value defined, but it will not be used for defined `ProcFlags` and `SpellPhaseMask` values.", spellInfo->Id); - - mSpellProcMap[spellInfo->Id] = procEntry; + procEntry.AttributesMask = 0; + if (spellInfo->ProcFlags & PROC_FLAG_KILL) + procEntry.AttributesMask |= PROC_ATTR_REQ_EXP_OR_HONOR; + if (addTriggerFlag) + procEntry.AttributesMask |= PROC_ATTR_TRIGGERED_CAN_PROC; - if (allRanks) - spellInfo = spellInfo->GetNextRankSpell(); - else - break; - } + procEntry.ProcsPerMinute = 0; + procEntry.Chance = spellInfo->ProcChance; + procEntry.Cooldown = Milliseconds::zero(); + procEntry.Charges = spellInfo->ProcCharges; + + mSpellProcMap[spellInfo->Id] = procEntry; ++count; } - while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u spell proc conditions and data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Generated spell proc data for %u spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } void SpellMgr::LoadSpellBonusess() @@ -2951,12 +2591,6 @@ void SpellMgr::LoadSpellInfoCorrections() case 63137: // Force Cast (HACK: Target shouldn't be changed; summon position should be untied from spell destination) spellInfo->Effects[0].TargetA = SpellImplicitTargetInfo(TARGET_DEST_DB); break; - case 53096: // Quetz'lun's Judgment - case 70743: // AoD Special - case 70614: // AoD Special - Vegard - case 4020: // Safirdrang's Chill - spellInfo->MaxAffectedTargets = 1; - break; case 42436: // Drink! (Brewfest) spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_TARGET_ANY); break; @@ -2984,6 +2618,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; @@ -3011,6 +2647,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: @@ -3034,7 +2679,7 @@ void SpellMgr::LoadSpellInfoCorrections() case 59725: // Improved Spell Reflection - aoe aura // Target entry seems to be wrong for this spell :/ spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CASTER_AREA_PARTY); - spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_10_YARDS_2); + spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_20_YARDS); break; case 44978: // Wild Magic case 45001: @@ -3061,6 +2706,14 @@ void SpellMgr::LoadSpellInfoCorrections() case 36327: // Shoot Arcane Explosion Arrow case 55479: // Force Obedience case 28560: // Summon Blizzard (Sapphiron) + case 53096: // Quetz'lun's Judgment + case 70743: // AoD Special + case 70614: // AoD Special - Vegard + case 4020: // Safirdrang's Chill + case 52438: // Summon Skittering Swarmer (Force Cast) + case 52449: // Summon Skittering Infector (Force Cast) + case 53609: // Summon Anub'ar Assassin (Force Cast) + case 53457: // Summon Impale Trigger (AoE) spellInfo->MaxAffectedTargets = 1; break; case 36384: // Skartax Purple Beam @@ -3085,6 +2738,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; @@ -3119,36 +2778,17 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->MaxAffectedTargets = 1; spellInfo->Effects[EFFECT_0].TriggerSpell = 33760; break; - case 17941: // Shadow Trance - case 22008: // Netherwind Focus - case 31834: // Light's Grace - case 34936: // Backlash - case 48108: // Hot Streak - case 51124: // Killing Machine - case 54741: // Firestarter - case 57761: // Fireball! - case 64823: // Item - Druid T8 Balance 4P Bonus - case 34477: // Misdirection - case 44401: // Missile Barrage - case 18820: // Insight - spellInfo->ProcCharges = 1; - break; case 44544: // Fingers of Frost spellInfo->Effects[EFFECT_0].SpellClassMask = flag96(685904631, 1151048, 0); break; - case 74396: // Fingers of Frost visual buff - case 53257: // Cobra Strikes - spellInfo->ProcCharges = 2; - spellInfo->StackAmount = 0; - break; - case 28200: // Ascendance (Talisman of Ascendance trinket) - spellInfo->ProcCharges = 6; - 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; @@ -3228,6 +2868,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 @@ -3335,9 +2988,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 @@ -3375,6 +3025,24 @@ void SpellMgr::LoadSpellInfoCorrections() case 29726: // Test Ribbon Pole Channel 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) @@ -3822,6 +3490,55 @@ void SpellMgr::LoadSpellInfoCorrections() properties->Type = SUMMON_TYPE_TOTEM; if (SummonPropertiesEntry* properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(647))) // 52893 properties->Type = SUMMON_TYPE_TOTEM; + if (SummonPropertiesEntry* properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(628))) // Hungry Plaguehound + properties->Category = SUMMON_CATEGORY_PET; TC_LOG_INFO("server.loading", ">> Loaded SpellInfo corrections in %u ms", GetMSTimeDiffToNow(oldMSTime)); } + +void SpellMgr::LoadSpellInfoSpellSpecificAndAuraState() +{ + uint32 oldMSTime = getMSTime(); + + for (SpellInfo* spellInfo : mSpellInfoMap) + { + if (!spellInfo) + continue; + + // AuraState depends on SpellSpecific + spellInfo->_LoadSpellSpecific(); + spellInfo->_LoadAuraState(); + } + + 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 23329c1ff1e..a08ff921a2e 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 @@ -194,43 +195,6 @@ enum ProcFlags PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS | \ PROC_FLAG_TAKEN_SPELL_RANGED_DMG_CLASS) -enum ProcFlagsExLegacy -{ - PROC_EX_NONE = 0x0000000, // If none can tigger on Hit/Crit only (passive spells MUST defined by SpellFamily flag) - PROC_EX_NORMAL_HIT = 0x0000001, // If set only from normal hit (only damage spells) - PROC_EX_CRITICAL_HIT = 0x0000002, - PROC_EX_MISS = 0x0000004, - PROC_EX_RESIST = 0x0000008, - PROC_EX_DODGE = 0x0000010, - PROC_EX_PARRY = 0x0000020, - PROC_EX_BLOCK = 0x0000040, - PROC_EX_EVADE = 0x0000080, - PROC_EX_IMMUNE = 0x0000100, - PROC_EX_DEFLECT = 0x0000200, - PROC_EX_ABSORB = 0x0000400, - PROC_EX_REFLECT = 0x0000800, - PROC_EX_INTERRUPT = 0x0001000, // Melee hit result can be Interrupt (not used) - PROC_EX_FULL_BLOCK = 0x0002000, // block al attack damage - PROC_EX_RESERVED2 = 0x0004000, - PROC_EX_NOT_ACTIVE_SPELL = 0x0008000, // Spell mustn't do damage/heal to proc - PROC_EX_EX_TRIGGER_ALWAYS = 0x0010000, // If set trigger always no matter of hit result - PROC_EX_EX_ONE_TIME_TRIGGER = 0x0020000, // If set trigger always but only one time (not implemented yet) - PROC_EX_ONLY_ACTIVE_SPELL = 0x0040000, // Spell has to do damage/heal to proc - - // Flags for internal use - do not use these in db! - PROC_EX_INTERNAL_CANT_PROC = 0x0800000, - PROC_EX_INTERNAL_DOT = 0x1000000, - PROC_EX_INTERNAL_HOT = 0x2000000, - PROC_EX_INTERNAL_TRIGGERED = 0x4000000, - PROC_EX_INTERNAL_REQ_FAMILY = 0x8000000 -}; - -#define AURA_SPELL_PROC_EX_MASK \ - (PROC_EX_NORMAL_HIT | PROC_EX_CRITICAL_HIT | PROC_EX_MISS | \ - PROC_EX_RESIST | PROC_EX_DODGE | PROC_EX_PARRY | PROC_EX_BLOCK | \ - PROC_EX_EVADE | PROC_EX_IMMUNE | PROC_EX_DEFLECT | \ - PROC_EX_ABSORB | PROC_EX_REFLECT | PROC_EX_INTERRUPT) - enum ProcFlagsSpellType { PROC_SPELL_TYPE_NONE = 0x0000000, @@ -271,23 +235,16 @@ enum ProcFlagsHit enum ProcAttributes { - PROC_ATTR_REQ_EXP_OR_HONOR = 0x0000010 + PROC_ATTR_REQ_EXP_OR_HONOR = 0x0000001, // requires proc target to give exp or honor for aura proc + PROC_ATTR_TRIGGERED_CAN_PROC = 0x0000002, // aura can proc even with triggered spells + PROC_ATTR_REQ_MANA_COST = 0x0000004, // requires triggering spell to have a mana cost for aura proc + PROC_ATTR_REQ_SPELLMOD = 0x0000008, // requires triggering spell to be affected by proccing aura to drop charges + + PROC_ATTR_DISABLE_EFF_0 = 0x0000010, // explicitly disables aura proc from effects, USE ONLY IF 100% SURE AURA SHOULDN'T PROC + PROC_ATTR_DISABLE_EFF_1 = 0x0000020, // used to avoid a console error if the spell has invalid trigger spell and handled elsewhere + PROC_ATTR_DISABLE_EFF_2 = 0x0000040 // or handling not needed }; -struct SpellProcEventEntry -{ - uint32 schoolMask; // if nonzero - bit mask for matching proc condition based on spell candidate's school: Fire=2, Mask=1<<(2-1)=2 - uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyNamer value - flag96 spellFamilyMask; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyFlags (like auras 107 and 108 do) - uint32 procFlags; // bitmask for matching proc event - uint32 procEx; // proc Extend info (see ProcFlagsEx) - float ppmRate; // for melee (ranged?) damage spells - proc rate per minute. if zero, falls back to flat chance from Spell.dbc - float customChance; // Owerride chance (in most cases for debug only) - uint32 cooldown; // hidden cooldown used for some spell proc events, applied to _triggered_spell_ -}; - -typedef std::unordered_map<uint32, SpellProcEventEntry> SpellProcEventMap; - struct SpellProcEntry { uint32 SchoolMask; // if nonzero - bitmask for matching proc condition based on spell's school @@ -545,7 +502,7 @@ struct SpellLearnSkillNode uint16 maxvalue; // 0 - max skill value for player level }; -typedef std::map<uint32, SpellLearnSkillNode> SpellLearnSkillMap; +typedef std::unordered_map<uint32, SpellLearnSkillNode> SpellLearnSkillMap; struct SpellLearnSpellNode { @@ -594,13 +551,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 @@ -658,13 +608,9 @@ class TC_GAME_API SpellMgr SpellGroupStackRule CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2) const; SpellGroupStackRule GetSpellGroupStackRule(SpellGroup groupid) const; - // Spell proc event table - SpellProcEventEntry const* GetSpellProcEvent(uint32 spellId) const; - bool IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellInfo const* procSpell, uint32 procFlags, uint32 procExtra, bool active) const; - // Spell proc table SpellProcEntry const* GetSpellProcEntry(uint32 spellId) const; - bool CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; + static bool CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo); // Spell bonus data table SpellBonusEntry const* GetSpellBonusData(uint32 spellId) const; @@ -720,7 +666,6 @@ class TC_GAME_API SpellMgr void LoadSpellTargetPositions(); void LoadSpellGroups(); void LoadSpellGroupStackRules(); - void LoadSpellProcEvents(); void LoadSpellProcs(); void LoadSpellBonusess(); void LoadSpellThreats(); @@ -737,6 +682,9 @@ class TC_GAME_API SpellMgr void UnloadSpellInfoImplicitTargetConditionLists(); void LoadSpellInfoCustomAttributes(); void LoadSpellInfoCorrections(); + void LoadSpellInfoSpellSpecificAndAuraState(); + void LoadSpellInfoDiminishing(); + void LoadSpellInfoImmunities(); private: SpellDifficultySearcherMap mSpellDifficultySearcherMap; @@ -749,7 +697,6 @@ class TC_GAME_API SpellMgr SpellSpellGroupMap mSpellSpellGroup; SpellGroupSpellMap mSpellGroupSpell; SpellGroupStackMap mSpellGroupStack; - SpellProcEventMap mSpellProcEventMap; SpellProcMap mSpellProcMap; SpellBonusMap mSpellBonusMap; SpellThreatMap mSpellThreatMap; diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp index e2598386466..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,95 +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<AuraProcHandler>::iterator itr = DoPrepareProc.begin(); itr != DoPrepareProc.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 (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()); @@ -887,6 +891,17 @@ bool AuraScript::CheckProcHandler::Call(AuraScript* auraScript, ProcEventInfo& e return (auraScript->*_HandlerScript)(eventInfo); } +AuraScript::CheckEffectProcHandler::CheckEffectProcHandler(AuraCheckEffectProcFnType handlerScript, uint8 effIndex, uint16 effName) + : AuraScript::EffectBase(effIndex, effName) +{ + _HandlerScript = handlerScript; +} + +bool AuraScript::CheckEffectProcHandler::Call(AuraScript* auraScript, AuraEffect const* aurEff, ProcEventInfo& eventInfo) +{ + return (auraScript->*_HandlerScript)(aurEff, eventInfo); +} + AuraScript::AuraProcHandler::AuraProcHandler(AuraProcFnType handlerScript) { _HandlerScript = handlerScript; @@ -1148,6 +1163,7 @@ Unit* AuraScript::GetTarget() const case AURA_SCRIPT_HOOK_EFFECT_AFTER_MANASHIELD: case AURA_SCRIPT_HOOK_EFFECT_SPLIT: case AURA_SCRIPT_HOOK_CHECK_PROC: + case AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC: case AURA_SCRIPT_HOOK_PREPARE_PROC: case AURA_SCRIPT_HOOK_PROC: case AURA_SCRIPT_HOOK_AFTER_PROC: diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index 539bc54cc94..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); }; @@ -468,6 +468,7 @@ enum AuraScriptHookType AURA_SCRIPT_HOOK_AFTER_DISPEL, // Spell Proc Hooks AURA_SCRIPT_HOOK_CHECK_PROC, + AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC, AURA_SCRIPT_HOOK_PREPARE_PROC, AURA_SCRIPT_HOOK_PROC, AURA_SCRIPT_HOOK_EFFECT_PROC, @@ -499,6 +500,7 @@ class TC_GAME_API AuraScript : public _SpellScript typedef void(CLASSNAME::*AuraEffectAbsorbFnType)(AuraEffect*, DamageInfo &, uint32 &); \ typedef void(CLASSNAME::*AuraEffectSplitFnType)(AuraEffect*, DamageInfo &, uint32 &); \ typedef bool(CLASSNAME::*AuraCheckProcFnType)(ProcEventInfo&); \ + typedef bool(CLASSNAME::*AuraCheckEffectProcFnType)(AuraEffect const*, ProcEventInfo&); \ typedef void(CLASSNAME::*AuraProcFnType)(ProcEventInfo&); \ typedef void(CLASSNAME::*AuraEffectProcFnType)(AuraEffect const*, ProcEventInfo&); \ @@ -608,6 +610,14 @@ class TC_GAME_API AuraScript : public _SpellScript private: AuraCheckProcFnType _HandlerScript; }; + class TC_GAME_API CheckEffectProcHandler : public EffectBase + { + public: + CheckEffectProcHandler(AuraCheckEffectProcFnType handlerScript, uint8 effIndex, uint16 effName); + bool Call(AuraScript* auraScript, AuraEffect const* aurEff, ProcEventInfo& eventInfo); + private: + AuraCheckEffectProcFnType _HandlerScript; + }; class TC_GAME_API AuraProcHandler { public: @@ -638,6 +648,7 @@ class TC_GAME_API AuraScript : public _SpellScript class EffectManaShieldFunction : public AuraScript::EffectManaShieldHandler { public: EffectManaShieldFunction(AuraEffectAbsorbFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectManaShieldHandler((AuraScript::AuraEffectAbsorbFnType)_pEffectHandlerScript, _effIndex) { } }; \ class EffectSplitFunction : public AuraScript::EffectSplitHandler { public: EffectSplitFunction(AuraEffectSplitFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectSplitHandler((AuraScript::AuraEffectSplitFnType)_pEffectHandlerScript, _effIndex) { } }; \ class CheckProcHandlerFunction : public AuraScript::CheckProcHandler { public: CheckProcHandlerFunction(AuraCheckProcFnType handlerScript) : AuraScript::CheckProcHandler((AuraScript::AuraCheckProcFnType)handlerScript) { } }; \ + class CheckEffectProcHandlerFunction : public AuraScript::CheckEffectProcHandler { public: CheckEffectProcHandlerFunction(AuraCheckEffectProcFnType handlerScript, uint8 effIndex, uint16 effName) : AuraScript::CheckEffectProcHandler((AuraScript::AuraCheckEffectProcFnType)handlerScript, effIndex, effName) { } }; \ class AuraProcHandlerFunction : public AuraScript::AuraProcHandler { public: AuraProcHandlerFunction(AuraProcFnType handlerScript) : AuraScript::AuraProcHandler((AuraScript::AuraProcFnType)handlerScript) { } }; \ class EffectProcHandlerFunction : public AuraScript::EffectProcHandler { public: EffectProcHandlerFunction(AuraEffectProcFnType effectHandlerScript, uint8 effIndex, uint16 effName) : AuraScript::EffectProcHandler((AuraScript::AuraEffectProcFnType)effectHandlerScript, effIndex, effName) { } } @@ -776,6 +787,12 @@ class TC_GAME_API AuraScript : public _SpellScript HookList<CheckProcHandler> DoCheckProc; #define AuraCheckProcFn(F) CheckProcHandlerFunction(&F) + // executed when aura effect checks if it can proc the aura + // example: DoCheckEffectProc += AuraCheckEffectProcFn(class::function, EffectIndexSpecifier, EffectAuraNameSpecifier); + // where function is bool function (AuraEffect const* aurEff, ProcEventInfo& eventInfo); + HookList<CheckEffectProcHandler> DoCheckEffectProc; + #define AuraCheckEffectProcFn(F, I, N) CheckEffectProcHandlerFunction(&F, I, N) + // executed before aura procs (possibility to prevent charge drop/cooldown) // example: DoPrepareProc += AuraProcFn(class::function); // where function is: void function (ProcEventInfo& eventInfo); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index de605ee9f2d..7bbf73d028d 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -872,6 +872,7 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_CAST_UNSTUCK] = sConfigMgr->GetBoolDefault("CastUnstuck", true); m_int_configs[CONFIG_INSTANCE_RESET_TIME_HOUR] = sConfigMgr->GetIntDefault("Instance.ResetTimeHour", 4); m_int_configs[CONFIG_INSTANCE_UNLOAD_DELAY] = sConfigMgr->GetIntDefault("Instance.UnloadDelay", 30 * MINUTE * IN_MILLISECONDS); + m_int_configs[CONFIG_DAILY_QUEST_RESET_TIME_HOUR] = sConfigMgr->GetIntDefault("Quests.DailyResetTime", 3); m_int_configs[CONFIG_MAX_PRIMARY_TRADE_SKILL] = sConfigMgr->GetIntDefault("MaxPrimaryTradeSkill", 2); m_int_configs[CONFIG_MIN_PETITION_SIGNS] = sConfigMgr->GetIntDefault("MinPetitionSigns", 9); @@ -1448,6 +1449,12 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading SpellInfo custom attributes..."); sSpellMgr->LoadSpellInfoCustomAttributes(); + 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); @@ -1507,12 +1514,12 @@ 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(); - TC_LOG_INFO("server.loading", "Loading Spell Proc Event conditions..."); - sSpellMgr->LoadSpellProcEvents(); - TC_LOG_INFO("server.loading", "Loading Spell Proc conditions and data..."); sSpellMgr->LoadSpellProcs(); @@ -2103,7 +2110,7 @@ void World::Update(uint32 diff) if (m_gameTime > m_NextDailyQuestReset) { ResetDailyQuests(); - m_NextDailyQuestReset += DAY; + InitDailyQuestResetTime(false); } /// Handle weekly quests reset time @@ -2934,25 +2941,25 @@ void World::InitWeeklyQuestResetTime() m_NextWeeklyQuestReset = wstime < curtime ? curtime : time_t(wstime); } -void World::InitDailyQuestResetTime() +void World::InitDailyQuestResetTime(bool loading) { - time_t mostRecentQuestTime; + time_t mostRecentQuestTime = 0; - QueryResult result = CharacterDatabase.Query("SELECT MAX(time) FROM character_queststatus_daily"); - if (result) + if (loading) { - Field* fields = result->Fetch(); - mostRecentQuestTime = time_t(fields[0].GetUInt32()); + QueryResult result = CharacterDatabase.Query("SELECT MAX(time) FROM character_queststatus_daily"); + if (result) + { + Field* fields = result->Fetch(); + mostRecentQuestTime = time_t(fields[0].GetUInt32()); + } } - else - mostRecentQuestTime = 0; - // client built-in time for reset is 6:00 AM // FIX ME: client not show day start time time_t curTime = time(NULL); tm localTm; localtime_r(&curTime, &localTm); - localTm.tm_hour = 6; + localTm.tm_hour = getIntConfig(CONFIG_DAILY_QUEST_RESET_TIME_HOUR); localTm.tm_min = 0; localTm.tm_sec = 0; diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 4dcb07e2f9c..8d6182887c0 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -242,6 +242,7 @@ enum WorldIntConfigs CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL_DIFFERENCE, CONFIG_INSTANCE_RESET_TIME_HOUR, CONFIG_INSTANCE_UNLOAD_DELAY, + CONFIG_DAILY_QUEST_RESET_TIME_HOUR, CONFIG_MAX_PRIMARY_TRADE_SKILL, CONFIG_MIN_PETITION_SIGNS, CONFIG_GM_LOGIN_STATE, @@ -783,7 +784,7 @@ class TC_GAME_API World // callback for UpdateRealmCharacters void _UpdateRealmCharCount(PreparedQueryResult resultCharCount); - void InitDailyQuestResetTime(); + void InitDailyQuestResetTime(bool loading = true); void InitWeeklyQuestResetTime(); void InitMonthlyQuestResetTime(); void InitRandomBGResetTime(); 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_guild.cpp b/src/server/scripts/Commands/cs_guild.cpp index 98fc852b573..8c4bf73395d 100644 --- a/src/server/scripts/Commands/cs_guild.cpp +++ b/src/server/scripts/Commands/cs_guild.cpp @@ -146,7 +146,8 @@ public: return false; // player's guild membership checked in AddMember before add - return targetGuild->AddMember(targetGuid); + SQLTransaction trans(nullptr); + return targetGuild->AddMember(trans, targetGuid); } static bool HandleGuildUninviteCommand(ChatHandler* handler, char const* args) @@ -164,7 +165,8 @@ public: if (!targetGuild) return false; - targetGuild->DeleteMember(targetGuid, false, true, true); + SQLTransaction trans(nullptr); + targetGuild->DeleteMember(trans, targetGuid, false, true, true); return true; } @@ -191,7 +193,8 @@ public: return false; uint8 newRank = uint8(atoi(rankStr)); - return targetGuild->ChangeMemberRank(targetGuid, newRank); + SQLTransaction trans(nullptr); + return targetGuild->ChangeMemberRank(trans, targetGuid, newRank); } static bool HandleGuildRenameCommand(ChatHandler* handler, char const* _args) diff --git a/src/server/scripts/Commands/cs_message.cpp b/src/server/scripts/Commands/cs_message.cpp index 69ff04ffb46..4b3caae686b 100644 --- a/src/server/scripts/Commands/cs_message.cpp +++ b/src/server/scripts/Commands/cs_message.cpp @@ -24,6 +24,7 @@ EndScriptData */ #include "ScriptMgr.h" #include "Chat.h" +#include "Channel.h" #include "ChannelMgr.h" #include "Language.h" #include "Player.h" @@ -63,21 +64,49 @@ public: if (!*args) return false; char const* channelStr = strtok((char*)args, " "); - char const* argStr = strtok(NULL, ""); + char const* argStr = strtok(nullptr, ""); if (!channelStr || !argStr) return false; + uint32 channelId = 0; + for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) + { + ChatChannelsEntry const* entry = sChatChannelsStore.LookupEntry(i); + if (!entry) + continue; + + if (strstr(entry->pattern[handler->GetSessionDbcLocale()], channelStr)) + { + channelId = i; + break; + } + } + + AreaTableEntry const* zoneEntry = nullptr; + for (uint32 i = 0; i < sAreaTableStore.GetNumRows(); ++i) + { + AreaTableEntry const* entry = sAreaTableStore.LookupEntry(i); + if (!entry) + continue; + + if (strstr(entry->area_name[handler->GetSessionDbcLocale()], channelStr)) + { + zoneEntry = entry; + break; + } + } + Player* player = handler->GetSession()->GetPlayer(); - Channel* channcel = NULL; + Channel* channel = nullptr; if (ChannelMgr* cMgr = ChannelMgr::forTeam(player->GetTeam())) - channcel = cMgr->GetChannel(channelStr, player); + channel = cMgr->GetChannel(channelId, channelStr, player, false, zoneEntry); if (strcmp(argStr, "on") == 0) { - if (channcel) - channcel->SetOwnership(true); + if (channel) + channel->SetOwnership(true); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL_OWNERSHIP); stmt->setUInt8 (0, 1); stmt->setString(1, channelStr); @@ -86,8 +115,8 @@ public: } else if (strcmp(argStr, "off") == 0) { - if (channcel) - channcel->SetOwnership(false); + if (channel) + channel->SetOwnership(false); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL_OWNERSHIP); stmt->setUInt8 (0, 0); stmt->setString(1, channelStr); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 16217fbaea6..5487b9c7b2f 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; } @@ -2288,10 +2289,22 @@ public: // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form uint32 spellid = handler->extractSpellIdFromLink((char*)args); - if (!spellid || !sSpellMgr->GetSpellInfo(spellid)) + if (!spellid) + return false; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid); + if (!spellInfo) return false; - handler->GetSession()->GetPlayer()->SpellNonMeleeDamageLog(target, spellid, damage); + Player* attacker = handler->GetSession()->GetPlayer(); + SpellNonMeleeDamage dmgInfo(attacker, target, spellid, spellInfo->GetSchoolMask()); + damage = attacker->SpellDamageBonusDone(target, spellInfo, damage, SPELL_DIRECT_DAMAGE); + damage = target->SpellDamageBonusTaken(attacker, spellInfo, damage, SPELL_DIRECT_DAMAGE); + + attacker->CalculateSpellDamageTaken(&dmgInfo, damage, spellInfo); + attacker->DealDamageMods(dmgInfo.target, dmgInfo.damage, &dmgInfo.absorb); + attacker->SendSpellNonMeleeDamageLog(&dmgInfo); + attacker->DealSpellDamage(&dmgInfo, true); return true; } diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 4470fa7de42..f44b66c7498 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -144,7 +144,6 @@ public: { "spell_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesSpellCommand, "" }, { "spell_linked_spell", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_LINKED_SPELL, true, &HandleReloadSpellLinkedSpellCommand, "" }, { "spell_pet_auras", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PET_AURAS, true, &HandleReloadSpellPetAurasCommand, "" }, - { "spell_proc_event", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PROC_EVENT, true, &HandleReloadSpellProcEventCommand, "" }, { "spell_proc", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PROC, true, &HandleReloadSpellProcsCommand, "" }, { "spell_scripts", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_SCRIPTS, true, &HandleReloadSpellScriptsCommand, "" }, { "spell_target_position", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_TARGET_POSITION, true, &HandleReloadSpellTargetPositionCommand, "" }, @@ -276,7 +275,6 @@ public: HandleReloadSpellGroupsCommand(handler, "a"); HandleReloadSpellLearnSpellCommand(handler, "a"); HandleReloadSpellLinkedSpellCommand(handler, "a"); - HandleReloadSpellProcEventCommand(handler, "a"); HandleReloadSpellProcsCommand(handler, "a"); HandleReloadSpellBonusesCommand(handler, "a"); HandleReloadSpellTargetPositionCommand(handler, "a"); @@ -811,14 +809,6 @@ public: return true; } - static bool HandleReloadSpellProcEventCommand(ChatHandler* handler, const char* /*args*/) - { - TC_LOG_INFO("misc", "Re-Loading Spell Proc Event conditions..."); - sSpellMgr->LoadSpellProcEvents(); - handler->SendGlobalGMSysMessage("DB table `spell_proc_event` (spell proc trigger requirements) reloaded."); - return true; - } - static bool HandleReloadSpellProcsCommand(ChatHandler* handler, const char* /*args*/) { TC_LOG_INFO("misc", "Re-Loading Spell Proc conditions and data..."); 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/BattleForMountHyjal/boss_anetheron.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp index 5009cce48f4..722b7768617 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp @@ -17,17 +17,19 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" #include "hyjal.h" #include "hyjal_trash.h" enum Spells { - SPELL_CARRION_SWARM = 31306, - SPELL_SLEEP = 31298, - SPELL_VAMPIRIC_AURA = 38196, - SPELL_INFERNO = 31299, - SPELL_IMMOLATION = 31303, - SPELL_INFERNO_EFFECT = 31302, + SPELL_CARRION_SWARM = 31306, + SPELL_SLEEP = 31298, + SPELL_VAMPIRIC_AURA = 38196, + SPELL_VAMPIRIC_AURA_HEAL = 31285, + SPELL_INFERNO = 31299, + SPELL_IMMOLATION = 31303, + SPELL_INFERNO_EFFECT = 31302 }; enum Texts @@ -261,8 +263,48 @@ public: }; +class spell_anetheron_vampiric_aura : public SpellScriptLoader +{ + public: + spell_anetheron_vampiric_aura() : SpellScriptLoader("spell_anetheron_vampiric_aura") { } + + class spell_anetheron_vampiric_aura_AuraScript : public AuraScript + { + PrepareAuraScript(spell_anetheron_vampiric_aura_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_VAMPIRIC_AURA_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 bp = damageInfo->GetDamage() * 3; + eventInfo.GetActor()->CastCustomSpell(SPELL_VAMPIRIC_AURA_HEAL, SPELLVALUE_BASE_POINT0, bp, eventInfo.GetActor(), true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_anetheron_vampiric_aura_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_anetheron_vampiric_aura_AuraScript(); + } +}; + void AddSC_boss_anetheron() { new boss_anetheron(); new npc_towering_infernal(); + new spell_anetheron_vampiric_aura(); } 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_azuremyst_isle.cpp b/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp index c47971836ac..fdc9b0c12ff 100644 --- a/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp +++ b/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp @@ -331,7 +331,14 @@ enum Magwin SAY_END1 = 3, SAY_END2 = 4, EMOTE_HUG = 5, - QUEST_A_CRY_FOR_SAY_HELP = 9528, + NPC_COWLEN = 17311, + SAY_COWLEN = 0, + EVENT_ACCEPT_QUEST = 1, + EVENT_START_ESCORT = 2, + EVENT_STAND = 3, + EVENT_TALK_END = 4, + EVENT_COWLEN_TALK = 5, + QUEST_A_CRY_FOR_HELP = 9528, FACTION_QUEST = 113 }; @@ -344,7 +351,10 @@ public: { npc_magwinAI(Creature* creature) : npc_escortAI(creature) { } - void Reset() override { } + void Reset() override + { + _events.Reset(); + } void EnterCombat(Unit* who) override { @@ -353,10 +363,10 @@ public: void sQuestAccept(Player* player, Quest const* quest) override { - if (quest->GetQuestId() == QUEST_A_CRY_FOR_SAY_HELP) + if (quest->GetQuestId() == QUEST_A_CRY_FOR_HELP) { - me->setFaction(FACTION_QUEST); - npc_escortAI::Start(true, false, player->GetGUID()); + _player = player->GetGUID(); + _events.ScheduleEvent(EVENT_ACCEPT_QUEST, Seconds(2)); } } @@ -366,23 +376,63 @@ public: { switch (waypointId) { - case 0: - Talk(SAY_START, player); - break; case 17: Talk(SAY_PROGRESS, player); break; case 28: - Talk(SAY_END1, player); + player->GroupEventHappens(QUEST_A_CRY_FOR_HELP, me); + _events.ScheduleEvent(EVENT_TALK_END, Seconds(2)); + SetRun(true); break; case 29: - Talk(EMOTE_HUG, player); + if (Creature* cowlen = me->FindNearestCreature(NPC_COWLEN, 50.0f, true)) + Talk(EMOTE_HUG, cowlen); Talk(SAY_END2, player); - player->GroupEventHappens(QUEST_A_CRY_FOR_SAY_HELP, me); break; } } } + + void UpdateEscortAI(uint32 diff) override + { + _events.Update(diff); + + if (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_ACCEPT_QUEST: + if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) + Talk(SAY_START, player); + me->setFaction(FACTION_QUEST); + _events.ScheduleEvent(EVENT_START_ESCORT, Seconds(1)); + break; + case EVENT_START_ESCORT: + if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) + npc_escortAI::Start(true, false, player->GetGUID()); + _events.ScheduleEvent(EVENT_STAND, Seconds(2)); + break; + case EVENT_STAND: // Remove kneel standstate. Using a separate delayed event because it causes unwanted delay before starting waypoint movement. + me->SetByteValue(UNIT_FIELD_BYTES_1, 0, 0); + break; + case EVENT_TALK_END: + if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) + Talk(SAY_END1, player); + _events.ScheduleEvent(EVENT_COWLEN_TALK, Seconds(2)); + break; + case EVENT_COWLEN_TALK: + if (Creature* cowlen = me->FindNearestCreature(NPC_COWLEN, 50.0f, true)) + cowlen->AI()->Talk(SAY_COWLEN); + break; + } + } + + npc_escortAI::UpdateEscortAI(diff); + } + + private: + EventMap _events; + ObjectGuid _player; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp index a7b2b156128..3ca7954cd87 100644 --- a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp +++ b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp @@ -41,7 +41,7 @@ EndContentData */ ######*/ //possible creatures to be spawned -uint32 const possibleSpawns[32] = {17322, 17661, 17496, 17522, 17340, 17352, 17333, 17524, 17654, 17348, 17339, 17345, 17359, 17353, 17336, 17550, 17330, 17701, 17321, 17680, 17325, 17320, 17683, 17342, 17715, 17334, 17341, 17338, 17337, 17346, 17344, 17327}; +uint32 const possibleSpawns[31] = {17322, 17661, 17496, 17522, 17340, 17352, 17333, 17524, 17654, 17348, 17339, 17345, 17359, 17353, 17336, 17550, 17330, 17701, 17321, 17325, 17320, 17683, 17342, 17715, 17334, 17341, 17338, 17337, 17346, 17344, 17327}; enum WebbedCreature { @@ -74,6 +74,7 @@ public: case 0: if (Player* player = killer->ToPlayer()) player->KilledMonsterCredit(NPC_EXPEDITION_RESEARCHER); + spawnCreatureID = NPC_EXPEDITION_RESEARCHER; break; case 1: case 2: @@ -81,8 +82,7 @@ public: break; } - if (spawnCreatureID) - me->SummonCreature(spawnCreatureID, 0.0f, 0.0f, 0.0f, me->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); + me->SummonCreature(spawnCreatureID, 0.0f, 0.0f, 0.0f, me->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); } }; diff --git a/src/server/scripts/Kalimdor/zone_darkshore.cpp b/src/server/scripts/Kalimdor/zone_darkshore.cpp index 230e4e2aca5..de98addd7b0 100644 --- a/src/server/scripts/Kalimdor/zone_darkshore.cpp +++ b/src/server/scripts/Kalimdor/zone_darkshore.cpp @@ -316,7 +316,7 @@ public: enum Threshwackonator { EMOTE_START = 0, - SAY_AT_CLOSE = 1, + SAY_AT_CLOSE = 0, QUEST_GYROMAST_REV = 2078, NPC_GELKAK = 6667, FACTION_HOSTILE = 14 @@ -344,7 +344,7 @@ public: { if (me->IsWithinDistInMap(who, 10.0f)) { - Talk(SAY_AT_CLOSE, who); + who->ToCreature()->AI()->Talk(SAY_AT_CLOSE, who); DoAtEnd(); } } diff --git a/src/server/scripts/Kalimdor/zone_feralas.cpp b/src/server/scripts/Kalimdor/zone_feralas.cpp index 3c6ab633f66..c35081bfdcb 100644 --- a/src/server/scripts/Kalimdor/zone_feralas.cpp +++ b/src/server/scripts/Kalimdor/zone_feralas.cpp @@ -156,7 +156,8 @@ public: enum GordunniTrap { - GO_GORDUNNI_DIRT_MOUND = 144064, + GO_GORDUNNI_DIRT_MOUND_1 = 144064, + GO_GORDUNNI_DIRT_MOUND_2 = 177681 }; class spell_gordunni_trap : public SpellScriptLoader @@ -171,7 +172,7 @@ class spell_gordunni_trap : public SpellScriptLoader void HandleDummy() { Unit* caster = GetCaster(); - if (GameObject* chest = caster->SummonGameObject(GO_GORDUNNI_DIRT_MOUND, *caster, G3D::Quat(), 0)) + if (GameObject* chest = caster->SummonGameObject(urand(0, 1) ? GO_GORDUNNI_DIRT_MOUND_1 : GO_GORDUNNI_DIRT_MOUND_2, *caster, G3D::Quat(), 0)) { chest->SetSpellId(GetSpellInfo()->Id); caster->RemoveGameObject(chest, false); 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 cc454882154..2eda9509bb8 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h @@ -18,7 +18,6 @@ #ifndef AZJOL_NERUB_H_ #define AZJOL_NERUB_H_ -#define AzjolNerubScriptName "instance_azjol_nerub" #define DataHeader "AN" uint32 const EncounterCount = 3; @@ -26,14 +25,16 @@ uint32 const EncounterCount = 3; enum DataTypes { // Encounter States/Boss GUIDs - DATA_KRIKTHIR_THE_GATEWATCHER = 0, + DATA_KRIKTHIR = 0, DATA_HADRONOX = 1, DATA_ANUBARAK = 2, // Additional Data - DATA_WATCHER_GASHRA = 3, - DATA_WATCHER_SILTHIK = 4, - DATA_WATCHER_NARJIL = 5 + DATA_WATCHER_NARJIL, + DATA_WATCHER_GASHRA, + DATA_WATCHER_SILTHIK, + DATA_ANUBARAK_WALL, + DATA_ANUBARAK_WALL_2 }; enum CreatureIds @@ -47,6 +48,12 @@ enum CreatureIds NPC_WATCHER_SILTHIK = 28731 }; +// These are passed as -action to AI's DoAction to differentiate between them and boss scripts' own actions +enum InstanceActions +{ + ACTION_GATEWATCHER_GREET = 1 +}; + enum GameObjectIds { GO_KRIKTHIR_DOOR = 192395, @@ -55,10 +62,4 @@ enum GameObjectIds GO_ANUBARAK_DOOR_3 = 192398 }; -template<class AI> -AI* GetAzjolNerubAI(Creature* creature) -{ - return GetInstanceAI<AI>(creature, AzjolNerubScriptName); -} - #endif // AZJOL_NERUB_H_ diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp index 95335393942..2860698a8b2 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp @@ -17,65 +17,90 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" +#include "PassiveAI.h" #include "azjol_nerub.h" enum Spells { - SPELL_CARRION_BEETLES = 53520, - SPELL_SUMMON_CARRION_BEETLES = 53521, - SPELL_LEECHING_SWARM = 53467, - SPELL_POUND = 53472, - SPELL_SUBMERGE = 53421, - SPELL_IMPALE_DMG = 53454, - SPELL_IMPALE_SHAKEGROUND = 53455, - SPELL_IMPALE_SPIKE = 53539, //this is not the correct visual effect - //SPELL_IMPALE_TARGET = 53458, + SPELL_EMERGE = 53500, + SPELL_SUBMERGE = 53421, + SPELL_IMPALE_AURA = 53456, + SPELL_IMPALE_VISUAL = 53455, + SPELL_IMPALE_DAMAGE = 53454, + SPELL_LEECHING_SWARM = 53467, + SPELL_POUND = 59433, + SPELL_POUND_DAMAGE = 59432, + SPELL_CARRION_BEETLES = 53520, + SPELL_CARRION_BEETLE = 53521, + + SPELL_SUMMON_DARTER = 53599, + SPELL_SUMMON_ASSASSIN = 53609, + SPELL_SUMMON_GUARDIAN = 53614, + SPELL_SUMMON_VENOMANCER = 53615, + + SPELL_DART = 59349, + SPELL_BACKSTAB = 52540, + SPELL_ASSASSIN_VISUAL = 53611, + SPELL_SUNDER_ARMOR = 53618, + SPELL_POISON_BOLT = 53617 }; enum Creatures { - CREATURE_GUARDIAN = 29216, - CREATURE_VENOMANCER = 29217, - CREATURE_DATTER = 29213, - CREATURE_IMPALE_TARGET = 89, - DISPLAY_INVISIBLE = 11686 + NPC_WORLD_TRIGGER = 22515, }; -// not in db enum Yells { - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_DEATH = 2, - SAY_LOCUST = 3, - SAY_SUBMERGE = 4, - SAY_INTRO = 5 + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_DEATH = 2, + SAY_LOCUST = 3, + SAY_SUBMERGE = 4, + SAY_INTRO = 5 +}; + +enum Events +{ + EVENT_POUND = 1, + EVENT_IMPALE, + EVENT_LEECHING_SWARM, + EVENT_CARRION_BEETLES, + EVENT_SUBMERGE, // use event for this so we don't submerge mid-cast + EVENT_DARTER, + EVENT_ASSASSIN, + EVENT_GUARDIAN, + EVENT_VENOMANCER, + EVENT_CLOSE_DOOR +}; + +enum Actions +{ + ACTION_PET_DIED = 1, + ACTION_PET_EVADE }; enum Misc { - ACHIEV_TIMED_START_EVENT = 20381, + ACHIEV_GOTTA_GO_START_EVENT = 20381, }; enum Phases { - PHASE_MELEE = 0, - PHASE_UNDERGROUND = 1, - IMPALE_PHASE_TARGET = 0, - IMPALE_PHASE_ATTACK = 1, - IMPALE_PHASE_DMG = 2 + PHASE_EMERGE = 1, + PHASE_SUBMERGE }; -const Position SpawnPoint[2] = +enum GUIDTypes { - { 550.7f, 282.8f, 224.3f, 0.0f }, - { 551.1f, 229.4f, 224.3f, 0.0f }, + GUID_TYPE_PET = 0, + GUID_TYPE_IMPALE }; -const Position SpawnPointGuardian[2] = +enum SummonGroups { - { 550.348633f, 316.006805f, 234.2947f, 0.0f }, - { 550.188660f, 324.264557f, 237.7412f, 0.0f }, + SUMMON_GROUP_WORLD_TRIGGER_GUARDIAN = 1 }; class boss_anub_arak : public CreatureScript @@ -83,97 +108,62 @@ class boss_anub_arak : public CreatureScript public: boss_anub_arak() : CreatureScript("boss_anub_arak") { } - struct boss_anub_arakAI : public ScriptedAI + struct boss_anub_arakAI : public BossAI { - boss_anub_arakAI(Creature* creature) : ScriptedAI(creature), Summons(me) - { - Initialize(); - instance = creature->GetInstanceScript(); - GuardianSummoned = false; - VenomancerSummoned = false; - DatterSummoned = false; - UndergroundTimer = 0; - VenomancerTimer = 0; - DatterTimer = 0; - DelayTimer = 0; - } + boss_anub_arakAI(Creature* creature) : BossAI(creature, DATA_ANUBARAK), _nextSubmerge(0), _petCount(0), _assassinCount(0), _guardianCount(0), _venomancerCount(0) { } - void Initialize() + void Reset() override { - CarrionBeetlesTimer = 8 * IN_MILLISECONDS; - LeechingSwarmTimer = 20 * IN_MILLISECONDS; - ImpaleTimer = 9 * IN_MILLISECONDS; - PoundTimer = 15 * IN_MILLISECONDS; - - Phase = PHASE_MELEE; - UndergroundPhase = 0; - Channeling = false; - ImpalePhase = IMPALE_PHASE_TARGET; - ImpaleTarget.Clear(); + BossAI::Reset(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_GOTTA_GO_START_EVENT); + _nextSubmerge = 75; + _petCount = 0; } - InstanceScript* instance; - - bool Channeling; - bool GuardianSummoned; - bool VenomancerSummoned; - bool DatterSummoned; - uint8 Phase; - uint32 UndergroundPhase; - uint32 CarrionBeetlesTimer; - uint32 LeechingSwarmTimer; - uint32 PoundTimer; - uint32 UndergroundTimer; - uint32 VenomancerTimer; - uint32 DatterTimer; - uint32 DelayTimer; - - uint32 ImpaleTimer; - uint32 ImpalePhase; - ObjectGuid ImpaleTarget; - - SummonList Summons; + bool CanAIAttack(Unit const* /*who*/) const override { return true; } // do not check boundary here - void Reset() override + void EnterCombat(Unit* who) override { - Initialize(); - - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); - me->RemoveAura(SPELL_SUBMERGE); + BossAI::EnterCombat(who); - Summons.DespawnAll(); + 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); - instance->SetBossState(DATA_ANUBARAK, NOT_STARTED); - instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); - } - - Creature* DoSummonImpaleTarget(Unit* target) - { - Position targetPos = target->GetPosition(); - - if (TempSummon* impaleTarget = me->SummonCreature(CREATURE_IMPALE_TARGET, targetPos, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 6*IN_MILLISECONDS)) + Talk(SAY_AGGRO); + instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_GOTTA_GO_START_EVENT); + + events.SetPhase(PHASE_EMERGE); + events.ScheduleEvent(EVENT_CLOSE_DOOR, Seconds(5)); + events.ScheduleEvent(EVENT_POUND, randtime(Seconds(2), Seconds(4)), 0, PHASE_EMERGE); + events.ScheduleEvent(EVENT_LEECHING_SWARM, randtime(Seconds(5), Seconds(7)), 0, PHASE_EMERGE); + events.ScheduleEvent(EVENT_CARRION_BEETLES, randtime(Seconds(14), Seconds(17)), 0, PHASE_EMERGE); + + // set up world triggers + std::list<TempSummon*> summoned; + me->SummonCreatureGroup(SUMMON_GROUP_WORLD_TRIGGER_GUARDIAN, &summoned); + if (summoned.empty()) // something went wrong { - ImpaleTarget = impaleTarget->GetGUID(); - impaleTarget->SetReactState(REACT_PASSIVE); - impaleTarget->SetDisplayId(DISPLAY_INVISIBLE); - impaleTarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - impaleTarget->SetControlled(true, UNIT_STATE_ROOT); - return impaleTarget; + EnterEvadeMode(EVADE_REASON_OTHER); + return; + } + _guardianTrigger = (*summoned.begin())->GetGUID(); + + if (Creature* trigger = DoSummon(NPC_WORLD_TRIGGER, me->GetPosition(), 0u, TEMPSUMMON_MANUAL_DESPAWN)) + _assassinTrigger = trigger->GetGUID(); + else + { + EnterEvadeMode(EVADE_REASON_OTHER); + return; } - - return NULL; - } - - void EnterCombat(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - DelayTimer = 0; - instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); } - void DelayEventStart() + void EnterEvadeMode(EvadeReason /*why*/) override { - instance->SetBossState(DATA_ANUBARAK, IN_PROGRESS); + summons.DespawnAll(); + _DespawnAtEvade(); } void UpdateAI(uint32 diff) override @@ -181,192 +171,551 @@ public: if (!UpdateVictim()) return; - if (DelayTimer && DelayTimer > 5000) - DelayEventStart(); - else DelayTimer+=diff; + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - switch (Phase) + while (uint32 eventId = events.ExecuteEvent()) { - case PHASE_UNDERGROUND: - if (ImpaleTimer <= diff) + switch (eventId) { - switch (ImpalePhase) + 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); + events.Repeat(randtime(Seconds(26), Seconds(32))); + break; + case EVENT_LEECHING_SWARM: + Talk(SAY_LOCUST); + DoCastAOE(SPELL_LEECHING_SWARM); + events.Repeat(randtime(Seconds(25), Seconds(28))); + break; + case EVENT_CARRION_BEETLES: + DoCastAOE(SPELL_CARRION_BEETLES); + events.Repeat(randtime(Seconds(24), Seconds(27))); + break; + case EVENT_IMPALE: + if (Creature* impaleTarget = ObjectAccessor::GetCreature(*me, _impaleTarget)) + DoCast(impaleTarget, SPELL_IMPALE_DAMAGE, true); + break; + case EVENT_SUBMERGE: + Talk(SAY_SUBMERGE); + DoCastSelf(SPELL_SUBMERGE); + break; + case EVENT_DARTER: { - case IMPALE_PHASE_TARGET: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) + std::list<Creature*> triggers; + me->GetCreatureListWithEntryInGrid(triggers, NPC_WORLD_TRIGGER); + if (!triggers.empty()) { - if (Creature* impaleTarget = DoSummonImpaleTarget(target)) - impaleTarget->CastSpell(impaleTarget, SPELL_IMPALE_SHAKEGROUND, true); - ImpaleTimer = 3*IN_MILLISECONDS; - ImpalePhase = IMPALE_PHASE_ATTACK; + std::list<Creature*>::iterator it = triggers.begin(); + std::advance(it, urand(0, triggers.size()-1)); + (*it)->CastSpell(*it, SPELL_SUMMON_DARTER, true); + events.Repeat(Seconds(11)); } + else + EnterEvadeMode(EVADE_REASON_OTHER); break; - case IMPALE_PHASE_ATTACK: - if (Creature* impaleTarget = ObjectAccessor::GetCreature(*me, ImpaleTarget)) + } + case EVENT_ASSASSIN: + if (Creature* trigger = ObjectAccessor::GetCreature(*me, _assassinTrigger)) { - impaleTarget->CastSpell(impaleTarget, SPELL_IMPALE_SPIKE, false); - impaleTarget->RemoveAurasDueToSpell(SPELL_IMPALE_SHAKEGROUND); + trigger->CastSpell(trigger, SPELL_SUMMON_ASSASSIN, true); + trigger->CastSpell(trigger, SPELL_SUMMON_ASSASSIN, true); + if (_assassinCount > 2) + { + _assassinCount -= 2; + events.Repeat(Seconds(20)); + } + else + _assassinCount = 0; } - ImpalePhase = IMPALE_PHASE_DMG; - ImpaleTimer = 1*IN_MILLISECONDS; + else // something went wrong + EnterEvadeMode(EVADE_REASON_OTHER); break; - case IMPALE_PHASE_DMG: - if (Creature* impaleTarget = ObjectAccessor::GetCreature(*me, ImpaleTarget)) - me->CastSpell(impaleTarget, SPELL_IMPALE_DMG, true); - ImpalePhase = IMPALE_PHASE_TARGET; - ImpaleTimer = 9*IN_MILLISECONDS; - break; - } - } else ImpaleTimer -= diff; - - if (!GuardianSummoned) - { - for (uint8 i = 0; i < 2; ++i) - { - if (Creature* Guardian = me->SummonCreature(CREATURE_GUARDIAN, SpawnPointGuardian[i], TEMPSUMMON_CORPSE_DESPAWN, 0)) + case EVENT_GUARDIAN: + if (Creature* trigger = ObjectAccessor::GetCreature(*me, _guardianTrigger)) { - Guardian->AddThreat(me->GetVictim(), 0.0f); - DoZoneInCombat(Guardian); + trigger->CastSpell(trigger, SPELL_SUMMON_GUARDIAN, true); + trigger->CastSpell(trigger, SPELL_SUMMON_GUARDIAN, true); + if (_guardianCount > 2) + { + _guardianCount -= 2; + events.Repeat(Seconds(20)); + } + else + _guardianCount = 0; } - } - GuardianSummoned = true; - } - - if (!VenomancerSummoned) - { - if (VenomancerTimer <= diff) - { - if (UndergroundPhase > 1) + else + EnterEvadeMode(EVADE_REASON_OTHER); + break; + case EVENT_VENOMANCER: + if (Creature* trigger = ObjectAccessor::GetCreature(*me, _guardianTrigger)) { - for (uint8 i = 0; i < 2; ++i) + trigger->CastSpell(trigger, SPELL_SUMMON_VENOMANCER, true); + trigger->CastSpell(trigger, SPELL_SUMMON_VENOMANCER, true); + if (_venomancerCount > 2) { - if (Creature* Venomancer = me->SummonCreature(CREATURE_VENOMANCER, SpawnPoint[i], TEMPSUMMON_CORPSE_DESPAWN, 0)) - { - Venomancer->AddThreat(me->GetVictim(), 0.0f); - DoZoneInCombat(Venomancer); - } + _venomancerCount -= 2; + events.Repeat(Seconds(20)); } - VenomancerSummoned = true; + else + _venomancerCount = 0; } - } else VenomancerTimer -= diff; + else + EnterEvadeMode(EVADE_REASON_OTHER); + break; + default: + break; } - if (!DatterSummoned) + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + + DoMeleeAttackIfReady(); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void SetGUID(ObjectGuid guid, int32 type) override + { + switch (type) + { + case GUID_TYPE_PET: { - if (DatterTimer <= diff) + if (Creature* creature = ObjectAccessor::GetCreature(*me, guid)) + JustSummoned(creature); + else // something has gone horribly wrong + EnterEvadeMode(EVADE_REASON_OTHER); + break; + } + case GUID_TYPE_IMPALE: + _impaleTarget = guid; + events.ScheduleEvent(EVENT_IMPALE, Seconds(4)); + break; + } + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_PET_DIED: + if (!_petCount) // underflow check - something has gone horribly wrong { - if (UndergroundPhase > 2) - { - for (uint8 i = 0; i < 2; ++i) - { - if (Creature* Datter = me->SummonCreature(CREATURE_DATTER, SpawnPoint[i], TEMPSUMMON_CORPSE_DESPAWN, 0)) - { - Datter->AddThreat(me->GetVictim(), 0.0f); - DoZoneInCombat(Datter); - } - } - DatterSummoned = true; - } - } else DatterTimer -= diff; + EnterEvadeMode(EVADE_REASON_OTHER); + return; + } + if (!--_petCount) // last pet died, emerge + { + me->RemoveAurasDueToSpell(SPELL_SUBMERGE); + me->RemoveAurasDueToSpell(SPELL_IMPALE_AURA); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + DoCastSelf(SPELL_EMERGE); + events.SetPhase(PHASE_EMERGE); + events.ScheduleEvent(EVENT_POUND, randtime(Seconds(13), Seconds(18)), 0, PHASE_EMERGE); + events.ScheduleEvent(EVENT_LEECHING_SWARM, randtime(Seconds(3), Seconds(7)), 0, PHASE_EMERGE); + events.ScheduleEvent(EVENT_CARRION_BEETLES, randtime(Seconds(10), Seconds(15)), 0, PHASE_EMERGE); + } + break; + case ACTION_PET_EVADE: + EnterEvadeMode(EVADE_REASON_OTHER); + break; + } + } - if (me->HasAura(SPELL_LEECHING_SWARM)) - me->RemoveAurasDueToSpell(SPELL_LEECHING_SWARM); + void DamageTaken(Unit* /*source*/, uint32& damage) override + { + if (me->HasAura(SPELL_SUBMERGE)) + damage = 0; + else + if (_nextSubmerge && me->HealthBelowPctDamaged(_nextSubmerge, damage)) + { + events.CancelEvent(EVENT_SUBMERGE); + events.ScheduleEvent(EVENT_SUBMERGE, 0, 0, PHASE_EMERGE); + _nextSubmerge = _nextSubmerge-25; } + } - if (UndergroundTimer <= diff) - { - me->RemoveAura(SPELL_SUBMERGE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); - Phase = PHASE_MELEE; - } else UndergroundTimer -= diff; - break; - - case PHASE_MELEE: - if (((UndergroundPhase == 0 && HealthBelowPct(75)) - || (UndergroundPhase == 1 && HealthBelowPct(50)) - || (UndergroundPhase == 2 && HealthBelowPct(25))) - && !me->HasUnitState(UNIT_STATE_CASTING)) + void SpellHit(Unit* /*whose*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_SUBMERGE) + { + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->RemoveAurasDueToSpell(SPELL_LEECHING_SWARM); + DoCastSelf(SPELL_IMPALE_AURA, true); + + events.SetPhase(PHASE_SUBMERGE); + switch (_nextSubmerge) { - GuardianSummoned = false; - VenomancerSummoned = false; - DatterSummoned = false; + case 50: // first submerge phase + _assassinCount = 4; + _guardianCount = 2; + _venomancerCount = 0; + break; + case 25: // second submerge phase + _assassinCount = 6; + _guardianCount = 2; + _venomancerCount = 2; + break; + case 0: // third submerge phase + _assassinCount = 6; + _guardianCount = 2; + _venomancerCount = 2; + events.ScheduleEvent(EVENT_DARTER, Seconds(0), 0, PHASE_SUBMERGE); + break; + } + _petCount = _guardianCount + _venomancerCount; + if (_assassinCount) + events.ScheduleEvent(EVENT_ASSASSIN, Seconds(0), 0, PHASE_SUBMERGE); + if (_guardianCount) + events.ScheduleEvent(EVENT_GUARDIAN, Seconds(4), 0, PHASE_SUBMERGE); + if (_venomancerCount) + events.ScheduleEvent(EVENT_VENOMANCER, Seconds(20), 0, PHASE_SUBMERGE); + } + } - UndergroundTimer = 40*IN_MILLISECONDS; - VenomancerTimer = 25*IN_MILLISECONDS; - DatterTimer = 32*IN_MILLISECONDS; + private: + ObjectGuid _impaleTarget; + uint32 _nextSubmerge; + uint32 _petCount; + ObjectGuid _guardianTrigger; + ObjectGuid _assassinTrigger; + uint8 _assassinCount; + uint8 _guardianCount; + uint8 _venomancerCount; + }; - ImpalePhase = 0; - ImpaleTimer = 9*IN_MILLISECONDS; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_anub_arakAI>(creature); + } +}; - DoCast(me, SPELL_SUBMERGE, false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); +class npc_anubarak_pet_template : public ScriptedAI +{ + public: + npc_anubarak_pet_template(Creature* creature, bool isLarge) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _isLarge(isLarge) { } - Phase = PHASE_UNDERGROUND; - ++UndergroundPhase; - } + void InitializeAI() override + { + ScriptedAI::InitializeAI(); + if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK)) + anubarak->AI()->SetGUID(me->GetGUID(), GUID_TYPE_PET); + else + me->DespawnOrUnsummon(); + } + + void JustDied(Unit* killer) override + { + ScriptedAI::JustDied(killer); + if (_isLarge) + if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK)) + anubarak->AI()->DoAction(ACTION_PET_DIED); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK)) + anubarak->AI()->DoAction(ACTION_PET_EVADE); + else + me->DespawnOrUnsummon(); + } - if (Channeling == true) + protected: + InstanceScript* _instance; + private: + bool const _isLarge; +}; + +class npc_anubarak_anub_ar_darter : public CreatureScript +{ + public: + npc_anubarak_anub_ar_darter() : CreatureScript("npc_anubarak_anub_ar_darter") { } + + struct npc_anubarak_anub_ar_darterAI : public npc_anubarak_pet_template + { + npc_anubarak_anub_ar_darterAI(Creature* creature) : npc_anubarak_pet_template(creature, false) { } + + void InitializeAI() override + { + npc_anubarak_pet_template::InitializeAI(); + DoCastAOE(SPELL_DART); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_anubarak_anub_ar_darterAI>(creature); + } +}; + +class npc_anubarak_anub_ar_assassin : public CreatureScript +{ + public: + npc_anubarak_anub_ar_assassin() : CreatureScript("npc_anubarak_anub_ar_assassin") { } + + struct npc_anubarak_anub_ar_assassinAI : public npc_anubarak_pet_template + { + npc_anubarak_anub_ar_assassinAI(Creature* creature) : npc_anubarak_pet_template(creature, false), _backstabTimer(6 * IN_MILLISECONDS) { } + + bool IsInBounds(Position const& jumpTo, CreatureBoundary const* boundary) + { + if (!boundary) + return true; + for (CreatureBoundary::const_iterator it = boundary->cbegin(); it != boundary->cend(); ++it) + if (!(*it)->IsWithinBoundary(&jumpTo)) + return false; + return true; + } + Position GetRandomPositionAround(Creature* anubarak) + { + static float DISTANCE_MIN = 10.0f; + static float DISTANCE_MAX = 30.0f; + double angle = rand_norm() * 2.0 * M_PI; + return { anubarak->GetPositionX() + (float)(frand(DISTANCE_MIN, DISTANCE_MAX)*std::sin(angle)), anubarak->GetPositionY() + (float)(frand(DISTANCE_MIN, DISTANCE_MAX)*std::cos(angle)), anubarak->GetPositionZ() }; + } + void InitializeAI() override + { + npc_anubarak_pet_template::InitializeAI(); + CreatureBoundary const* boundary = _instance->GetBossBoundary(DATA_ANUBARAK); + if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK)) { - for (uint8 i = 0; i < 8; ++i) - DoCastVictim(SPELL_SUMMON_CARRION_BEETLES, true); - Channeling = false; + Position jumpTo; + do + jumpTo = GetRandomPositionAround(anubarak); + while (!IsInBounds(jumpTo, boundary)); + me->GetMotionMaster()->MoveJump(jumpTo, 40.0f, 40.0f); + DoCastSelf(SPELL_ASSASSIN_VISUAL, true); } - else if (CarrionBeetlesTimer <= diff) - { - Channeling = true; - DoCastVictim(SPELL_CARRION_BEETLES); - CarrionBeetlesTimer = 25*IN_MILLISECONDS; - } else CarrionBeetlesTimer -= diff; + } - if (LeechingSwarmTimer <= diff) - { - DoCast(me, SPELL_LEECHING_SWARM, true); - LeechingSwarmTimer = 19*IN_MILLISECONDS; - } else LeechingSwarmTimer -= diff; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - if (PoundTimer <= diff) + if (diff >= _backstabTimer) { - if (Unit* target = me->GetVictim()) - { - if (Creature* pImpaleTarget = DoSummonImpaleTarget(target)) - me->CastSpell(pImpaleTarget, SPELL_POUND, false); - } - PoundTimer = 16500; - } else PoundTimer -= diff; + if (me->GetVictim() && me->GetVictim()->isInBack(me)) + DoCastVictim(SPELL_BACKSTAB); + _backstabTimer = 6 * IN_MILLISECONDS; + } + else + _backstabTimer -= diff; DoMeleeAttackIfReady(); - break; } - } - void JustDied(Unit* /*killer*/) override + void MovementInform(uint32 /*type*/, uint32 id) override + { + if (id == EVENT_JUMP) + { + me->RemoveAurasDueToSpell(SPELL_ASSASSIN_VISUAL); + DoZoneInCombat(); + } + } + + private: + uint32 _backstabTimer; + }; + + CreatureAI* GetAI(Creature* creature) const override { - Talk(SAY_DEATH); - Summons.DespawnAll(); - instance->SetBossState(DATA_ANUBARAK, DONE); + return GetInstanceAI<npc_anubarak_anub_ar_assassinAI>(creature); } +}; - void KilledUnit(Unit* victim) override +class npc_anubarak_anub_ar_guardian : public CreatureScript +{ + public: + npc_anubarak_anub_ar_guardian() : CreatureScript("npc_anubarak_anub_ar_guardian") { } + + struct npc_anubarak_anub_ar_guardianAI : public npc_anubarak_pet_template + { + npc_anubarak_anub_ar_guardianAI(Creature* creature) : npc_anubarak_pet_template(creature, true), _sunderTimer(6 * IN_MILLISECONDS) { } + + void UpdateAI(uint32 diff) override { - if (victim->GetTypeId() != TYPEID_PLAYER) + if (!UpdateVictim()) return; - Talk(SAY_SLAY); + if (diff >= _sunderTimer) + { + DoCastVictim(SPELL_SUNDER_ARMOR); + _sunderTimer = 12 * IN_MILLISECONDS; + } + else + _sunderTimer -= diff; + + DoMeleeAttackIfReady(); } - void JustSummoned(Creature* summon) override + private: + uint32 _sunderTimer; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_anubarak_anub_ar_guardianAI>(creature); + } +}; + +class npc_anubarak_anub_ar_venomancer : public CreatureScript +{ + public: + npc_anubarak_anub_ar_venomancer() : CreatureScript("npc_anubarak_anub_ar_venomancer") { } + + struct npc_anubarak_anub_ar_venomancerAI : public npc_anubarak_pet_template + { + npc_anubarak_anub_ar_venomancerAI(Creature* creature) : npc_anubarak_pet_template(creature, true), _boltTimer(5 * IN_MILLISECONDS) { } + + void UpdateAI(uint32 diff) override { - Summons.Summon(summon); + if (!UpdateVictim()) + return; + + if (diff >= _boltTimer) + { + DoCastVictim(SPELL_POISON_BOLT); + _boltTimer = urandms(2, 3); + } + else + _boltTimer -= diff; + + DoMeleeAttackIfReady(); } + + private: + uint32 _boltTimer; }; CreatureAI* GetAI(Creature* creature) const override { - return GetInstanceAI<boss_anub_arakAI>(creature); + return GetInstanceAI<npc_anubarak_anub_ar_venomancerAI>(creature); } }; +class npc_anubarak_impale_target : public CreatureScript +{ + public: + npc_anubarak_impale_target() : CreatureScript("npc_anubarak_impale_target") { } + + struct npc_anubarak_impale_targetAI : public NullCreatureAI + { + npc_anubarak_impale_targetAI(Creature* creature) : NullCreatureAI(creature) { } + + void InitializeAI() override + { + if (Creature* anubarak = me->GetInstanceScript()->GetCreature(DATA_ANUBARAK)) + { + DoCastSelf(SPELL_IMPALE_VISUAL); + me->DespawnOrUnsummon(Seconds(6)); + anubarak->AI()->SetGUID(me->GetGUID(), GUID_TYPE_IMPALE); + } + else + me->DespawnOrUnsummon(); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_anubarak_impale_targetAI>(creature); + } +}; + +class spell_anubarak_pound : public SpellScriptLoader +{ + public: + spell_anubarak_pound() : SpellScriptLoader("spell_anubarak_pound") { } + + class spell_anubarak_pound_SpellScript : public SpellScript + { + PrepareSpellScript(spell_anubarak_pound_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return sSpellMgr->GetSpellInfo(SPELL_POUND_DAMAGE) != nullptr; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + GetCaster()->CastSpell(target, SPELL_POUND_DAMAGE, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_anubarak_pound_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_anubarak_pound_SpellScript(); + } +}; + +class spell_anubarak_carrion_beetles : public SpellScriptLoader +{ + public: + spell_anubarak_carrion_beetles() : SpellScriptLoader("spell_anubarak_carrion_beetles") { } + + class spell_anubarak_carrion_beetles_AuraScript : public AuraScript + { + public: + PrepareAuraScript(spell_anubarak_carrion_beetles_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return (sSpellMgr->GetSpellInfo(SPELL_CARRION_BEETLE) != nullptr); + } + + void HandlePeriodic(AuraEffect const* /*eff*/) + { + GetCaster()->CastSpell(GetCaster(), SPELL_CARRION_BEETLE, true); + GetCaster()->CastSpell(GetCaster(), SPELL_CARRION_BEETLE, true); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_anubarak_carrion_beetles_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_anubarak_carrion_beetles_AuraScript(); + } +}; + void AddSC_boss_anub_arak() { new boss_anub_arak(); + + new npc_anubarak_anub_ar_darter(); + new npc_anubarak_anub_ar_assassin(); + new npc_anubarak_anub_ar_guardian(); + new npc_anubarak_anub_ar_venomancer(); + new npc_anubarak_impale_target(); + + new spell_anubarak_pound(); + new spell_anubarak_carrion_beetles(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp index ff2588d204e..c41ec1c488d 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp @@ -15,34 +15,136 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* -* Comment: No Waves atm and the doors spells are crazy... -* -* When your group enters the main room (the one after the bridge), you will notice a group of 3 Nerubians. -* When you engage them, 2 more groups like this one spawn behind the first one - it is important to pull the first group back, -* so you don't aggro all 3. Hadronox will be under you, fighting Nerubians. -* -* This is the timed gauntlet - waves of non-elite spiders -* will spawn from the 3 doors located a little above the main room, and will then head down to fight Hadronox. After clearing the -* main room, it is recommended to just stay in it, kill the occasional non-elites that will attack you instead of the boss, and wait for -* Hadronox to make his way to you. When Hadronox enters the main room, she will web the doors, and no more non-elites will spawn. -*/ - #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" +#include "SpellAuras.h" +#include "SpellAuraEffects.h" #include "azjol_nerub.h" +enum Events +{ + // Hadronox + EVENT_LEECH_POISON = 1, + EVENT_ACID_CLOUD, + EVENT_WEB_GRAB, + EVENT_PIERCE_ARMOR, + EVENT_PLAYER_CHECK, + + // Anub'ar Crusher + EVENT_SMASH, + + // Anub'ar foes - Shared + EVENT_TAUNT, + + // Anub'ar Champion + EVENT_REND, + EVENT_PUMMEL, + + // Anub'ar Crypt Guard + EVENT_CRUSHING_WEBS, + EVENT_INFECTED_WOUND, + + // Anub'ar Necromancer + EVENT_SHADOW_BOLT, + EVENT_ANIMATE_BONES +}; + enum Spells { - SPELL_ACID_CLOUD = 53400, // Victim - SPELL_LEECH_POISON = 53030, // Victim - SPELL_PIERCE_ARMOR = 53418, // Victim - SPELL_WEB_GRAB = 57731, // Victim - SPELL_WEB_FRONT_DOORS = 53177, // Self - SPELL_WEB_SIDE_DOORS = 53185, // Self - H_SPELL_ACID_CLOUD = 59419, - H_SPELL_LEECH_POISON = 59417, - H_SPELL_WEB_GRAB = 59421 + // Hadronox + SPELL_WEB_FRONT_DOORS = 53177, + SPELL_WEB_SIDE_DOORS = 53185, + SPELL_LEECH_POISON = 53030, + SPELL_LEECH_POISON_HEAL = 53800, + SPELL_ACID_CLOUD = 53400, + SPELL_WEB_GRAB = 57731, + SPELL_PIERCE_ARMOR = 53418, + + // Anub'ar opponent summoning spells + SPELL_SUMMON_CHAMPION_PERIODIC = 53035, + SPELL_SUMMON_CRYPT_FIEND_PERIODIC = 53037, + SPELL_SUMMON_NECROMANCER_PERIODIC = 53036, + SPELL_SUMMON_CHAMPION_TOP = 53064, + SPELL_SUMMON_CRYPT_FIEND_TOP = 53065, + SPELL_SUMMON_NECROMANCER_TOP = 53066, + SPELL_SUMMON_CHAMPION_BOTTOM = 53090, + SPELL_SUMMON_CRYPT_FIEND_BOTTOM = 53091, + SPELL_SUMMON_NECROMANCER_BOTTOM = 53092, + + // Anub'ar Crusher + SPELL_SMASH = 53318, + SPELL_FRENZY = 53801, + + // Anub'ar foes - Shared + SPELL_TAUNT = 53798, + + // Anub'ar Champion + SPELL_REND = 59343, + SPELL_PUMMEL = 59344, + + // Anub'ar Crypt Guard + SPELL_CRUSHING_WEBS = 59347, + SPELL_INFECTED_WOUND = 59348, + + // Anub'ar Necromancer + SPELL_SHADOW_BOLT = 53333, + SPELL_ANIMATE_BONES_1 = 53334, + SPELL_ANIMATE_BONES_2 = 53336, +}; + +enum SummonGroups +{ + SUMMON_GROUP_CRUSHER_1 = 1, + SUMMON_GROUP_CRUSHER_2 = 2, + SUMMON_GROUP_CRUSHER_3 = 3 +}; + +enum Actions +{ + ACTION_HADRONOX_MOVE = 1, + ACTION_CRUSHER_ENGAGED, + ACTION_PACK_WALK +}; + +enum Data +{ + DATA_CRUSHER_PACK_ID = 1, + DATA_HADRONOX_ENTERED_COMBAT, + DATA_HADRONOX_WEBBED_DOORS +}; + +enum Creatures +{ + NPC_CRUSHER = 28922, + NPC_WORLDTRIGGER_LARGE = 23472 +}; + +enum Talk +{ + CRUSHER_SAY_AGGRO = 1, + CRUSHER_EMOTE_FRENZY = 2, + HADRONOX_EMOTE_MOVE = 1 +}; + +// Movement IDs used by the permanently spawning Anub'ar opponents - they are done in sequence, as one finishes, the next one starts +enum Movements +{ + MOVE_NONE = 0, + MOVE_OUTSIDE, + MOVE_DOWNSTAIRS, + MOVE_DOWNSTAIRS_2, + MOVE_HADRONOX, // this one might have us take a detour to avoid pathfinding "through" the floor... + MOVE_HADRONOX_REAL // while this one will always make us movechase +}; + +static const uint8 NUM_STEPS = 4; +static const Position hadronoxStep[NUM_STEPS] = +{ + { 515.5848f, 544.2007f, 673.6272f }, + { 562.191f , 514.068f , 696.4448f }, + { 610.3828f, 518.6407f, 695.9385f }, + { 530.42f , 560.003f, 733.0308f } }; class boss_hadronox : public CreatureScript @@ -50,157 +152,1014 @@ class boss_hadronox : public CreatureScript public: boss_hadronox() : CreatureScript("boss_hadronox") { } - struct boss_hadronoxAI : public ScriptedAI + struct boss_hadronoxAI : public BossAI { - boss_hadronoxAI(Creature* creature) : ScriptedAI(creature) + boss_hadronoxAI(Creature* creature) : BossAI(creature, DATA_HADRONOX), _enteredCombat(false), _doorsWebbed(false), _lastPlayerCombatState(false), _step(0) { } + + bool IsInCombatWithPlayer() const { - Initialize(); - instance = creature->GetInstanceScript(); - fMaxDistance = 50.0f; - bFirstTime = true; + std::list<HostileReference*> const& refs = me->getThreatManager().getThreatList(); + for (HostileReference const* hostileRef : refs) + { + if (Unit const* target = hostileRef->getTarget()) + if (target->IsControlledByPlayer()) + return true; + } + return false; } - void Initialize() + void SetStep(uint8 step) + { + if (_lastPlayerCombatState) + return; + + _step = step; + me->SetHomePosition(hadronoxStep[step]); + me->GetMotionMaster()->Clear(); + me->AttackStop(); + SetCombatMovement(false); + me->GetMotionMaster()->MovePoint(0, hadronoxStep[step]); + } + + void SummonCrusherPack(SummonGroups group) { - uiAcidTimer = urand(10 * IN_MILLISECONDS, 14 * IN_MILLISECONDS); - uiLeechTimer = urand(3 * IN_MILLISECONDS, 9 * IN_MILLISECONDS); - uiPierceTimer = urand(1 * IN_MILLISECONDS, 3 * IN_MILLISECONDS); - uiGrabTimer = urand(15 * IN_MILLISECONDS, 19 * IN_MILLISECONDS); - uiDoorsTimer = urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS); - uiCheckDistanceTimer = 2 * IN_MILLISECONDS; + std::list<TempSummon*> summoned; + me->SummonCreatureGroup(group, &summoned); + for (TempSummon* summon : summoned) + { + summon->AI()->SetData(DATA_CRUSHER_PACK_ID, group); + summon->AI()->DoAction(ACTION_PACK_WALK); + } + } + + void MovementInform(uint32 type, uint32 /*id*/) override + { + if (type != POINT_MOTION_TYPE) + return; + SetCombatMovement(true); + AttackStart(me->GetVictim()); + if (_step < NUM_STEPS-1) + return; + DoCastAOE(SPELL_WEB_FRONT_DOORS); + DoCastAOE(SPELL_WEB_SIDE_DOORS); + _doorsWebbed = true; + DoZoneInCombat(); + } + + uint32 GetData(uint32 data) const override + { + if (data == DATA_HADRONOX_ENTERED_COMBAT) + return _enteredCombat ? 1 : 0; + if (data == DATA_HADRONOX_WEBBED_DOORS) + return _doorsWebbed ? 1 : 0; + return 0; } - InstanceScript* instance; + bool CanAIAttack(Unit const* target) const override + { + // Prevent Hadronox from going too far from her current home position + if (!target->IsControlledByPlayer() && target->GetDistance(me->GetHomePosition()) > 20.0f) + return false; + return BossAI::CanAIAttack(target); + } - uint32 uiAcidTimer; - uint32 uiLeechTimer; - uint32 uiPierceTimer; - uint32 uiGrabTimer; - uint32 uiDoorsTimer; - uint32 uiCheckDistanceTimer; + void EnterCombat(Unit* /*who*/) override + { + events.ScheduleEvent(EVENT_LEECH_POISON, randtime(Seconds(5), Seconds(7))); + events.ScheduleEvent(EVENT_ACID_CLOUD, randtime(Seconds(7), Seconds(13))); + events.ScheduleEvent(EVENT_WEB_GRAB, randtime(Seconds(13), Seconds(19))); + events.ScheduleEvent(EVENT_PIERCE_ARMOR, randtime(Seconds(4), Seconds(7))); + events.ScheduleEvent(EVENT_PLAYER_CHECK, Seconds(1)); + me->setActive(true); + } - bool bFirstTime; + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_CRUSHER_ENGAGED: + if (_enteredCombat) + break; + instance->SetBossState(DATA_HADRONOX, IN_PROGRESS); + _enteredCombat = true; + SummonCrusherPack(SUMMON_GROUP_CRUSHER_2); + SummonCrusherPack(SUMMON_GROUP_CRUSHER_3); + break; + case ACTION_HADRONOX_MOVE: + if (_step < NUM_STEPS-1) + { + SetStep(_step + 1); + Talk(HADRONOX_EMOTE_MOVE); + } + break; + } + } - float fMaxDistance; + void EnterEvadeMode(EvadeReason /*why*/) override + { + std::list<Creature*> triggers; + me->GetCreatureListWithEntryInGrid(triggers, NPC_WORLDTRIGGER_LARGE); + for (Creature* trigger : triggers) + if (trigger->HasAura(SPELL_SUMMON_CHAMPION_PERIODIC) || trigger->HasAura(SPELL_WEB_FRONT_DOORS) || trigger->HasAura(SPELL_WEB_SIDE_DOORS)) + _DespawnAtEvade(25, trigger); + _DespawnAtEvade(25); + summons.DespawnAll(); + for (ObjectGuid gNerubian : _anubar) + if (Creature* nerubian = ObjectAccessor::GetCreature(*me, gNerubian)) + nerubian->DespawnOrUnsummon(); + } - void Reset() override + void SetGUID(ObjectGuid guid, int32 /*what*/) override + { + _anubar.push_back(guid); + } + + void Initialize() { me->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 9.0f); me->SetFloatValue(UNIT_FIELD_COMBATREACH, 9.0f); + _enteredCombat = false; + _doorsWebbed = false; + _lastPlayerCombatState = false; + SetStep(0); + SetCombatMovement(true); + SummonCrusherPack(SUMMON_GROUP_CRUSHER_1); + } + + void InitializeAI() override + { + BossAI::InitializeAI(); + if (me->IsAlive()) + Initialize(); + } + void JustRespawned() override + { + BossAI::JustRespawned(); Initialize(); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - if (instance->GetBossState(DATA_HADRONOX) != DONE && !bFirstTime) - instance->SetBossState(DATA_HADRONOX, FAIL); + events.Update(diff); - bFirstTime = false; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_LEECH_POISON: + DoCastAOE(SPELL_LEECH_POISON); + events.Repeat(randtime(Seconds(7), Seconds(9))); + break; + case EVENT_ACID_CLOUD: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f)) + DoCast(target, SPELL_ACID_CLOUD); + events.Repeat(randtime(Seconds(16), Seconds(23))); + break; + case EVENT_WEB_GRAB: + DoCastAOE(SPELL_WEB_GRAB); + events.Repeat(randtime(Seconds(20), Seconds(25))); + break; + case EVENT_PIERCE_ARMOR: + DoCastVictim(SPELL_PIERCE_ARMOR); + events.Repeat(randtime(Seconds(10), Seconds(15))); + break; + case EVENT_PLAYER_CHECK: + if (IsInCombatWithPlayer() != _lastPlayerCombatState) + { + _lastPlayerCombatState = !_lastPlayerCombatState; + if (_lastPlayerCombatState) // we are now in combat with players + { + if (!instance->CheckRequiredBosses(DATA_HADRONOX)) + { + EnterEvadeMode(EVADE_REASON_SEQUENCE_BREAK); + return; + } + // cancel current point movement if engaged by players + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE) + { + me->GetMotionMaster()->Clear(); + SetCombatMovement(true); + AttackStart(me->GetVictim()); + } + } + else // we are no longer in combat with players - reset the encounter + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + } + events.Repeat(Seconds(1)); + break; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + DoMeleeAttackIfReady(); } - //when Hadronox kills any enemy (that includes a party member) she will regain 10% of her HP if the target had Leech Poison on - void KilledUnit(Unit* Victim) override + // Safeguard to prevent Hadronox dying to NPCs + void DamageTaken(Unit* who, uint32& damage) override { - // not sure if this aura check is correct, I think it is though - if (!Victim || !Victim->HasAura(DUNGEON_MODE(SPELL_LEECH_POISON, H_SPELL_LEECH_POISON)) || !me->IsAlive()) - return; + if (!who->IsControlledByPlayer() && me->HealthBelowPct(70)) + { + if (me->HealthBelowPctDamaged(5, damage)) + damage = 0; + else + damage *= (me->GetHealthPct()-5.0f)/ 65.0f; + } + } - me->ModifyHealth(int32(me->CountPctFromMaxHealth(10))); + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + // Do not enter combat with zone } + + private: + bool _enteredCombat; // has a player entered combat with the first crusher pack? (talk and spawn two more packs) + bool _doorsWebbed; // obvious - have we reached the top and webbed the doors shut? (trigger for hadronox denied achievement) + bool _lastPlayerCombatState; // was there a player in our threat list the last time we checked (we check every second) + uint8 _step; + std::list<ObjectGuid> _anubar; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_hadronoxAI>(creature); + } +}; + +struct npc_hadronox_crusherPackAI : public ScriptedAI +{ + npc_hadronox_crusherPackAI(Creature* creature, Position const* positions) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _positions(positions), _myPack(SummonGroups(0)), _doFacing(false) { } - void JustDied(Unit* /*killer*/) override + void DoAction(int32 action) override + { + if (action == ACTION_PACK_WALK) { - instance->SetBossState(DATA_HADRONOX, DONE); + switch (_myPack) + { + case SUMMON_GROUP_CRUSHER_1: + case SUMMON_GROUP_CRUSHER_2: + case SUMMON_GROUP_CRUSHER_3: + me->GetMotionMaster()->MovePoint(ACTION_PACK_WALK, _positions[_myPack - SUMMON_GROUP_CRUSHER_1]); + break; + default: + break; + } } + } - void EnterCombat(Unit* /*who*/) override + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE && id == ACTION_PACK_WALK) + _doFacing = true; + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX)) + hadronox->AI()->EnterEvadeMode(EVADE_REASON_OTHER); + } + + uint32 GetData(uint32 data) const override + { + if (data == DATA_CRUSHER_PACK_ID) + return _myPack; + return 0; + } + + void SetData(uint32 data, uint32 value) override + { + if (data == DATA_CRUSHER_PACK_ID) { - instance->SetBossState(DATA_HADRONOX, IN_PROGRESS); - me->SetInCombatWithZone(); + _myPack = SummonGroups(value); + me->SetReactState(_myPack ? REACT_PASSIVE : REACT_AGGRESSIVE); } + } - void CheckDistance(float dist, const uint32 uiDiff) + void EnterCombat(Unit* who) override + { + if (me->HasReactState(REACT_PASSIVE)) { - if (!me->IsInCombat()) - return; + std::list<Creature*> others; + me->GetCreatureListWithEntryInGrid(others, 0, 40.0f); + for (Creature* other : others) + if (other->AI()->GetData(DATA_CRUSHER_PACK_ID) == _myPack) + { + other->SetReactState(REACT_AGGRESSIVE); + other->AI()->AttackStart(who); + } + } + _EnterCombat(); + ScriptedAI::EnterCombat(who); + } - float x=0.0f, y=0.0f, z=0.0f; - me->GetRespawnPosition(x, y, z); + virtual void _EnterCombat() = 0; + virtual void DoEvent(uint32 /*eventId*/) = 0; - if (uiCheckDistanceTimer <= uiDiff) - uiCheckDistanceTimer = 5*IN_MILLISECONDS; - else + void MoveInLineOfSight(Unit* who) override + { + if (!me->HasReactState(REACT_PASSIVE)) + { + ScriptedAI::MoveInLineOfSight(who); + return; + } + + if (me->CanStartAttack(who, false) && me->IsWithinDistInMap(who, me->GetAttackDistance(who) + me->m_CombatDistance)) + EnterCombat(who); + } + + void UpdateAI(uint32 diff) override + { + if (_doFacing) + { + _doFacing = false; + me->SetFacingTo(_positions[_myPack - SUMMON_GROUP_CRUSHER_1].GetOrientation()); + } + + if (!UpdateVictim()) + return; + + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + DoEvent(eventId); + + DoMeleeAttackIfReady(); + } + + protected: + InstanceScript* const _instance; + EventMap _events; + Position const* const _positions; + SummonGroups _myPack; + bool _doFacing; + +}; + +static const Position crusherWaypoints[] = +{ + { 529.6913f, 547.1257f, 731.9155f, 4.799650f }, + { 517.51f , 561.439f , 734.0306f, 4.520403f }, + { 543.414f , 551.728f , 732.0522f, 3.996804f } +}; +class npc_anub_ar_crusher : public CreatureScript +{ + public: + npc_anub_ar_crusher() : CreatureScript("npc_anub_ar_crusher") { } + + struct npc_anub_ar_crusherAI : public npc_hadronox_crusherPackAI + { + npc_anub_ar_crusherAI(Creature* creature) : npc_hadronox_crusherPackAI(creature, crusherWaypoints), _hadFrenzy(false) { } + + void _EnterCombat() override { - uiCheckDistanceTimer -= uiDiff; - return; + _events.ScheduleEvent(EVENT_SMASH, randtime(Seconds(8), Seconds(12))); + + if (_myPack != SUMMON_GROUP_CRUSHER_1) + return; + + if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX)) + { + if (hadronox->AI()->GetData(DATA_HADRONOX_ENTERED_COMBAT)) + return; + hadronox->AI()->DoAction(ACTION_CRUSHER_ENGAGED); + } + + Talk(CRUSHER_SAY_AGGRO); } - if (me->IsInEvadeMode() || !me->GetVictim()) - return; - if (me->GetDistance(x, y, z) > dist) - EnterEvadeMode(); + + void DamageTaken(Unit* /*source*/, uint32& damage) override + { + if (_hadFrenzy || !me->HealthBelowPctDamaged(25, damage)) + return; + _hadFrenzy = true; + Talk(CRUSHER_EMOTE_FRENZY); + DoCastSelf(SPELL_FRENZY); + } + + void DoEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_SMASH: + DoCastVictim(SPELL_SMASH); + _events.Repeat(randtime(Seconds(13), Seconds(21))); + break; + } + } + + void JustDied(Unit* killer) override + { + if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX)) + hadronox->AI()->DoAction(ACTION_HADRONOX_MOVE); + ScriptedAI::JustDied(killer); + } + + private: + bool _hadFrenzy; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_anub_ar_crusherAI>(creature); } +}; - void UpdateAI(uint32 diff) override +static const Position championWaypoints[] = +{ + { 539.2076f, 549.7539f, 732.8668f, 4.55531f }, + { 527.3098f, 559.5197f, 732.9407f, 4.742493f }, + { } +}; +class npc_anub_ar_crusher_champion : public CreatureScript +{ + public: + npc_anub_ar_crusher_champion() : CreatureScript("npc_anub_ar_crusher_champion") { } + + struct npc_anub_ar_crusher_championAI : public npc_hadronox_crusherPackAI { - //Return since we have no target - if (!UpdateVictim()) - return; + npc_anub_ar_crusher_championAI(Creature* creature) : npc_hadronox_crusherPackAI(creature, championWaypoints) { } + + void DoEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_REND: + DoCastVictim(SPELL_REND); + _events.Repeat(randtime(Seconds(12), Seconds(16))); + break; + case EVENT_PUMMEL: + DoCastVictim(SPELL_PUMMEL); + _events.Repeat(randtime(Seconds(12), Seconds(17))); + break; + } + } + + void _EnterCombat() override + { + _events.ScheduleEvent(EVENT_REND, randtime(Seconds(4), Seconds(8))); + _events.ScheduleEvent(EVENT_PUMMEL, randtime(Seconds(15), Seconds(19))); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_anub_ar_crusher_championAI>(creature); + } +}; + +static const Position cryptFiendWaypoints[] = +{ + { 520.3911f, 548.7895f, 732.0118f, 5.0091f }, + { }, + { 550.9611f, 545.1674f, 731.9031f, 3.996804f } +}; +class npc_anub_ar_crusher_crypt_fiend : public CreatureScript +{ + public: + npc_anub_ar_crusher_crypt_fiend() : CreatureScript("npc_anub_ar_crusher_crypt_fiend") { } + + struct npc_anub_ar_crusher_crypt_fiendAI : public npc_hadronox_crusherPackAI + { + npc_anub_ar_crusher_crypt_fiendAI(Creature* creature) : npc_hadronox_crusherPackAI(creature, cryptFiendWaypoints) { } - // Without he comes up through the air to players on the bridge after krikthir if players crossing this bridge! - CheckDistance(fMaxDistance, diff); + void DoEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_CRUSHING_WEBS: + DoCastVictim(SPELL_CRUSHING_WEBS); + _events.Repeat(randtime(Seconds(12), Seconds(16))); + break; + case EVENT_INFECTED_WOUND: + DoCastVictim(SPELL_INFECTED_WOUND); + _events.Repeat(randtime(Seconds(16), Seconds(25))); + break; + } + } - if (me->HasAura(SPELL_WEB_FRONT_DOORS) || me->HasAura(SPELL_WEB_SIDE_DOORS)) + void _EnterCombat() override { - if (IsCombatMovementAllowed()) - SetCombatMovement(false); + _events.ScheduleEvent(EVENT_CRUSHING_WEBS, randtime(Seconds(4), Seconds(8))); + _events.ScheduleEvent(EVENT_INFECTED_WOUND, randtime(Seconds(15), Seconds(19))); } - else if (!IsCombatMovementAllowed()) - SetCombatMovement(true); + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_anub_ar_crusher_crypt_fiendAI>(creature); + } +}; + +static const Position necromancerWaypoints[] = +{ + { }, + { 507.6937f, 563.3471f, 734.8986f, 4.520403f }, + { 535.1049f, 552.8961f, 732.8441f, 3.996804f }, +}; +class npc_anub_ar_crusher_necromancer : public CreatureScript +{ + public: + npc_anub_ar_crusher_necromancer() : CreatureScript("npc_anub_ar_crusher_necromancer") { } + + struct npc_anub_ar_crusher_necromancerAI : public npc_hadronox_crusherPackAI + { + npc_anub_ar_crusher_necromancerAI(Creature* creature) : npc_hadronox_crusherPackAI(creature, necromancerWaypoints) { } - if (uiPierceTimer <= diff) + void DoEvent(uint32 eventId) override { - DoCastVictim(SPELL_PIERCE_ARMOR); - uiPierceTimer = 8*IN_MILLISECONDS; - } else uiPierceTimer -= diff; + switch (eventId) + { + case EVENT_SHADOW_BOLT: + DoCastVictim(SPELL_SHADOW_BOLT); + _events.Repeat(randtime(Seconds(2), Seconds(5))); + break; + case EVENT_ANIMATE_BONES: + DoCastVictim(urand(0, 1) ? SPELL_ANIMATE_BONES_2 : SPELL_ANIMATE_BONES_1); + _events.Repeat(randtime(Seconds(35), Seconds(50))); + break; + } + } - if (uiAcidTimer <= diff) + void _EnterCombat() override { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_ACID_CLOUD); + _events.ScheduleEvent(EVENT_SHADOW_BOLT, randtime(Seconds(2), Seconds(4))); + _events.ScheduleEvent(EVENT_ANIMATE_BONES, randtime(Seconds(37), Seconds(45))); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_anub_ar_crusher_necromancerAI>(creature); + } +}; + +static const uint8 NUM_SPAWNS = 3; +static const Position initialMoves[NUM_SPAWNS] = +{ + { 485.314606f, 611.418640f, 771.428406f }, + { 575.760437f, 611.516418f, 771.427368f }, + { 588.930725f, 598.233276f, 739.142151f } +}; +static const Position downstairsMoves[NUM_SPAWNS] = +{ + { 513.574341f, 587.022156f, 736.229065f }, + { 537.920410f, 580.436157f, 732.796692f }, + { 601.289246f, 583.259644f, 725.443054f }, +}; +static const Position downstairsMoves2[NUM_SPAWNS] = +{ + { 571.498718f, 576.978333f, 727.582947f }, + { 571.498718f, 576.978333f, 727.582947f }, + { } +}; +struct npc_hadronox_foeAI : public ScriptedAI +{ + npc_hadronox_foeAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _nextMovement(MOVE_OUTSIDE), _mySpawn(0) { } + + void InitializeAI() override + { + ScriptedAI::InitializeAI(); + if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX)) + hadronox->AI()->SetGUID(me->GetGUID()); + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE) + _nextMovement = Movements(id+1); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + me->DespawnOrUnsummon(); + } + + virtual void DoEvent(uint32 /*eventId*/) = 0; + + void UpdateAI(uint32 diff) override + { + if (_nextMovement) + { + switch (_nextMovement) + { + case MOVE_OUTSIDE: + { + float dist = HUGE_VALF; + for (uint8 spawn = 0; spawn < NUM_SPAWNS; ++spawn) + { + float thisDist = initialMoves[spawn].GetExactDistSq(me); + if (thisDist < dist) + { + _mySpawn = spawn; + dist = thisDist; + } + } + me->GetMotionMaster()->MovePoint(MOVE_OUTSIDE, initialMoves[_mySpawn], false); // do not pathfind here, we have to pass through a "wall" of webbing + break; + } + case MOVE_DOWNSTAIRS: + me->GetMotionMaster()->MovePoint(MOVE_DOWNSTAIRS, downstairsMoves[_mySpawn]); + break; + case MOVE_DOWNSTAIRS_2: + if (downstairsMoves2[_mySpawn].GetPositionX() > 0.0f) // might be unset for this spawn - if yes, skip + { + me->GetMotionMaster()->MovePoint(MOVE_DOWNSTAIRS_2, downstairsMoves2[_mySpawn]); + break; + } + // intentional missing break + case MOVE_HADRONOX: + case MOVE_HADRONOX_REAL: + { + static const float zCutoff = 702.0f; + Creature* hadronox = _instance->GetCreature(DATA_HADRONOX); + if (hadronox && hadronox->IsAlive()) + { + if (_nextMovement != MOVE_HADRONOX_REAL) + if (hadronox->GetPositionZ() < zCutoff) + { + me->GetMotionMaster()->MovePoint(MOVE_HADRONOX, hadronoxStep[2]); + break; + } + AttackStart(hadronox); + } + break; + } + default: + break; + } + _nextMovement = MOVE_NONE; + } + + if (!UpdateVictim()) + return; + + _events.Update(diff); - uiAcidTimer = urand(20*IN_MILLISECONDS, 30*IN_MILLISECONDS); - } else uiAcidTimer -= diff; + while (uint32 eventId = _events.ExecuteEvent()) + DoEvent(eventId); - if (uiLeechTimer <= diff) + DoMeleeAttackIfReady(); + } + + protected: + EventMap _events; + InstanceScript* const _instance; + + private: + Movements _nextMovement; + uint8 _mySpawn; +}; + +class npc_anub_ar_champion : public CreatureScript +{ + public: + npc_anub_ar_champion() : CreatureScript("npc_anub_ar_champion") { } + + struct npc_anub_ar_championAI : public npc_hadronox_foeAI + { + npc_anub_ar_championAI(Creature* creature) : npc_hadronox_foeAI(creature) { } + + void DoEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_REND: + DoCastVictim(SPELL_REND); + _events.Repeat(randtime(Seconds(12), Seconds(16))); + break; + case EVENT_PUMMEL: + DoCastVictim(SPELL_PUMMEL); + _events.Repeat(randtime(Seconds(12), Seconds(17))); + break; + case EVENT_TAUNT: + DoCastVictim(SPELL_TAUNT); + _events.Repeat(randtime(Seconds(15), Seconds(50))); + break; + } + } + + void EnterCombat(Unit* /*who*/) override { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_LEECH_POISON); + _events.ScheduleEvent(EVENT_REND, randtime(Seconds(4), Seconds(8))); + _events.ScheduleEvent(EVENT_PUMMEL, randtime(Seconds(15), Seconds(19))); + _events.ScheduleEvent(EVENT_TAUNT, randtime(Seconds(15), Seconds(50))); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_anub_ar_championAI>(creature); + } +}; + +class npc_anub_ar_crypt_fiend : public CreatureScript +{ + public: + npc_anub_ar_crypt_fiend() : CreatureScript("npc_anub_ar_crypt_fiend") { } - uiLeechTimer = urand(11*IN_MILLISECONDS, 14*IN_MILLISECONDS); - } else uiLeechTimer -= diff; + struct npc_anub_ar_crypt_fiendAI : public npc_hadronox_foeAI + { + npc_anub_ar_crypt_fiendAI(Creature* creature) : npc_hadronox_foeAI(creature) { } - if (uiGrabTimer <= diff) + void DoEvent(uint32 eventId) override + { + switch (eventId) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) // Draws all players (and attacking Mobs) to itself. - DoCast(target, SPELL_WEB_GRAB); + case EVENT_CRUSHING_WEBS: + DoCastVictim(SPELL_CRUSHING_WEBS); + _events.Repeat(randtime(Seconds(12), Seconds(16))); + break; + case EVENT_INFECTED_WOUND: + DoCastVictim(SPELL_INFECTED_WOUND); + _events.Repeat(randtime(Seconds(16), Seconds(25))); + break; + case EVENT_TAUNT: + DoCastVictim(SPELL_TAUNT); + _events.Repeat(randtime(Seconds(15), Seconds(50))); + break; + } + } - uiGrabTimer = urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS); - } else uiGrabTimer -= diff; + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_CRUSHING_WEBS, randtime(Seconds(4), Seconds(8))); + _events.ScheduleEvent(EVENT_INFECTED_WOUND, randtime(Seconds(15), Seconds(19))); + _events.ScheduleEvent(EVENT_TAUNT, randtime(Seconds(15), Seconds(50))); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_anub_ar_crypt_fiendAI>(creature); + } +}; + +class npc_anub_ar_necromancer : public CreatureScript +{ + public: + npc_anub_ar_necromancer() : CreatureScript("npc_anub_ar_necromancer") { } + + struct npc_anub_ar_necromancerAI : public npc_hadronox_foeAI + { + npc_anub_ar_necromancerAI(Creature* creature) : npc_hadronox_foeAI(creature) { } - if (uiDoorsTimer <= diff) + void DoEvent(uint32 eventId) override + { + switch (eventId) { - uiDoorsTimer = urand(30*IN_MILLISECONDS, 60*IN_MILLISECONDS); - } else uiDoorsTimer -= diff; + case EVENT_SHADOW_BOLT: + DoCastVictim(SPELL_SHADOW_BOLT); + _events.Repeat(randtime(Seconds(2), Seconds(5))); + break; + case EVENT_ANIMATE_BONES: + DoCastVictim(urand(0,1) ? SPELL_ANIMATE_BONES_2 : SPELL_ANIMATE_BONES_1); + _events.Repeat(randtime(Seconds(35), Seconds(50))); + break; + case EVENT_TAUNT: + DoCastVictim(SPELL_TAUNT); + _events.Repeat(randtime(Seconds(15), Seconds(50))); + break; + } + } - DoMeleeAttackIfReady(); + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_SHADOW_BOLT, randtime(Seconds(2), Seconds(4))); + _events.ScheduleEvent(EVENT_ANIMATE_BONES, randtime(Seconds(37), Seconds(45))); + _events.ScheduleEvent(EVENT_TAUNT, randtime(Seconds(15), Seconds(50))); } }; CreatureAI* GetAI(Creature* creature) const override { - return GetInstanceAI<boss_hadronoxAI>(creature); + return GetInstanceAI<npc_anub_ar_necromancerAI>(creature); } }; +class spell_hadronox_periodic_summon_template_AuraScript : public AuraScript +{ + public: + spell_hadronox_periodic_summon_template_AuraScript(uint32 topSpellId, uint32 bottomSpellId) : AuraScript(), _topSpellId(topSpellId), _bottomSpellId(bottomSpellId) { } + PrepareAuraScript(spell_hadronox_periodic_summon_template_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return + (sSpellMgr->GetSpellInfo(_topSpellId) != nullptr) && + (sSpellMgr->GetSpellInfo(_bottomSpellId) != nullptr); + } + + void HandleApply(AuraEffect const* /*eff*/, AuraEffectHandleModes /*mode*/) + { + if (AuraEffect* effect = GetAura()->GetEffect(EFFECT_0)) + effect->SetPeriodicTimer(urandms(2, 17)); + } + + void HandlePeriodic(AuraEffect const* /*eff*/) + { + Unit* caster = GetCaster(); + if (!caster) + return; + InstanceScript* instance = caster->GetInstanceScript(); + if (!instance) + return; + if (instance->GetBossState(DATA_HADRONOX) == DONE) + GetAura()->Remove(); + else + { + if (caster->GetPositionZ() >= 750.0f) + caster->CastSpell(caster, _topSpellId, true); + else + caster->CastSpell(caster, _bottomSpellId, true); + } + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_hadronox_periodic_summon_template_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_hadronox_periodic_summon_template_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } + + private: + uint32 _topSpellId; + uint32 _bottomSpellId; +}; + +class spell_hadronox_periodic_summon_champion : public SpellScriptLoader +{ + public: + spell_hadronox_periodic_summon_champion() : SpellScriptLoader("spell_hadronox_periodic_summon_champion") { } + + class spell_hadronox_periodic_summon_champion_AuraScript : public spell_hadronox_periodic_summon_template_AuraScript + { + public: + spell_hadronox_periodic_summon_champion_AuraScript() : spell_hadronox_periodic_summon_template_AuraScript(SPELL_SUMMON_CHAMPION_TOP, SPELL_SUMMON_CHAMPION_BOTTOM) { } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hadronox_periodic_summon_champion_AuraScript(); + } +}; + +class spell_hadronox_periodic_summon_crypt_fiend : public SpellScriptLoader +{ + public: + spell_hadronox_periodic_summon_crypt_fiend() : SpellScriptLoader("spell_hadronox_periodic_summon_crypt_fiend") { } + + class spell_hadronox_periodic_summon_crypt_fiend_AuraScript : public spell_hadronox_periodic_summon_template_AuraScript + { + public: + spell_hadronox_periodic_summon_crypt_fiend_AuraScript() : spell_hadronox_periodic_summon_template_AuraScript(SPELL_SUMMON_CRYPT_FIEND_TOP, SPELL_SUMMON_CRYPT_FIEND_BOTTOM) { } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hadronox_periodic_summon_crypt_fiend_AuraScript(); + } +}; + +class spell_hadronox_periodic_summon_necromancer : public SpellScriptLoader +{ + public: + spell_hadronox_periodic_summon_necromancer() : SpellScriptLoader("spell_hadronox_periodic_summon_necromancer") { } + + class spell_hadronox_periodic_summon_necromancer_AuraScript : public spell_hadronox_periodic_summon_template_AuraScript + { + public: + spell_hadronox_periodic_summon_necromancer_AuraScript() : spell_hadronox_periodic_summon_template_AuraScript(SPELL_SUMMON_NECROMANCER_TOP, SPELL_SUMMON_NECROMANCER_BOTTOM) { } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hadronox_periodic_summon_necromancer_AuraScript(); + } +}; + +class spell_hadronox_leeching_poison : public SpellScriptLoader +{ + public: + spell_hadronox_leeching_poison() : SpellScriptLoader("spell_hadronox_leeching_poison") { } + + class spell_hadronox_leeching_poison_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hadronox_leeching_poison_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return sSpellMgr->GetSpellInfo(SPELL_LEECH_POISON_HEAL) != nullptr; + } + + void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEATH) + return; + + if (GetTarget()->IsGuardian()) + return; + + if (Unit* caster = GetCaster()) + caster->CastSpell(caster, SPELL_LEECH_POISON_HEAL, true); + } + + void Register() override + { + OnEffectRemove += AuraEffectRemoveFn(spell_hadronox_leeching_poison_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_LEECH, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hadronox_leeching_poison_AuraScript(); + } +}; + +class spell_hadronox_web_doors : public SpellScriptLoader +{ + public: + spell_hadronox_web_doors() : SpellScriptLoader("spell_hadronox_web_doors") { } + + class spell_hadronox_web_doors_SpellScript : public SpellScript + { + PrepareSpellScript(spell_hadronox_web_doors_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return ( + sSpellMgr->GetSpellInfo(SPELL_SUMMON_CHAMPION_PERIODIC) && + sSpellMgr->GetSpellInfo(SPELL_SUMMON_CRYPT_FIEND_PERIODIC) && + sSpellMgr->GetSpellInfo(SPELL_SUMMON_NECROMANCER_PERIODIC) + ); + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + { + target->RemoveAurasDueToSpell(SPELL_SUMMON_CHAMPION_PERIODIC); + target->RemoveAurasDueToSpell(SPELL_SUMMON_CRYPT_FIEND_PERIODIC); + target->RemoveAurasDueToSpell(SPELL_SUMMON_NECROMANCER_PERIODIC); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_hadronox_web_doors_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_hadronox_web_doors_SpellScript(); + } +}; + +class achievement_hadronox_denied : public AchievementCriteriaScript +{ + public: + achievement_hadronox_denied() : AchievementCriteriaScript("achievement_hadronox_denied") { } + + bool OnCheck(Player* /*player*/, Unit* target) override + { + if (!target) + return false; + + if (Creature* cTarget = target->ToCreature()) + if (!cTarget->AI()->GetData(DATA_HADRONOX_WEBBED_DOORS)) + return true; + + return false; + } +}; + void AddSC_boss_hadronox() { new boss_hadronox(); + + new npc_anub_ar_crusher(); + new npc_anub_ar_crusher_champion(); + new npc_anub_ar_crusher_crypt_fiend(); + new npc_anub_ar_crusher_necromancer(); + + new npc_anub_ar_champion(); + new npc_anub_ar_crypt_fiend(); + new npc_anub_ar_necromancer(); + + new spell_hadronox_periodic_summon_champion(); + new spell_hadronox_periodic_summon_crypt_fiend(); + new spell_hadronox_periodic_summon_necromancer(); + + new spell_hadronox_leeching_poison(); + new spell_hadronox_web_doors(); + + new achievement_hadronox_denied(); } 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 8c977e892df..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 @@ -21,63 +21,105 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" +#include "PassiveAI.h" +#include "SpellAuras.h" #include "azjol_nerub.h" -enum Spells +enum Events { - SPELL_MIND_FLAY = 52586, - SPELL_CURSE_OF_FATIGUE = 52592, - SPELL_FRENZY = 28747, //maybe 53361 - SPELL_SUMMON_SKITTERING_SWARMER = 52438, //AOE Effect 140, maybe 52439 - SPELL_SUMMON_SKITTERING_SWARMER_1 = 52439, //Summon 3x 28735 - SPELL_ACID_SPLASH = 52446, - SPELL_CHARGE = 16979, //maybe is another spell - SPELL_BACKSTAB = 52540, - SPELL_SHADOW_BOLT = 52534, - SPELL_SHADOW_NOVA = 52535, - SPELL_STRIKE = 52532, - SPELL_CLEAVE = 49806, - SPELL_ENRAGE = 52470, - SPELL_INFECTED_BITE = 52469, - SPELL_WEB_WRAP = 52086, //the spell is not working properly - SPELL_BLINDING_WEBS = 52524, - SPELL_POSION_SPRAY = 52493 + // Krik'thir the Gatewatcher + EVENT_SEND_GROUP = 1, + EVENT_SWARM, + EVENT_MIND_FLAY, + EVENT_FRENZY, + + // Watchers - Shared + EVENT_WEB_WRAP, + EVENT_INFECTED_BITE, + + // Watcher Gashra + EVENT_ENRAGE, + // Watcher Narjil + EVENT_BLINDING_WEBS, + // Watcher Silthik + EVENT_POISON_SPRAY, + + // Anubar Skirmisher + EVENT_ANUBAR_CHARGE, + EVENT_BACKSTAB, + + // Anubar Shadowcaster + EVENT_SHADOW_BOLT, + EVENT_SHADOW_NOVA, + + // Anubar Warrior + EVENT_STRIKE, + EVENT_CLEAVE }; -enum Mobs +enum Spells { - NPC_SKITTERING_SWARMER = 28735, - NPC_SKITTERING_SWARMER_CONTROLLER = 32593, - NPC_SKITTERING_INFECTIOR = 28736 + // Krik'thir the Gatewatcher + SPELL_SUBBOSS_AGGRO_TRIGGER = 52343, + SPELL_SWARM = 52440, + SPELL_MIND_FLAY = 52586, + SPELL_CURSE_OF_FATIGUE = 52592, + SPELL_FRENZY = 28747, + + // Watchers - Shared + SPELL_WEB_WRAP = 52086, + SPELL_WEB_WRAP_WRAPPED = 52087, + SPELL_INFECTED_BITE = 52469, + + // Watcher Gashra + SPELL_ENRAGE = 52470, + // Watcher Narjil + SPELL_BLINDING_WEBS = 52524, + // Watcher Silthik + SPELL_POISON_SPRAY = 52493, + + // Anub'ar Warrior + SPELL_CLEAVE = 49806, + SPELL_STRIKE = 52532, + + // Anub'ar Skirmisher + SPELL_CHARGE = 52538, + SPELL_BACKSTAB = 52540, + SPELL_FIXTATE_TRIGGER = 52536, + SPELL_FIXTATE_TRIGGERED = 52537, + + // Anub'ar Shadowcaster + SPELL_SHADOW_BOLT = 52534, + SPELL_SHADOW_NOVA = 52535, + + // Skittering Infector + SPELL_ACID_SPLASH = 52446 }; -enum Yells +enum Data { - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_DEATH = 2, - SAY_SWARM = 3, - SAY_PREFIGHT = 4, - SAY_SEND_GROUP = 5 + DATA_PET_GROUP }; -Position const SpawnPoint[] = +enum Actions { - { 566.164f, 682.087f, 769.079f, 2.21657f }, - { 529.042f, 706.941f, 777.298f, 1.0821f }, - { 489.975f, 671.239f, 772.131f, 0.261799f }, - { 488.556f, 692.95f, 771.764f, 4.88692f }, - { 553.34f, 640.387f, 777.419f, 1.20428f }, - { 517.486f, 706.398f, 777.335f, 5.35816f }, - { 504.01f, 637.693f, 777.479f, 0.506145f }, - { 552.625f, 706.408f, 777.177f, 3.4383f } + ACTION_GASHRA_DIED, + ACTION_NARJIL_DIED, + ACTION_SILTHIK_DIED, + ACTION_WATCHER_ENGAGED, + ACTION_PET_ENGAGED, + ACTION_PET_EVADE }; -enum Events +enum Yells { - EVENT_SUMMON = 1, - EVENT_MIND_FLAY, - EVENT_CURSE_FATIGUE + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_DEATH = 2, + SAY_SWARM = 3, + SAY_PREFIGHT = 4, + SAY_SEND_GROUP = 5 }; class boss_krik_thir : public CreatureScript @@ -87,37 +129,128 @@ class boss_krik_thir : public CreatureScript struct boss_krik_thirAI : public BossAI { - boss_krik_thirAI(Creature* creature) : BossAI(creature, DATA_KRIKTHIR_THE_GATEWATCHER) { } + boss_krik_thirAI(Creature* creature) : BossAI(creature, DATA_KRIKTHIR), _hadGreet(false), _hadFrenzy(false), _petsInCombat(false), _watchersActive(0) { } + + void SummonAdds() + { + if (instance->GetBossState(DATA_KRIKTHIR) == DONE) + return; + + for (uint8 i = 1; i <= 3; ++i) + { + std::list<TempSummon*> adds; + me->SummonCreatureGroup(i, &adds); + for (TempSummon* add : adds) + add->AI()->SetData(DATA_PET_GROUP, i); + } + } void Reset() override { - _Reset(); + BossAI::Reset(); + _hadFrenzy = false; + _petsInCombat = false; + _watchersActive = 0; + me->SetReactState(REACT_PASSIVE); + } + + void InitializeAI() override + { + BossAI::InitializeAI(); + SummonAdds(); + } + + void JustRespawned() override + { + BossAI::JustRespawned(); + SummonAdds(); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void JustDied(Unit* killer) override + { + summons.clear(); + BossAI::JustDied(killer); + Talk(SAY_DEATH); } - void EnterCombat(Unit* /*who*/) override + void EnterCombat(Unit* who) override { - Talk(SAY_AGGRO); - _EnterCombat(); - Summon(); + _petsInCombat = false; + me->SetReactState(REACT_AGGRESSIVE); + summons.DoZoneInCombat(); + + events.CancelEvent(EVENT_SEND_GROUP); + events.ScheduleEvent(EVENT_SWARM, Seconds(5)); + events.ScheduleEvent(EVENT_MIND_FLAY, randtime(Seconds(1), Seconds(3))); - events.ScheduleEvent(EVENT_SUMMON, 15000); - events.ScheduleEvent(EVENT_MIND_FLAY, 15000); - events.ScheduleEvent(EVENT_CURSE_FATIGUE, 12000); + BossAI::EnterCombat(who); } - void Summon() + void MoveInLineOfSight(Unit* who) override { - for (uint8 i = 0; i < 8; i++) + if (!me->HasReactState(REACT_PASSIVE)) { - me->SummonCreature(NPC_SKITTERING_SWARMER, SpawnPoint[i], TEMPSUMMON_TIMED_DESPAWN, 25000); - uint32 summon_entry = (i == 4 || i == 5 || i == 6) ? NPC_SKITTERING_INFECTIOR : NPC_SKITTERING_SWARMER; - me->SummonCreature(summon_entry, SpawnPoint[i], TEMPSUMMON_TIMED_DESPAWN, 25000); + ScriptedAI::MoveInLineOfSight(who); + return; + } + + if (me->CanStartAttack(who, false) && me->IsWithinDistInMap(who, me->GetAttackDistance(who) + me->m_CombatDistance)) + EnterCombat(who); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + summons.DespawnAll(); + _DespawnAtEvade(); + } + + void DoAction(int32 action) override + { + switch (action) + { + case -ACTION_GATEWATCHER_GREET: + if (!_hadGreet && me->IsAlive() && !me->IsInCombat() && !_petsInCombat) + { + _hadGreet = true; + Talk(SAY_PREFIGHT); + } + break; + case ACTION_GASHRA_DIED: + case ACTION_NARJIL_DIED: + case ACTION_SILTHIK_DIED: + if (!_watchersActive) // something is wrong + { + EnterEvadeMode(EVADE_REASON_OTHER); + return; + } + if (!--_watchersActive) // if there are no watchers currently in combat... + events.RescheduleEvent(EVENT_SEND_GROUP, Seconds(5)); // ...send the next watcher after the targets sooner + break; + case ACTION_WATCHER_ENGAGED: + ++_watchersActive; + break; + case ACTION_PET_ENGAGED: + if (_petsInCombat || me->IsInCombat()) + break; + _petsInCombat = true; + Talk(SAY_AGGRO); + events.ScheduleEvent(EVENT_SEND_GROUP, Seconds(70)); + break; + case ACTION_PET_EVADE: + EnterEvadeMode(EVADE_REASON_OTHER); + break; } } void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) + if (!UpdateVictim() && !_petsInCombat) return; events.Update(diff); @@ -125,128 +258,183 @@ class boss_krik_thir : public CreatureScript if (me->HasUnitState(UNIT_STATE_CASTING)) return; + if (me->HealthBelowPct(10) && !_hadFrenzy) + { + _hadFrenzy = true; + events.ScheduleEvent(EVENT_FRENZY, Seconds(1)); + } + while (uint32 eventId = events.ExecuteEvent()) { switch (eventId) { - case EVENT_SUMMON: - Summon(); - events.ScheduleEvent(EVENT_SUMMON, 15000); + case EVENT_SEND_GROUP: + DoCastAOE(SPELL_SUBBOSS_AGGRO_TRIGGER, true); + events.Repeat(Seconds(70)); + break; + + case EVENT_SWARM: + DoCastAOE(SPELL_SWARM); + Talk(SAY_SWARM); break; + case EVENT_MIND_FLAY: DoCastVictim(SPELL_MIND_FLAY); - events.ScheduleEvent(EVENT_MIND_FLAY, 15000); - break; - case EVENT_CURSE_FATIGUE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_CURSE_OF_FATIGUE); - events.ScheduleEvent(EVENT_CURSE_FATIGUE, 10000); + events.Repeat(randtime(Seconds(9), Seconds(11))); break; - default: + + case EVENT_FRENZY: + DoCastSelf(SPELL_FRENZY); + DoCastAOE(SPELL_CURSE_OF_FATIGUE); + events.Repeat(Seconds(15)); break; } - } - if (!me->HasAura(SPELL_FRENZY) && HealthBelowPct(10)) - DoCast(me, SPELL_FRENZY, true); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } DoMeleeAttackIfReady(); } - void JustDied(Unit* /*killer*/) override + void SpellHit(Unit* /*whose*/, SpellInfo const* spell) override { - Talk(SAY_DEATH); - _JustDied(); + if (spell->Id == SPELL_SUBBOSS_AGGRO_TRIGGER) + DoZoneInCombat(); } - void KilledUnit(Unit* victim) override + void SpellHitTarget(Unit* /*who*/, SpellInfo const* spell) override { - if (victim->GetTypeId() != TYPEID_PLAYER) - return; - - Talk(SAY_SLAY); + if (spell->Id == SPELL_SUBBOSS_AGGRO_TRIGGER) + Talk(SAY_SEND_GROUP); } - void JustSummoned(Creature* summoned) override - { - summoned->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); - } + private: + bool _hadGreet; + bool _hadFrenzy; + bool _petsInCombat; + uint8 _watchersActive; }; CreatureAI* GetAI(Creature* creature) const override { - return GetAzjolNerubAI<boss_krik_thirAI>(creature); + return GetInstanceAI<boss_krik_thirAI>(creature); } }; -class npc_skittering_infector : public CreatureScript +struct npc_gatewatcher_petAI : public ScriptedAI { - public: - npc_skittering_infector() : CreatureScript("npc_skittering_infector") { } + npc_gatewatcher_petAI(Creature* creature, bool isWatcher) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _petGroup(0), _isWatcher(isWatcher) { } - struct npc_skittering_infectorAI : public ScriptedAI + virtual void _EnterCombat() = 0; + void EnterCombat(Unit* who) override + { + if (_isWatcher) { - npc_skittering_infectorAI(Creature* creature) : ScriptedAI(creature) { } - - void JustDied(Unit* /*killer*/) override - { - DoCastAOE(SPELL_ACID_SPLASH); - } - }; + _isWatcher = false; + if (TempSummon* meSummon = me->ToTempSummon()) + if (Creature* summoner = meSummon->GetSummonerCreatureBase()) + summoner->AI()->DoAction(ACTION_WATCHER_ENGAGED); + } - CreatureAI* GetAI(Creature* creature) const override + if (me->HasReactState(REACT_PASSIVE)) { - return GetAzjolNerubAI<npc_skittering_infectorAI>(creature); - } -}; + std::list<Creature*> others; + me->GetCreatureListWithEntryInGrid(others, 0, 40.0f); + for (Creature* other : others) + if (other->AI()->GetData(DATA_PET_GROUP) == _petGroup) + { + other->SetReactState(REACT_AGGRESSIVE); + other->AI()->AttackStart(who); + } -enum TrashEvents -{ - // Anubar Skrimisher - EVENT_ANUBAR_CHARGE = 1, - EVENT_BACKSTAB, + if (TempSummon* meSummon = me->ToTempSummon()) + if (Creature* summoner = meSummon->GetSummonerCreatureBase()) + summoner->AI()->DoAction(ACTION_PET_ENGAGED); + } + _EnterCombat(); + ScriptedAI::EnterCombat(who); + } - // Anubar Shadowcaster - EVENT_SHADOW_BOLT, - EVENT_SHADOW_NOVA, + void SetData(uint32 data, uint32 value) override + { + if (data == DATA_PET_GROUP) + { + _petGroup = value; + me->SetReactState(_petGroup ? REACT_PASSIVE : REACT_AGGRESSIVE); + } + } + + uint32 GetData(uint32 data) const override + { + if (data == DATA_PET_GROUP) + return _petGroup; + return 0; + } + + void MoveInLineOfSight(Unit* who) override + { + if (!me->HasReactState(REACT_PASSIVE)) + { + ScriptedAI::MoveInLineOfSight(who); + return; + } - // Anubar Warrior - EVENT_STRIKE, - EVENT_CLEAVE, + if (me->CanStartAttack(who, false) && me->IsWithinDistInMap(who, me->GetAttackDistance(who) + me->m_CombatDistance)) + EnterCombat(who); + } - // Watcher Gashra - EVENT_WEB_WRAP_GASHRA, - EVENT_INFECTED_BITE_GASHRA, + void SpellHit(Unit* /*whose*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_SUBBOSS_AGGRO_TRIGGER) + DoZoneInCombat(); + } - // Watcher Narjil - EVENT_WEB_WRAP_NARJIL, - EVENT_INFECTED_BITE_NARJIL, - EVENT_BINDING_WEBS, + void EnterEvadeMode(EvadeReason why) override + { + if (TempSummon* meSummon = me->ToTempSummon()) + { + if (Creature* summoner = meSummon->GetSummonerCreatureBase()) + summoner->AI()->DoAction(ACTION_PET_EVADE); + else + me->DespawnOrUnsummon(); + return; + } + ScriptedAI::EnterEvadeMode(why); + } - // Watcher Silthik - EVENT_WEB_WRAP_SILTHIK, - EVENT_INFECTED_BITE_SILTHIK, - EVENT_POISON_SPRAY + EventMap _events; + InstanceScript* _instance; + uint32 _petGroup; + bool _isWatcher; }; -class npc_anub_ar_skirmisher : public CreatureScript +class npc_watcher_gashra : public CreatureScript { public: - npc_anub_ar_skirmisher() : CreatureScript("npc_anub_ar_skirmisher") { } + npc_watcher_gashra() : CreatureScript("npc_watcher_gashra") { } - struct npc_anub_ar_skirmisherAI : public ScriptedAI + struct npc_watcher_gashraAI : public npc_gatewatcher_petAI { - npc_anub_ar_skirmisherAI(Creature* creature) : ScriptedAI(creature) { } + npc_watcher_gashraAI(Creature* creature) : npc_gatewatcher_petAI(creature, true) { } void Reset() override { - events.Reset(); + _events.Reset(); } - void EnterCombat(Unit* /*who*/) override + void _EnterCombat() override { - events.ScheduleEvent(EVENT_ANUBAR_CHARGE, 11000); - events.ScheduleEvent(EVENT_BACKSTAB, 7000); + _events.ScheduleEvent(EVENT_ENRAGE, randtime(Seconds(3), Seconds(5))); + _events.ScheduleEvent(EVENT_WEB_WRAP, randtime(Seconds(16), Seconds(19))); + _events.ScheduleEvent(EVENT_INFECTED_BITE, randtime(Seconds(7),Seconds(11))); + } + + void JustDied(Unit* /*killer*/) override + { + Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR); + if (krikthir && krikthir->IsAlive()) + krikthir->AI()->DoAction(ACTION_GASHRA_DIED); } void UpdateAI(uint32 diff) override @@ -254,64 +442,77 @@ class npc_anub_ar_skirmisher : public CreatureScript if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { - case EVENT_ANUBAR_CHARGE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - { - DoResetThreat(); - me->AddThreat(target, 1.0f); - DoCast(target, SPELL_CHARGE, true); - } - events.ScheduleEvent(EVENT_ANUBAR_CHARGE, 15000); + case EVENT_ENRAGE: + DoCastSelf(SPELL_ENRAGE); + _events.Repeat(randtime(Seconds(12), Seconds(20))); break; - case EVENT_BACKSTAB: - DoCastVictim(SPELL_BACKSTAB); - events.ScheduleEvent(EVENT_BACKSTAB, 12000); + case EVENT_WEB_WRAP: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f)) + DoCast(target, SPELL_WEB_WRAP); + _events.Repeat(randtime(Seconds(13), Seconds(19))); + break; + case EVENT_INFECTED_BITE: + DoCastVictim(SPELL_INFECTED_BITE); + _events.Repeat(randtime(Seconds(23), Seconds(27))); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); } private: - EventMap events; + EventMap _events; }; CreatureAI* GetAI(Creature* creature) const override { - return GetAzjolNerubAI<npc_anub_ar_skirmisherAI>(creature); + return GetInstanceAI<npc_watcher_gashraAI>(creature); } }; -class npc_anub_ar_shadowcaster : public CreatureScript +class npc_watcher_narjil : public CreatureScript { public: - npc_anub_ar_shadowcaster() : CreatureScript("npc_anub_ar_shadowcaster") { } + npc_watcher_narjil() : CreatureScript("npc_watcher_narjil") { } - struct npc_anub_ar_shadowcasterAI : public ScriptedAI + struct npc_watcher_narjilAI : public npc_gatewatcher_petAI { - npc_anub_ar_shadowcasterAI(Creature* creature) : ScriptedAI(creature) { } + npc_watcher_narjilAI(Creature* creature) : npc_gatewatcher_petAI(creature, true) + { + } void Reset() override { - events.Reset(); + _events.Reset(); + } + + void _EnterCombat() override + { + _events.ScheduleEvent(EVENT_BLINDING_WEBS, randtime(Seconds(13), Seconds(18))); + _events.ScheduleEvent(EVENT_WEB_WRAP, randtime(Seconds(3), Seconds(5))); + _events.ScheduleEvent(EVENT_INFECTED_BITE, randtime(Seconds(7), Seconds(11))); } - void EnterCombat(Unit* /*who*/) override + void JustDied(Unit* /*killer*/) override { - events.ScheduleEvent(EVENT_SHADOW_BOLT, 6000); - events.ScheduleEvent(EVENT_SHADOW_NOVA, 15000); + Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR); + if (krikthir && krikthir->IsAlive()) + krikthir->AI()->DoAction(ACTION_NARJIL_DIED); } void UpdateAI(uint32 diff) override @@ -319,60 +520,77 @@ class npc_anub_ar_shadowcaster : public CreatureScript if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { - case EVENT_SHADOW_BOLT: + case EVENT_BLINDING_WEBS: + DoCastVictim(SPELL_BLINDING_WEBS); + _events.Repeat(randtime(Seconds(23), Seconds(27))); + break; + case EVENT_WEB_WRAP: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_SHADOW_BOLT, true); - events.ScheduleEvent(EVENT_SHADOW_BOLT, 15000); + DoCast(target, SPELL_WEB_WRAP); + _events.Repeat(randtime(Seconds(13), Seconds(19))); break; - case EVENT_SHADOW_NOVA: - DoCastVictim(SPELL_SHADOW_NOVA, true); - events.ScheduleEvent(EVENT_SHADOW_NOVA, 17000); + case EVENT_INFECTED_BITE: + DoCastVictim(SPELL_INFECTED_BITE); + _events.Repeat(randtime(Seconds(20), Seconds(25))); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); } private: - EventMap events; + EventMap _events; }; CreatureAI* GetAI(Creature* creature) const override { - return GetAzjolNerubAI<npc_anub_ar_shadowcasterAI>(creature); + return GetInstanceAI<npc_watcher_narjilAI>(creature); } }; -class npc_anub_ar_warrior : public CreatureScript +class npc_watcher_silthik : public CreatureScript { public: - npc_anub_ar_warrior() : CreatureScript("npc_anub_ar_warrior") { } + npc_watcher_silthik() : CreatureScript("npc_watcher_silthik") { } - struct npc_anub_ar_warriorAI : public ScriptedAI + struct npc_watcher_silthikAI : public npc_gatewatcher_petAI { - npc_anub_ar_warriorAI(Creature* creature) : ScriptedAI(creature) { } + npc_watcher_silthikAI(Creature* creature) : npc_gatewatcher_petAI(creature, true) + { + } void Reset() override { - events.Reset(); + _events.Reset(); + } + + void _EnterCombat() override + { + _events.ScheduleEvent(EVENT_POISON_SPRAY, randtime(Seconds(16), Seconds(19))); + _events.ScheduleEvent(EVENT_WEB_WRAP, randtime(Seconds(7), Seconds(11))); + _events.ScheduleEvent(EVENT_INFECTED_BITE, randtime(Seconds(3), Seconds(5))); } - void EnterCombat(Unit* /*who*/) override + void JustDied(Unit* /*killer*/) override { - events.ScheduleEvent(EVENT_CLEAVE, 11000); - events.ScheduleEvent(EVENT_STRIKE, 6000); + Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR); + if (krikthir && krikthir->IsAlive()) + krikthir->AI()->DoAction(ACTION_SILTHIK_DIED); } void UpdateAI(uint32 diff) override @@ -380,70 +598,67 @@ class npc_anub_ar_warrior : public CreatureScript if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { - case EVENT_CLEAVE: - DoCastVictim(SPELL_STRIKE, true); - events.ScheduleEvent(EVENT_CLEAVE, 15000); + case EVENT_POISON_SPRAY: + DoCastVictim(SPELL_POISON_SPRAY); + _events.Repeat(randtime(Seconds(13), Seconds(19))); break; - case EVENT_STRIKE: - DoCastVictim(SPELL_CLEAVE, true); - events.ScheduleEvent(EVENT_STRIKE, 17000); + case EVENT_WEB_WRAP: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) + DoCast(target, SPELL_WEB_WRAP); + _events.Repeat(randtime(Seconds(13), Seconds(17))); + break; + case EVENT_INFECTED_BITE: + DoCastVictim(SPELL_INFECTED_BITE); + _events.Repeat(randtime(Seconds(20), Seconds(24))); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); } private: - EventMap events; + EventMap _events; }; CreatureAI* GetAI(Creature* creature) const override { - return GetAzjolNerubAI<npc_anub_ar_warriorAI>(creature); + return GetInstanceAI<npc_watcher_silthikAI>(creature); } }; -class npc_watcher_gashra : public CreatureScript +class npc_anub_ar_warrior : public CreatureScript { public: - npc_watcher_gashra() : CreatureScript("npc_watcher_gashra") { } + npc_anub_ar_warrior() : CreatureScript("npc_anub_ar_warrior") { } - struct npc_watcher_gashraAI : public ScriptedAI + struct npc_anub_ar_warriorAI : public npc_gatewatcher_petAI { - npc_watcher_gashraAI(Creature* creature) : ScriptedAI(creature) - { - _instance = creature->GetInstanceScript(); - } + npc_anub_ar_warriorAI(Creature* creature) : npc_gatewatcher_petAI(creature, false) { } void Reset() override { _events.Reset(); } - void EnterCombat(Unit* /*who*/) override - { - DoCast(me, SPELL_ENRAGE, true); - _events.ScheduleEvent(EVENT_WEB_WRAP_GASHRA, 11000); - _events.ScheduleEvent(EVENT_INFECTED_BITE_GASHRA, 4000); - } - - void JustDied(Unit* /*killer*/) override + void _EnterCombat() override { - Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER); - if (krikthir && krikthir->IsAlive()) - krikthir->AI()->Talk(SAY_PREFIGHT); + _events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(7), Seconds(9))); + _events.ScheduleEvent(EVENT_STRIKE, randtime(Seconds(5), Seconds(10))); } void UpdateAI(uint32 diff) override @@ -460,63 +675,50 @@ class npc_watcher_gashra : public CreatureScript { switch (eventId) { - case EVENT_WEB_WRAP_GASHRA: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_WEB_WRAP, true); - _events.ScheduleEvent(EVENT_WEB_WRAP_GASHRA, 17000); + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + _events.Repeat(randtime(Seconds(10), Seconds(16))); break; - case EVENT_INFECTED_BITE_GASHRA: - DoCastVictim(SPELL_INFECTED_BITE, true); - _events.ScheduleEvent(EVENT_INFECTED_BITE_GASHRA, 15000); + case EVENT_STRIKE: + DoCastVictim(SPELL_STRIKE); + _events.Repeat(randtime(Seconds(15), Seconds(19))); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); } - - private: - EventMap _events; - InstanceScript* _instance; }; CreatureAI* GetAI(Creature* creature) const override { - return GetAzjolNerubAI<npc_watcher_gashraAI>(creature); + return GetInstanceAI<npc_anub_ar_warriorAI>(creature); } }; -class npc_watcher_narjil : public CreatureScript +class npc_anub_ar_skirmisher : public CreatureScript { public: - npc_watcher_narjil() : CreatureScript("npc_watcher_narjil") { } + npc_anub_ar_skirmisher() : CreatureScript("npc_anub_ar_skirmisher") { } - struct npc_watcher_narjilAI : public ScriptedAI + struct npc_anub_ar_skirmisherAI : public npc_gatewatcher_petAI { - npc_watcher_narjilAI(Creature* creature) : ScriptedAI(creature) - { - _instance = creature->GetInstanceScript(); - } + npc_anub_ar_skirmisherAI(Creature* creature) : npc_gatewatcher_petAI(creature, false) { } void Reset() override { _events.Reset(); } - void EnterCombat(Unit* /*who*/) override - { - _events.ScheduleEvent(EVENT_WEB_WRAP_NARJIL, 11000); - _events.ScheduleEvent(EVENT_INFECTED_BITE_NARJIL, 4000); - _events.ScheduleEvent(EVENT_BINDING_WEBS, 17000); - } - - void JustDied(Unit* /*killer*/) override + void _EnterCombat() override { - Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER); - if (krikthir && krikthir->IsAlive()) - krikthir->AI()->Talk(SAY_PREFIGHT); + _events.ScheduleEvent(EVENT_ANUBAR_CHARGE, randtime(Seconds(6), Seconds(8))); + _events.ScheduleEvent(EVENT_BACKSTAB, randtime(Seconds(7), Seconds(9))); } void UpdateAI(uint32 diff) override @@ -533,67 +735,58 @@ class npc_watcher_narjil : public CreatureScript { switch (eventId) { - case EVENT_WEB_WRAP_NARJIL: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_WEB_WRAP, true); - _events.ScheduleEvent(EVENT_WEB_WRAP_NARJIL, 15000); - break; - case EVENT_INFECTED_BITE_NARJIL: - DoCastVictim(SPELL_INFECTED_BITE, true); - _events.ScheduleEvent(EVENT_INFECTED_BITE_NARJIL, 11000); + case EVENT_ANUBAR_CHARGE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_CHARGE); + _events.Repeat(randtime(Seconds(20), Seconds(25))); break; - case EVENT_BINDING_WEBS: - DoCastVictim(SPELL_BLINDING_WEBS, true); - _events.ScheduleEvent(EVENT_BINDING_WEBS, 17000); + case EVENT_BACKSTAB: + if (me->GetVictim() && me->GetVictim()->isInBack(me)) + DoCastVictim(SPELL_BACKSTAB); + _events.Repeat(randtime(Seconds(10), Seconds(13))); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); } - private: - EventMap _events; - InstanceScript* _instance; + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + if (spell->Id == SPELL_CHARGE && target) + DoCast(target, SPELL_FIXTATE_TRIGGER); + } }; CreatureAI* GetAI(Creature* creature) const override { - return GetAzjolNerubAI<npc_watcher_narjilAI>(creature); + return GetInstanceAI<npc_anub_ar_skirmisherAI>(creature); } }; -class npc_watcher_silthik : public CreatureScript +class npc_anub_ar_shadowcaster : public CreatureScript { public: - npc_watcher_silthik() : CreatureScript("npc_watcher_silthik") { } + npc_anub_ar_shadowcaster() : CreatureScript("npc_anub_ar_shadowcaster") { } - struct npc_watcher_silthikAI : public ScriptedAI + struct npc_anub_ar_shadowcasterAI : public npc_gatewatcher_petAI { - npc_watcher_silthikAI(Creature* creature) : ScriptedAI(creature) - { - _instance = creature->GetInstanceScript(); - } + npc_anub_ar_shadowcasterAI(Creature* creature) : npc_gatewatcher_petAI(creature, false) { } void Reset() override { _events.Reset(); } - void EnterCombat(Unit* /*who*/) override - { - _events.ScheduleEvent(EVENT_WEB_WRAP_SILTHIK, 11000); - _events.ScheduleEvent(EVENT_INFECTED_BITE_SILTHIK, 4000); - _events.ScheduleEvent(EVENT_POISON_SPRAY, 15000); - } - - void JustDied(Unit* /*killer*/) override + void _EnterCombat() override { - Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER); - if (krikthir && krikthir->IsAlive()) - krikthir->AI()->Talk(SAY_PREFIGHT); + _events.ScheduleEvent(EVENT_SHADOW_BOLT, Seconds(4)); + _events.ScheduleEvent(EVENT_SHADOW_NOVA, randtime(Seconds(10), Seconds(14))); } void UpdateAI(uint32 diff) override @@ -610,35 +803,234 @@ class npc_watcher_silthik : public CreatureScript { switch (eventId) { - case EVENT_WEB_WRAP_SILTHIK: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_WEB_WRAP, true); - _events.ScheduleEvent(EVENT_WEB_WRAP_SILTHIK, 15000); - break; - case EVENT_INFECTED_BITE_SILTHIK: - DoCastVictim(SPELL_INFECTED_BITE, true); - _events.ScheduleEvent(EVENT_INFECTED_BITE_SILTHIK, 11000); + case EVENT_SHADOW_BOLT: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_SHADOW_BOLT); + _events.Repeat(randtime(Seconds(2), Seconds(4))); break; - case EVENT_POISON_SPRAY: - DoCastVictim(SPELL_POSION_SPRAY, true); - _events.ScheduleEvent(EVENT_POISON_SPRAY, 17000); + case EVENT_SHADOW_NOVA: + DoCastVictim(SPELL_SHADOW_NOVA); + _events.Repeat(randtime(Seconds(10), Seconds(16))); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); } + }; - private: - EventMap _events; - InstanceScript* _instance; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_anub_ar_shadowcasterAI>(creature); + } +}; + +class npc_skittering_swarmer : public CreatureScript +{ + public: + npc_skittering_swarmer() : CreatureScript("npc_skittering_swarmer") { } + + struct npc_skittering_swarmerAI : public ScriptedAI + { + npc_skittering_swarmerAI(Creature* creature) : ScriptedAI(creature) { } + + void InitializeAI() override + { + ScriptedAI::InitializeAI(); + if (Creature* gatewatcher = me->GetInstanceScript()->GetCreature(DATA_KRIKTHIR)) + { + if (Unit* target = gatewatcher->getAttackerForHelper()) + AttackStart(target); + gatewatcher->AI()->JustSummoned(me); + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_skittering_swarmerAI>(creature); + } +}; + +class npc_skittering_infector : public CreatureScript +{ + public: + npc_skittering_infector() : CreatureScript("npc_skittering_infector") { } + + struct npc_skittering_infectorAI : public ScriptedAI + { + npc_skittering_infectorAI(Creature* creature) : ScriptedAI(creature) { } + + void InitializeAI() override + { + ScriptedAI::InitializeAI(); + if (Creature* gatewatcher = me->GetInstanceScript()->GetCreature(DATA_KRIKTHIR)) + { + if (Unit* target = gatewatcher->getAttackerForHelper()) + AttackStart(target); + gatewatcher->AI()->JustSummoned(me); + } + } + + void JustDied(Unit* killer) override + { + DoCastAOE(SPELL_ACID_SPLASH); + ScriptedAI::JustDied(killer); + } }; CreatureAI* GetAI(Creature* creature) const override { - return GetAzjolNerubAI<npc_watcher_silthikAI>(creature); + return GetInstanceAI<npc_skittering_infectorAI>(creature); + } +}; + +class npc_gatewatcher_web_wrap : public CreatureScript +{ + public: + npc_gatewatcher_web_wrap() : CreatureScript("npc_gatewatcher_web_wrap") { } + + struct npc_gatewatcher_web_wrapAI : public NullCreatureAI + { + npc_gatewatcher_web_wrapAI(Creature* creature) : NullCreatureAI(creature) { } + + void JustDied(Unit* /*killer*/) override + { + if (TempSummon* meSummon = me->ToTempSummon()) + if (Unit* summoner = meSummon->GetSummoner()) + summoner->RemoveAurasDueToSpell(SPELL_WEB_WRAP_WRAPPED); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_gatewatcher_web_wrapAI>(creature); + } +}; + +class spell_gatewatcher_subboss_trigger : public SpellScriptLoader +{ + public: + spell_gatewatcher_subboss_trigger() : SpellScriptLoader("spell_gatewatcher_subboss_trigger") { } + + class spell_gatewatcher_subboss_trigger_SpellScript : public SpellScript + { + PrepareSpellScript(spell_gatewatcher_subboss_trigger_SpellScript); + + void HandleTargets(std::list<WorldObject*>& targetList) + { + // Remove any Watchers that are already in combat + auto it = targetList.begin(); + while (it != targetList.end()) + { + if (Creature* creature = (*it)->ToCreature()) + if (creature->IsAlive() && !creature->IsInCombat()) + { + ++it; + continue; + } + it = targetList.erase(it); + } + + // Default to Krik'thir himself if he isn't engaged + WorldObject* target = nullptr; + if (GetCaster() && !GetCaster()->IsInCombat()) + target = GetCaster(); + // Unless there are Watchers that aren't engaged yet + if (!targetList.empty()) + { + // If there are, pick one of them at random + std::list<WorldObject*>::iterator it = targetList.begin(); + std::advance(it, urand(0, targetList.size() - 1)); + target = *it; + } + // And hit only that one + targetList.clear(); + if (target) + targetList.push_back(target); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gatewatcher_subboss_trigger_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_gatewatcher_subboss_trigger_SpellScript(); + } +}; + +class spell_anub_ar_skirmisher_fixtate : public SpellScriptLoader +{ + public: + spell_anub_ar_skirmisher_fixtate() : SpellScriptLoader("spell_anub_ar_skirmisher_fixtate") { } + + class spell_anub_ar_skirmisher_fixtate_SpellScript : public SpellScript + { + PrepareSpellScript(spell_anub_ar_skirmisher_fixtate_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return sSpellMgr->GetSpellInfo(SPELL_FIXTATE_TRIGGERED) != nullptr; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + target->CastSpell(GetCaster(), SPELL_FIXTATE_TRIGGERED, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_anub_ar_skirmisher_fixtate_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_anub_ar_skirmisher_fixtate_SpellScript(); + } +}; + +class spell_gatewatcher_web_wrap : public SpellScriptLoader +{ + public: + spell_gatewatcher_web_wrap() : SpellScriptLoader("spell_gatewatcher_web_wrap") { } + + class spell_gatewatcher_web_wrap_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gatewatcher_web_wrap_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return sSpellMgr->GetSpellInfo(SPELL_WEB_WRAP_WRAPPED) != nullptr; + } + + void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE) + return; + + if (Unit* target = GetTarget()) + target->CastSpell(target, SPELL_WEB_WRAP_WRAPPED, true); + } + + void Register() override + { + OnEffectRemove += AuraEffectRemoveFn(spell_gatewatcher_web_wrap_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_MOD_ROOT, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_gatewatcher_web_wrap_AuraScript(); } }; @@ -656,11 +1048,12 @@ class achievement_watch_him_die : public AchievementCriteriaScript if (!instance) return false; - for (uint8 n = 0; n < 3; ++n) + for (DataTypes watcherData : {DATA_WATCHER_GASHRA, DATA_WATCHER_NARJIL, DATA_WATCHER_SILTHIK}) { - if (Creature* watcher = instance->GetCreature(DATA_WATCHER_GASHRA + n)) - if (!watcher->IsAlive()) - return false; + if (Creature* watcher = instance->GetCreature(watcherData)) + if (watcher->IsAlive()) + continue; + return false; } return true; @@ -670,12 +1063,22 @@ class achievement_watch_him_die : public AchievementCriteriaScript void AddSC_boss_krik_thir() { new boss_krik_thir(); - new npc_skittering_infector(); - new npc_anub_ar_skirmisher(); - new npc_anub_ar_shadowcaster(); + new npc_watcher_gashra(); - new npc_anub_ar_warrior(); - new npc_watcher_silthik(); new npc_watcher_narjil(); + new npc_watcher_silthik(); + + new npc_anub_ar_warrior(); + new npc_anub_ar_skirmisher(); + new npc_anub_ar_shadowcaster(); + + new npc_skittering_swarmer(); + new npc_skittering_infector(); + new npc_gatewatcher_web_wrap(); + + new spell_gatewatcher_subboss_trigger(); + new spell_anub_ar_skirmisher_fixtate(); + new spell_gatewatcher_web_wrap(); + new achievement_watch_him_die(); } 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 88003680ec7..7f0ce5c369e 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp @@ -21,7 +21,7 @@ DoorData const doorData[] = { - { GO_KRIKTHIR_DOOR, DATA_KRIKTHIR_THE_GATEWATCHER, DOOR_TYPE_PASSAGE }, + { GO_KRIKTHIR_DOOR, DATA_KRIKTHIR, DOOR_TYPE_PASSAGE }, { GO_ANUBARAK_DOOR_1, DATA_ANUBARAK, DOOR_TYPE_ROOM }, { GO_ANUBARAK_DOOR_2, DATA_ANUBARAK, DOOR_TYPE_ROOM }, { GO_ANUBARAK_DOOR_3, DATA_ANUBARAK, DOOR_TYPE_ROOM }, @@ -30,17 +30,33 @@ DoorData const doorData[] = ObjectData const creatureData[] = { - { NPC_KRIKTHIR, DATA_KRIKTHIR_THE_GATEWATCHER }, + { NPC_KRIKTHIR, DATA_KRIKTHIR }, + { NPC_HADRONOX, DATA_HADRONOX }, + { NPC_ANUBARAK, DATA_ANUBARAK }, { NPC_WATCHER_NARJIL, DATA_WATCHER_GASHRA }, { NPC_WATCHER_GASHRA, DATA_WATCHER_SILTHIK }, { NPC_WATCHER_SILTHIK, DATA_WATCHER_NARJIL }, { 0, 0 } // END }; +ObjectData const gameobjectData[] = +{ + { GO_ANUBARAK_DOOR_1, DATA_ANUBARAK_WALL }, + { GO_ANUBARAK_DOOR_3, DATA_ANUBARAK_WALL_2 }, + { 0, 0 } // END +}; + +BossBoundaryData const boundaries = +{ + { DATA_KRIKTHIR, new RectangleBoundary(400.0f, 580.0f, 623.5f, 810.0f) }, + { DATA_HADRONOX, new ZRangeBoundary(666.0f, 776.0f) }, + { DATA_ANUBARAK, new CircleBoundary(Position(550.6178f, 253.5917f), 26.0f) } +}; + class instance_azjol_nerub : public InstanceMapScript { public: - instance_azjol_nerub() : InstanceMapScript(AzjolNerubScriptName, 601) { } + instance_azjol_nerub() : InstanceMapScript("instance_azjol_nerub", 601) { } struct instance_azjol_nerub_InstanceScript : public InstanceScript { @@ -48,8 +64,30 @@ class instance_azjol_nerub : public InstanceMapScript { SetHeaders(DataHeader); SetBossNumber(EncounterCount); + LoadBossBoundaries(boundaries); LoadDoorData(doorData); - LoadObjectData(creatureData, nullptr); + LoadObjectData(creatureData, gameobjectData); + } + + void OnUnitDeath(Unit* who) override + { + InstanceScript::OnUnitDeath(who); + Creature* creature = who->ToCreature(); + if (!creature || creature->IsCritter() || creature->IsControlledByPlayer()) + return; + 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; } }; diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp index d9200fbb2f5..ee44e1391b4 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp @@ -829,9 +829,6 @@ class npc_toc_druid : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } } }; @@ -925,9 +922,6 @@ class npc_toc_shaman : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } } }; @@ -1032,9 +1026,6 @@ class npc_toc_paladin : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } } }; @@ -1120,9 +1111,6 @@ class npc_toc_priest : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } } }; @@ -1220,9 +1208,6 @@ class npc_toc_shadow_priest : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } DoSpellAttackIfReady(SPELL_MIND_FLAY); } @@ -1314,9 +1299,6 @@ class npc_toc_warlock : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } DoSpellAttackIfReady(SPELL_SHADOW_BOLT); } @@ -1411,9 +1393,6 @@ class npc_toc_mage : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } DoSpellAttackIfReady(SPELL_FROSTBOLT); } @@ -1516,9 +1495,6 @@ class npc_toc_hunter : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } DoSpellAttackIfReady(SPELL_SHOOT); } @@ -1611,9 +1587,6 @@ class npc_toc_boomkin : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } DoSpellAttackIfReady(SPELL_WRATH); } @@ -1719,9 +1692,6 @@ class npc_toc_warrior : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } } }; @@ -1818,9 +1788,6 @@ class npc_toc_dk : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } } }; @@ -1926,9 +1893,6 @@ class npc_toc_rogue : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } } }; @@ -2060,9 +2024,6 @@ class npc_toc_enh_shaman : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } } private: @@ -2172,9 +2133,6 @@ class npc_toc_retro_paladin : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } } }; @@ -2227,9 +2185,6 @@ class npc_toc_pet_warlock : public CreatureScript default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } } }; 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 64299da79e6..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,34 +259,29 @@ 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; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } DoMeleeAttackIfReady(); @@ -295,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: @@ -302,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); } @@ -363,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)) @@ -419,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 @@ -617,9 +628,6 @@ struct boss_jormungarAI : public BossAI default: return; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; } if (events.IsInPhase(PHASE_MOBILE)) DoMeleeAttackIfReady(); @@ -1151,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: @@ -1299,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/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp index 8998b77d8b5..25e2c045af9 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp @@ -406,7 +406,11 @@ class spell_devourer_of_souls_mirrored_soul_proc : public SpellScriptLoader void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) { PreventDefaultAction(); - int32 damage = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), 45)); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 damage = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), 45); GetTarget()->CastCustomSpell(SPELL_MIRRORED_SOUL_DAMAGE, SPELLVALUE_BASE_POINT0, damage, GetCaster(), true); } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp index 28484280ada..707bbd4eb02 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp @@ -1498,6 +1498,26 @@ class spell_taldaram_ball_of_inferno_flame : public SpellScriptLoader { return new spell_taldaram_ball_of_inferno_flame_SpellScript(); } + + class spell_taldaram_ball_of_inferno_flame_AuraScript : public AuraScript + { + PrepareAuraScript(spell_taldaram_ball_of_inferno_flame_AuraScript); + + void HandleStackDrop(ProcEventInfo& /*eventInfo*/) + { + ModStackAmount(-1); + } + + void Register() override + { + OnProc += AuraProcFn(spell_taldaram_ball_of_inferno_flame_AuraScript::HandleStackDrop); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_taldaram_ball_of_inferno_flame_AuraScript(); + } }; // 72080 - Kinetic Bomb (Valanar) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp index a5d6d27724c..dd34f501e61 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp @@ -729,8 +729,12 @@ class spell_blood_queen_essence_of_the_blood_queen : public SpellScriptLoader void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - int32 heal = CalculatePct(int32(eventInfo.GetDamageInfo()->GetDamage()), aurEff->GetAmount()); - GetTarget()->CastCustomSpell(SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_HEAL, SPELLVALUE_BASE_POINT0, heal, GetTarget(), TRIGGERED_FULL_MASK, NULL, aurEff); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 heal = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + GetTarget()->CastCustomSpell(SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_HEAL, SPELLVALUE_BASE_POINT0, heal, GetTarget(), TRIGGERED_FULL_MASK, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp index 23049e82d5e..137cdb28fb5 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp @@ -431,7 +431,7 @@ class boss_deathbringer_saurfang : public CreatureScript case 72445: case 72446: if (me->GetPower(POWER_ENERGY) != me->GetMaxPower(POWER_ENERGY)) - target->CastCustomSpell(SPELL_BLOOD_LINK_DUMMY, SPELLVALUE_BASE_POINT0, 1, me, true); + target->CastCustomSpell(SPELL_BLOOD_LINK_DUMMY, SPELLVALUE_BASE_POINT0, 1, (Unit*)nullptr, true); break; default: break; @@ -1135,8 +1135,7 @@ class spell_deathbringer_rune_of_blood : public SpellScriptLoader void HandleScript(SpellEffIndex effIndex) { PreventHitDefaultEffect(effIndex); // make this the default handler - if (GetCaster()->GetPower(POWER_ENERGY) != GetCaster()->GetMaxPower(POWER_ENERGY)) - GetHitUnit()->CastCustomSpell(SPELL_BLOOD_LINK_DUMMY, SPELLVALUE_BASE_POINT0, 1, GetCaster(), true); + GetHitUnit()->CastCustomSpell(SPELL_BLOOD_LINK_DUMMY, SPELLVALUE_BASE_POINT0, 1, (Unit*)nullptr, true); } void Register() override @@ -1151,6 +1150,41 @@ class spell_deathbringer_rune_of_blood : public SpellScriptLoader } }; +// 72176 - Blood Beast's Blood Link +class spell_deathbringer_blood_beast_blood_link : public SpellScriptLoader +{ + public: + spell_deathbringer_blood_beast_blood_link() : SpellScriptLoader("spell_deathbringer_blood_beast_blood_link") { } + + class spell_deathbringer_blood_beast_blood_link_AuraScript : public AuraScript + { + PrepareAuraScript(spell_deathbringer_blood_beast_blood_link_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_BLOOD_LINK_DUMMY)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetProcTarget()->CastCustomSpell(SPELL_BLOOD_LINK_DUMMY, SPELLVALUE_BASE_POINT0, 3, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_deathbringer_blood_beast_blood_link_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_deathbringer_blood_beast_blood_link_AuraScript(); + } +}; + class spell_deathbringer_blood_nova : public SpellScriptLoader { public: @@ -1170,8 +1204,7 @@ class spell_deathbringer_blood_nova : public SpellScriptLoader void HandleScript(SpellEffIndex effIndex) { PreventHitDefaultEffect(effIndex); // make this the default handler - if (GetCaster()->GetPower(POWER_ENERGY) != GetCaster()->GetMaxPower(POWER_ENERGY)) - GetHitUnit()->CastCustomSpell(SPELL_BLOOD_LINK_DUMMY, SPELLVALUE_BASE_POINT0, 2, GetCaster(), true); + GetHitUnit()->CastCustomSpell(SPELL_BLOOD_LINK_DUMMY, SPELLVALUE_BASE_POINT0, 2, (Unit*)nullptr, true); } void Register() override @@ -1349,6 +1382,7 @@ void AddSC_boss_deathbringer_saurfang() new spell_deathbringer_blood_link_aura(); new spell_deathbringer_blood_power(); new spell_deathbringer_rune_of_blood(); + new spell_deathbringer_blood_beast_blood_link(); new spell_deathbringer_blood_nova(); new spell_deathbringer_blood_nova_targeting(); new spell_deathbringer_boiling_blood(); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp index 73a7de36580..88fd65baec4 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -1721,10 +1721,7 @@ class npc_gunship_mage : public CreatureScript me->SetReactState(REACT_PASSIVE); } - void EnterEvadeMode(EvadeReason why) override - { - ScriptedAI::EnterEvadeMode(why); - } + void EnterEvadeMode(EvadeReason /*why*/) override { } void MovementInform(uint32 type, uint32 pointId) override { 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/IcecrownCitadel/boss_professor_putricide.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp index 376cadd18d9..15e4885d4a7 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp @@ -838,9 +838,23 @@ class spell_putricide_gaseous_bloat : public SpellScriptLoader } } + void HandleProc(ProcEventInfo& eventInfo) + { + uint32 stack = GetStackAmount(); + Unit* caster = eventInfo.GetActor(); + + int32 const mod = caster->GetMap()->Is25ManRaid() ? 1500 : 1250; + int32 dmg = 0; + for (uint8 i = 1; i <= stack; ++i) + dmg += mod * i; + + caster->CastCustomSpell(SPELL_EXPUNGED_GAS, SPELLVALUE_BASE_POINT0, dmg); + } + void Register() override { OnEffectPeriodic += AuraEffectPeriodicFn(spell_putricide_gaseous_bloat_AuraScript::HandleExtraEffect, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + OnProc += AuraProcFn(spell_putricide_gaseous_bloat_AuraScript::HandleProc); } }; @@ -1078,6 +1092,45 @@ class spell_putricide_ooze_eruption_searcher : public SpellScriptLoader } }; +// 71770 - Ooze Spell Tank Protection +class spell_putricide_ooze_tank_protection : public SpellScriptLoader +{ + public: + spell_putricide_ooze_tank_protection() : SpellScriptLoader("spell_putricide_ooze_tank_protection") { } + + class spell_putricide_ooze_tank_protection_AuraScript : public AuraScript + { + PrepareAuraScript(spell_putricide_ooze_tank_protection_AuraScript); + + bool Validate(SpellInfo const* spellInfo) override + { + if (!sSpellMgr->GetSpellInfo(spellInfo->Effects[EFFECT_0].TriggerSpell) || + !sSpellMgr->GetSpellInfo(spellInfo->Effects[EFFECT_1].TriggerSpell)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* actionTarget = eventInfo.GetActionTarget(); + actionTarget->CastSpell((Unit*)nullptr, GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_putricide_ooze_tank_protection_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + OnEffectProc += AuraEffectProcFn(spell_putricide_ooze_tank_protection_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_putricide_ooze_tank_protection_AuraScript(); + } +}; + class spell_putricide_choking_gas_bomb : public SpellScriptLoader { public: @@ -1602,6 +1655,7 @@ void AddSC_boss_professor_putricide() new spell_putricide_slime_puddle_aura(); new spell_putricide_unstable_experiment(); new spell_putricide_ooze_eruption_searcher(); + new spell_putricide_ooze_tank_protection(); new spell_putricide_choking_gas_bomb(); new spell_putricide_unbound_plague(); new spell_putricide_eat_ooze(); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp index a2348119dff..f3021cdbab5 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp @@ -1099,6 +1099,12 @@ class spell_sindragosa_s_fury : public SpellScriptLoader if (!GetHitUnit()->IsAlive() || !_targetCount) return; + if (GetHitUnit()->IsImmunedToDamage(GetSpellInfo())) + { + GetCaster()->SendSpellDamageImmune(GetHitUnit(), GetSpellInfo()->Id); + return; + } + float resistance = float(GetHitUnit()->GetResistance(SpellSchoolMask(GetSpellInfo()->SchoolMask))); uint32 minResistFactor = uint32((resistance / (resistance + 510.0f)) * 10.0f) * 2; uint32 randomResist = urand(0, (9 - minResistFactor) * 100) / 100 + minResistFactor; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp index b618cb9ce2d..44f300e1efa 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -2991,8 +2991,12 @@ class spell_the_lich_king_dark_hunger : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - int32 heal = int32(eventInfo.GetDamageInfo()->GetDamage() / 2); - GetTarget()->CastCustomSpell(SPELL_DARK_HUNGER_HEAL, SPELLVALUE_BASE_POINT0, heal, GetTarget(), true, NULL, aurEff); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 heal = static_cast<int32>(damageInfo->GetDamage()) / 2; + GetTarget()->CastCustomSpell(SPELL_DARK_HUNGER_HEAL, SPELLVALUE_BASE_POINT0, heal, GetTarget(), true, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp index fb8fddeb7db..43ef3bd1eac 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp @@ -1189,7 +1189,8 @@ class npc_crok_scourgebane : public CreatureScript } else { - me->DealHeal(me, me->CountPctFromMaxHealth(5)); + // looks totally hacky to me + me->ModifyHealth(me->CountPctFromMaxHealth(5)); _events.ScheduleEvent(EVENT_HEALTH_CHECK, 1000); } break; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index 684e5866386..575c498ead3 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -639,7 +639,7 @@ class instance_icecrown_citadel : public InstanceMapScript case GO_CACHE_OF_THE_DREAMWALKER_10H: case GO_CACHE_OF_THE_DREAMWALKER_25H: if (Creature* valithria = instance->GetCreature(ValithriaDreamwalkerGUID)) - go->SetLootRecipient(valithria->GetLootRecipient()); + go->SetLootRecipient(valithria->GetLootRecipient(), valithria->GetLootRecipientGroup()); go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED | GO_FLAG_NOT_SELECTABLE | GO_FLAG_NODESPAWN); break; case GO_ARTHAS_PLATFORM: @@ -875,7 +875,7 @@ class instance_icecrown_citadel : public InstanceMapScript if (GameObject* loot = instance->GetGameObject(DeathbringersCacheGUID)) { if (Creature* deathbringer = instance->GetCreature(DeathbringerSaurfangGUID)) - loot->SetLootRecipient(deathbringer->GetLootRecipient()); + loot->SetLootRecipient(deathbringer->GetLootRecipient(), deathbringer->GetLootRecipientGroup()); loot->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED | GO_FLAG_NOT_SELECTABLE | GO_FLAG_NODESPAWN); } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp index ac69f422d5f..c34b3be99ea 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp @@ -580,7 +580,7 @@ static const float MINION_AGGRO_DISTANCE = 20.0f; struct npc_kelthuzad_minionAI : public ScriptedAI { public: - npc_kelthuzad_minionAI(Creature* creature) : ScriptedAI(creature), instance(creature->GetInstanceScript()), _movementTimer(urandms(4,12)), _home(me->GetPosition()) { } + npc_kelthuzad_minionAI(Creature* creature) : ScriptedAI(creature), instance(creature->GetInstanceScript()), pocketId(0), _movementTimer(urandms(4,12)), _home(me->GetPosition()) { } void Reset() override { @@ -602,6 +602,9 @@ struct npc_kelthuzad_minionAI : public ScriptedAI return; } + if (!pocketId) + return; + std::list<Creature*> others; me->GetCreatureListWithEntryInGrid(others, me->GetEntry(), 80.0f); for (Creature* other : others) diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index 326a2de43a3..ecf7ec2beb2 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -2037,11 +2037,7 @@ class spell_scion_of_eternity_arcane_barrage : public SpellScriptLoader void TriggerDamageSpellFromPlayer() { if (Player* hitTarget = GetHitPlayer()) - { - // There is some proc in this spell I have absolutely no idea of use, but just in case... - TriggerCastFlags triggerFlags = TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_DISALLOW_PROC_EVENTS); - hitTarget->CastSpell(hitTarget, SPELL_ARCANE_BARRAGE_DAMAGE, triggerFlags, NULL, NULL, GetCaster()->GetGUID()); - } + hitTarget->CastSpell(hitTarget, SPELL_ARCANE_BARRAGE_DAMAGE, true, nullptr, nullptr, GetCaster()->GetGUID()); } void Register() override diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp index 3d9ea97b136..b06bc9e872f 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp @@ -604,8 +604,11 @@ class spell_oculus_temporal_rift : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - int32 amount = aurEff->GetAmount() + eventInfo.GetDamageInfo()->GetDamage(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + int32 amount = aurEff->GetAmount() + damageInfo->GetDamage(); if (amount >= 15000) { if (Unit* caster = GetCaster()) diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp index 41dcfa5da93..e044e9dbb9c 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp @@ -1063,6 +1063,51 @@ class spell_xt002_submerged : public SpellScriptLoader } }; +class spell_xt002_321_boombot_aura : public SpellScriptLoader +{ + public: + spell_xt002_321_boombot_aura() : SpellScriptLoader("spell_xt002_321_boombot_aura") { } + + class spell_xt002_321_boombot_aura_AuraScript : public AuraScript + { + PrepareAuraScript(spell_xt002_321_boombot_aura_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_ACHIEVEMENT_CREDIT_NERF_SCRAPBOTS)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetActionTarget()->GetEntry() != NPC_XS013_SCRAPBOT) + return false; + return true; + } + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + InstanceScript* instance = eventInfo.GetActor()->GetInstanceScript(); + if (!instance) + return; + + instance->DoCastSpellOnPlayers(SPELL_ACHIEVEMENT_CREDIT_NERF_SCRAPBOTS); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_xt002_321_boombot_aura_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_xt002_321_boombot_aura_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_xt002_321_boombot_aura_AuraScript(); + } +}; + class achievement_nerf_engineering : public AchievementCriteriaScript { public: @@ -1122,6 +1167,7 @@ void AddSC_boss_xt002() new spell_xt002_heart_overload_periodic(); new spell_xt002_tympanic_tantrum(); new spell_xt002_submerged(); + new spell_xt002_321_boombot_aura(); new achievement_nerf_engineering(); new achievement_heartbreaker(); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp index 675268b4e93..70d9fc90cb9 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp @@ -2750,8 +2750,13 @@ class spell_yogg_saron_grim_reprisal : public SpellScriptLoader // 63305 void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { - int32 damage = CalculatePct(int32(eventInfo.GetDamageInfo()->GetDamage()), 60); - GetTarget()->CastCustomSpell(SPELL_GRIM_REPRISAL_DAMAGE, SPELLVALUE_BASE_POINT0, damage, eventInfo.GetDamageInfo()->GetAttacker(), true, NULL, aurEff); + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 damage = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), 60); + GetTarget()->CastCustomSpell(SPELL_GRIM_REPRISAL_DAMAGE, SPELLVALUE_BASE_POINT0, damage, damageInfo->GetAttacker(), true, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp index cff5b93d7a2..e159902c3c8 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp @@ -456,7 +456,11 @@ class spell_ingvar_woe_strike : public SpellScriptLoader bool CheckProc(ProcEventInfo& eventInfo) { - return eventInfo.GetHealInfo()->GetHeal() != 0; + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return false; + + return true; } void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/utgarde_keep.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/utgarde_keep.cpp index 6c9f7e215e6..82abb2836ba 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/utgarde_keep.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/utgarde_keep.cpp @@ -191,6 +191,57 @@ class spell_fixate : public SpellScriptLoader } }; +enum SecondWind +{ + SPELL_SECOND_WIND_TRIGGER = 42771 +}; + +// 42770 - Second Wind +class spell_uk_second_wind : public SpellScriptLoader +{ + public: + spell_uk_second_wind() : SpellScriptLoader("spell_uk_second_wind") { } + + class spell_uk_second_wind_AuraScript : public AuraScript + { + PrepareAuraScript(spell_uk_second_wind_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SECOND_WIND_TRIGGER)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return false; + + return (spellInfo->GetAllEffectsMechanicMask() & ((1 << MECHANIC_ROOT) | (1 << MECHANIC_STUN))) != 0; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActionTarget(); + caster->CastSpell(caster, SPELL_SECOND_WIND_TRIGGER, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_uk_second_wind_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_uk_second_wind_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_uk_second_wind_AuraScript(); + } +}; + enum EnslavedProtoDrake { TYPE_PROTODRAKE_AT = 28, @@ -301,4 +352,5 @@ void AddSC_utgarde_keep() new npc_enslaved_proto_drake(); new spell_ticking_time_bomb(); new spell_fixate(); + new spell_uk_second_wind(); } 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_howling_fjord.cpp b/src/server/scripts/Northrend/zone_howling_fjord.cpp index 0b38338ab7b..1bbf5adcb3d 100644 --- a/src/server/scripts/Northrend/zone_howling_fjord.cpp +++ b/src/server/scripts/Northrend/zone_howling_fjord.cpp @@ -40,13 +40,38 @@ EndContentData */ ######*/ enum Entries { - NPC_APOTHECARY_HANES = 23784, - FACTION_ESCORTEE_A = 774, - FACTION_ESCORTEE_H = 775, - NPC_HANES_FIRE_TRIGGER = 23968, - QUEST_TRAIL_OF_FIRE = 11241, - SPELL_COSMETIC_LOW_POLY_FIRE = 56274, - SPELL_HEALING_POTION = 17534 + NPC_APOTHECARY_HANES = 23784, + FACTION_ESCORTEE_H = 775, + QUEST_TRAIL_OF_FIRE = 11241, + + SPELL_HEALING_POTION = 17534, + SPELL_BURN = 42685, + + EVENT_EMOTE_BEG = 1, + EVENT_BEGIN = 2, + EVENT_START_ESCORT = 3, + EVENT_TALK_1 = 4, + EVENT_KNEEL = 5, + EVENT_TALK_2 = 6, + EVENT_BURN_CRATES = 7, + EVENT_TALK_3 = 8, + EVENT_TALK_4 = 9, + EVENT_LAUGH = 10, + EVENT_TALK_5 = 11, + EVENT_TALK_6 = 12, + EVENT_TALK_8 = 13, + + TALK_0 = 0, + TALK_1 = 1, + TALK_2 = 2, + TALK_3 = 3, + TALK_4 = 4, + TALK_5 = 5, + TALK_6 = 6, + TALK_7 = 7, + TALK_8 = 8, + + EQUIP_TORCH = 2 }; class npc_apothecary_hanes : public CreatureScript @@ -58,16 +83,7 @@ public: { if (quest->GetQuestId() == QUEST_TRAIL_OF_FIRE) { - switch (player->GetTeam()) - { - case ALLIANCE: - creature->setFaction(FACTION_ESCORTEE_A); - break; - case HORDE: - creature->setFaction(FACTION_ESCORTEE_H); - break; - } - ENSURE_AI(npc_escortAI, (creature->AI()))->Start(true, false, player->GetGUID()); + ENSURE_AI(npc_Apothecary_HanesAI, (creature->AI()))->StartEscort(player); } return true; } @@ -79,12 +95,25 @@ public: Initialize(); } + void StartEscort(Player* player) + { + events.ScheduleEvent(EVENT_BEGIN, Seconds(2)); + events.ScheduleEvent(EVENT_START_ESCORT, Seconds(6)); + _player = player->GetGUID(); + } + void Initialize() { PotTimer = 10000; //10 sec cooldown on potion + events.Reset(); + events.ScheduleEvent(EVENT_EMOTE_BEG, Seconds(2)); + me->SetStandState(UNIT_STAND_STATE_KNEEL); + _player = ObjectGuid(); } uint32 PotTimer; + EventMap events; + ObjectGuid _player; void Reset() override { @@ -98,7 +127,7 @@ public: player->FailQuest(QUEST_TRAIL_OF_FIRE); } - void UpdateEscortAI(uint32 diff) override + void UpdateAI(uint32 diff) override { if (HealthBelowPct(75)) { @@ -108,8 +137,77 @@ public: PotTimer = 10000; } else PotTimer -= diff; } + if (GetAttack() && UpdateVictim()) DoMeleeAttackIfReady(); + + npc_escortAI::UpdateAI(diff); + + if (me->IsInCombat()) + return; + + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_EMOTE_BEG: + me->HandleEmoteCommand(EMOTE_ONESHOT_BEG); + events.ScheduleEvent(EVENT_EMOTE_BEG, Seconds(25)); + break; + case EVENT_BEGIN: + if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) + Talk(TALK_0, player); + break; + case EVENT_START_ESCORT: + events.Reset(); + me->setFaction(FACTION_ESCORTEE_H); + me->SetReactState(REACT_AGGRESSIVE); + ENSURE_AI(npc_escortAI, (me->AI()))->Start(true, true, _player); + break; + case EVENT_TALK_1: + if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) + Talk(TALK_1, player); + break; + case EVENT_KNEEL: + me->HandleEmoteCommand(EMOTE_ONESHOT_KNEEL); + break; + case EVENT_TALK_2: + if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) + Talk(TALK_2, player); + me->LoadEquipment(EQUIP_TORCH); + me->SetSheath(SHEATH_STATE_MELEE); + break; + case EVENT_BURN_CRATES: + DoCastAOE(SPELL_BURN, true); + break; + case EVENT_TALK_3: + if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) + Talk(TALK_3, player); + break; + case EVENT_TALK_4: + if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) + Talk(TALK_4, player); + break; + case EVENT_LAUGH: + me->HandleEmoteCommand(EMOTE_ONESHOT_LAUGH); + break; + case EVENT_TALK_5: + if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) + Talk(TALK_5, player); + me->HandleEmoteCommand(EMOTE_ONESHOT_RUDE); + break; + case EVENT_TALK_6: + if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) + Talk(TALK_6, player); + break; + case EVENT_TALK_8: + if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) + Talk(TALK_8, player); + break; + } + } } void WaypointReached(uint32 waypointId) override @@ -121,42 +219,36 @@ public: switch (waypointId) { case 1: - me->SetReactState(REACT_AGGRESSIVE); - SetRun(true); - break; - case 23: - player->GroupEventHappens(QUEST_TRAIL_OF_FIRE, me); - me->DespawnOrUnsummon(); - break; - case 5: - if (Unit* Trigger = me->FindNearestCreature(NPC_HANES_FIRE_TRIGGER, 10.0f)) - Trigger->CastSpell(Trigger, SPELL_COSMETIC_LOW_POLY_FIRE, false); - SetRun(false); + events.ScheduleEvent(EVENT_TALK_1, Seconds(3)); + events.ScheduleEvent(EVENT_KNEEL, Seconds(5)); + events.ScheduleEvent(EVENT_TALK_2, Seconds(6)); + me->SetStandState(UNIT_STAND_STATE_STAND); break; - case 6: - if (Unit* Trigger = me->FindNearestCreature(NPC_HANES_FIRE_TRIGGER, 10.0f)) - Trigger->CastSpell(Trigger, SPELL_COSMETIC_LOW_POLY_FIRE, false); - SetRun(true); + case 12: + events.ScheduleEvent(EVENT_BURN_CRATES, Seconds(1)); + events.ScheduleEvent(EVENT_TALK_3, Seconds(3)); break; - case 8: - if (Unit* Trigger = me->FindNearestCreature(NPC_HANES_FIRE_TRIGGER, 10.0f)) - Trigger->CastSpell(Trigger, SPELL_COSMETIC_LOW_POLY_FIRE, false); - SetRun(false); + case 20: + events.ScheduleEvent(EVENT_BURN_CRATES, 0); break; - case 9: - if (Unit* Trigger = me->FindNearestCreature(NPC_HANES_FIRE_TRIGGER, 10.0f)) - Trigger->CastSpell(Trigger, SPELL_COSMETIC_LOW_POLY_FIRE, false); + case 21: + events.ScheduleEvent(EVENT_BURN_CRATES, 0); + events.ScheduleEvent(EVENT_TALK_4, Seconds(3)); break; - case 10: - SetRun(true); + case 28: + events.ScheduleEvent(EVENT_BURN_CRATES, 0); + events.ScheduleEvent(EVENT_LAUGH, Seconds(7)); + events.ScheduleEvent(EVENT_TALK_5, Seconds(9)); + events.ScheduleEvent(EVENT_TALK_6, Seconds(17)); break; - case 13: - SetRun(false); + case 35: + if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) + Talk(TALK_7, player); break; - case 14: - if (Unit* Trigger = me->FindNearestCreature(NPC_HANES_FIRE_TRIGGER, 10.0f)) - Trigger->CastSpell(Trigger, SPELL_COSMETIC_LOW_POLY_FIRE, false); - SetRun(true); + case 40: + if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) + player->GroupEventHappens(QUEST_TRAIL_OF_FIRE, me); + events.ScheduleEvent(EVENT_TALK_8, Seconds(4)); break; } } diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index 2effc1d9a2a..54ae9c27aeb 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -281,6 +281,7 @@ public: void UpdateAI(uint32 diff) override { + VehicleAI::UpdateAI(diff); events.Update(diff); switch (events.ExecuteEvent()) diff --git a/src/server/scripts/Northrend/zone_wintergrasp.cpp b/src/server/scripts/Northrend/zone_wintergrasp.cpp index 781b784b3d8..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 { @@ -337,25 +337,39 @@ class go_wg_vehicle_teleporter : public GameObjectScript struct go_wg_vehicle_teleporterAI : public GameObjectAI { - go_wg_vehicle_teleporterAI(GameObject* gameObject) : GameObjectAI(gameObject), _checkTimer(1000) { } + go_wg_vehicle_teleporterAI(GameObject* gameObject) : GameObjectAI(gameObject), _checkTimer(0) { } - void UpdateAI(uint32 diff) override + bool IsFriendly(Unit* passenger) + { + return ((go->GetUInt32Value(GAMEOBJECT_FACTION) == WintergraspFaction[TEAM_HORDE] && passenger->getFaction() == HORDE) || + (go->GetUInt32Value(GAMEOBJECT_FACTION) == WintergraspFaction[TEAM_ALLIANCE] && passenger->getFaction() == ALLIANCE)); + } + + Creature* GetValidVehicle(Creature* cVeh) + { + if (!cVeh->HasAura(SPELL_VEHICLE_TELEPORT)) + if (Vehicle* vehicle = cVeh->GetVehicleKit()) + if (Unit* passenger = vehicle->GetPassenger(0)) + if (IsFriendly(passenger)) + if (Creature* teleportTrigger = passenger->SummonTrigger(go->GetPositionX()-60.0f, go->GetPositionY(), go->GetPositionZ()+1.0f, cVeh->GetOrientation(), 1000)) + return teleportTrigger; + + return nullptr; + } + + void UpdateAI(uint32 diff) { - if (_checkTimer <= diff) + _checkTimer += diff; + if (_checkTimer >= 1000) { - if (Battlefield* wg = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG)) - // Tabulation madness in the hole! - for (uint8 i = 0; i < MAX_WINTERGRASP_VEHICLES; i++) - if (Creature* vehicleCreature = go->FindNearestCreature(vehiclesList[i], 3.0f, true)) - if (!vehicleCreature->HasAura(SPELL_VEHICLE_TELEPORT) && vehicleCreature->getFaction() == WintergraspFaction[wg->GetDefenderTeam()]) - if (Creature* teleportTrigger = vehicleCreature->FindNearestCreature(NPC_WORLD_TRIGGER_LARGE_AOI_NOT_IMMUNE_PC_NPC, 100.0f, true)) - teleportTrigger->CastSpell(vehicleCreature, SPELL_VEHICLE_TELEPORT, true); - - _checkTimer = 1000; + for (uint8 i = 0; i < MAX_WINTERGRASP_VEHICLES; i++) + if (Creature* vehicleCreature = go->FindNearestCreature(vehiclesList[i], 3.0f, true)) + if (Creature* teleportTrigger = GetValidVehicle(vehicleCreature)) + teleportTrigger->CastSpell(vehicleCreature, SPELL_VEHICLE_TELEPORT, true); + + _checkTimer = 0; } - else _checkTimer -= diff; } - private: uint32 _checkTimer; }; @@ -366,111 +380,31 @@ class go_wg_vehicle_teleporter : public GameObjectScript } }; -class npc_wg_quest_giver : public CreatureScript +class npc_wg_give_promotion_credit : public CreatureScript { public: - npc_wg_quest_giver() : CreatureScript("npc_wg_quest_giver") { } + npc_wg_give_promotion_credit() : CreatureScript("npc_wg_give_promotion_credit") { } - bool OnGossipHello(Player* player, Creature* creature) override + struct npc_wg_give_promotion_creditAI : public ScriptedAI { - Battlefield* wintergrasp = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG); - if (!wintergrasp) - return true; - - if (creature->IsVendor()) - { - AddGossipItemFor(player, Player::GetDefaultGossipMenuForSource(creature), 0, GOSSIP_SENDER_MAIN, GOSSIP_OPTION_VENDOR); - player->PlayerTalkClass->GetGossipMenu().AddGossipMenuItemData(0, 0, 0); - } - - /// @todo: move this to conditions or something else + npc_wg_give_promotion_creditAI(Creature* creature) : ScriptedAI(creature) { } - // Player::PrepareQuestMenu(guid) - if (creature->IsQuestGiver()) + void JustDied(Unit* killer) override { - QuestRelationBounds objectQR = sObjectMgr->GetCreatureQuestRelationBounds(creature->GetEntry()); - QuestRelationBounds objectQIR = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(creature->GetEntry()); - - QuestMenu& qm = player->PlayerTalkClass->GetQuestMenu(); - qm.ClearMenu(); + if (killer->GetTypeId() != TYPEID_PLAYER) + return; - for (QuestRelations::const_iterator i = objectQIR.first; i != objectQIR.second; ++i) - { - uint32 questId = i->second; - QuestStatus status = player->GetQuestStatus(questId); - if (status == QUEST_STATUS_COMPLETE) - qm.AddMenuItem(questId, 4); - else if (status == QUEST_STATUS_INCOMPLETE) - qm.AddMenuItem(questId, 4); - //else if (status == QUEST_STATUS_AVAILABLE) - // qm.AddMenuItem(quest_id, 2); - } + BattlefieldWG* wintergrasp = static_cast<BattlefieldWG*>(sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG)); + if (!wintergrasp) + return; - for (QuestRelations::const_iterator i = objectQR.first; i != objectQR.second; ++i) - { - uint32 questId = i->second; - Quest const* quest = sObjectMgr->GetQuestTemplate(questId); - if (!quest) - continue; - - if (!player->CanTakeQuest(quest, false)) - continue; - - switch (questId) - { - // Horde attacker - case QUEST_BONES_AND_ARROWS_HORDE_ATT: - case QUEST_JINXING_THE_WALLS_HORDE_ATT: - case QUEST_SLAY_THEM_ALL_HORDE_ATT: - case QUEST_FUELING_THE_DEMOLISHERS_HORDE_ATT: - case QUEST_HEALING_WITH_ROSES_HORDE_ATT: - case QUEST_DEFEND_THE_SIEGE_HORDE_ATT: - if (wintergrasp->GetAttackerTeam() != TEAM_HORDE) - continue; - break; - // Horde defender - case QUEST_BONES_AND_ARROWS_HORDE_DEF: - case QUEST_WARDING_THE_WALLS_HORDE_DEF: - case QUEST_SLAY_THEM_ALL_HORDE_DEF: - case QUEST_FUELING_THE_DEMOLISHERS_HORDE_DEF: - case QUEST_HEALING_WITH_ROSES_HORDE_DEF: - case QUEST_TOPPLING_THE_TOWERS_HORDE_DEF: - case QUEST_STOP_THE_SIEGE_HORDE_DEF: - if (wintergrasp->GetDefenderTeam() != TEAM_HORDE) - continue; - break; - // Alliance attacker - case QUEST_BONES_AND_ARROWS_ALLIANCE_ATT: - case QUEST_WARDING_THE_WARRIORS_ALLIANCE_ATT: - case QUEST_NO_MERCY_FOR_THE_MERCILESS_ALLIANCE_ATT: - case QUEST_DEFEND_THE_SIEGE_ALLIANCE_ATT: - case QUEST_A_RARE_HERB_ALLIANCE_ATT: - if (wintergrasp->GetAttackerTeam() != TEAM_ALLIANCE) - continue; - break; - // Alliance defender - case QUEST_BONES_AND_ARROWS_ALLIANCE_DEF: - case QUEST_WARDING_THE_WARRIORS_ALLIANCE_DEF: - case QUEST_NO_MERCY_FOR_THE_MERCILESS_ALLIANCE_DEF: - case QUEST_SHOUTHERN_SABOTAGE_ALLIANCE_DEF: - case QUEST_STOP_THE_SIEGE_ALLIANCE_DEF: - case QUEST_A_RARE_HERB_ALLIANCE_DEF: - if (wintergrasp->GetDefenderTeam() != TEAM_ALLIANCE) - continue; - break; - default: - break; - } - - if (quest->IsAutoComplete()) - qm.AddMenuItem(questId, 4); - else if (player->GetQuestStatus(questId) == QUEST_STATUS_NONE) - qm.AddMenuItem(questId, 2); - } + wintergrasp->HandlePromotion(killer->ToPlayer(), me); } + }; - SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); - return true; + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_wg_give_promotion_creditAI(creature); } }; @@ -540,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; + + if (Vehicle* vehicle = source->GetVehicle()) + if (vehicle->GetVehicleInfo()->m_ID == 244) // Wintergrasp Tower Cannon + return true; + } - return false; - } + return false; + } }; enum WgTeleport @@ -569,63 +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*/) + { + if (Unit* target = GetHitUnit()) + { + WorldLocation loc = target->GetWorldLocation(); + SetExplTargetDest(loc); + } + } + + void Register() override { - WorldLocation loc = target->GetWorldLocation(); - SetExplTargetDest(loc); + OnEffectHitTarget += SpellEffectFn(spell_wintergrasp_defender_teleport_trigger_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } + }; + + 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") { } - void Register() override + bool OnConditionCheck(Condition const* /* condition */, ConditionSourceInfo& /* sourceInfo */) { - OnEffectHitTarget += SpellEffectFn(spell_wintergrasp_defender_teleport_trigger_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + 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") { } - SpellScript* GetSpellScript() const override - { - return new spell_wintergrasp_defender_teleport_trigger_SpellScript(); - } + 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() @@ -634,10 +596,12 @@ void AddSC_wintergrasp() new npc_wg_spirit_guide(); new npc_wg_demolisher_engineer(); new go_wg_vehicle_teleporter(); - new npc_wg_quest_giver(); + new npc_wg_give_promotion_credit(); new spell_wintergrasp_force_building(); new spell_wintergrasp_grab_passenger(); new achievement_wg_didnt_stand_a_chance(); new spell_wintergrasp_defender_teleport(); new spell_wintergrasp_defender_teleport_trigger(); + new condition_is_wintergrasp_horde(); + new condition_is_wintergrasp_alliance(); } diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/shadow_labyrinth.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/shadow_labyrinth.cpp new file mode 100644 index 00000000000..bcfd40234b7 --- /dev/null +++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/shadow_labyrinth.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * 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/>. + */ + +#include "ScriptMgr.h" +#include "SpellMgr.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" + +enum Spells +{ + SPELL_MARK_OF_MALICE_TRIGGERED = 33494 +}; + +class spell_mark_of_malice : public SpellScriptLoader +{ + public: + spell_mark_of_malice() : SpellScriptLoader("spell_mark_of_malice") { } + + class spell_mark_of_malice_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mark_of_malice_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MARK_OF_MALICE_TRIGGERED)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + // just drop charges + if (aurEff->GetBase()->GetCharges() > 1) + return; + + GetTarget()->CastSpell(GetTarget(), SPELL_MARK_OF_MALICE_TRIGGERED, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_mark_of_malice_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mark_of_malice_AuraScript(); + } +}; + +void AddSC_shadow_labyrinth() +{ + new spell_mark_of_malice(); +} 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/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp index ee7dca668a9..549786b1fd6 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp @@ -50,16 +50,18 @@ enum Spells SPELL_HAMSTRING = 26211 }; +enum Misc +{ + EMOTE_SPOUT = 0, // "The Lurker Below takes a deep breath." + SPOUT_DIST = 100 +}; + enum Creatures { NPC_COILFANG_GUARDIAN = 21873, NPC_COILFANG_AMBUSHER = 21865 }; -#define EMOTE_SPOUT "The Lurker Below takes a deep breath." - -#define SPOUT_DIST 100 - float AddPos[9][3] = { // MOVE_AMBUSHER_1 X, Y, Z @@ -240,7 +242,7 @@ public: if (SpoutTimer <= diff) { - me->TextEmote(EMOTE_SPOUT, nullptr, true); + Talk(EMOTE_SPOUT); me->SetReactState(REACT_PASSIVE); me->GetMotionMaster()->MoveRotate(20000, urand(0, 1) ? ROTATE_DIRECTION_LEFT : ROTATE_DIRECTION_RIGHT); SpoutTimer = 45000; diff --git a/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_kelidan_the_breaker.cpp b/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_kelidan_the_breaker.cpp index f191a0c3ad7..9490995a633 100644 --- a/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_kelidan_the_breaker.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_kelidan_the_breaker.cpp @@ -231,11 +231,7 @@ class boss_kelidan_the_breaker : public CreatureScript Talk(SAY_NOVA); - if (SpellInfo const* nova = sSpellMgr->GetSpellInfo(SPELL_BURNING_NOVA)) - { - if (Aura* aura = Aura::TryRefreshStackOrCreate(nova, MAX_EFFECT_MASK, me, me)) - aura->ApplyForTargets(); - } + me->AddAura(SPELL_BURNING_NOVA, me); if (IsHeroic()) DoTeleportAll(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation()); diff --git a/src/server/scripts/Outland/boss_doomlord_kazzak.cpp b/src/server/scripts/Outland/boss_doomlord_kazzak.cpp index d4f0637c0d1..daea6d24a4a 100644 --- a/src/server/scripts/Outland/boss_doomlord_kazzak.cpp +++ b/src/server/scripts/Outland/boss_doomlord_kazzak.cpp @@ -34,16 +34,17 @@ enum Texts enum Spells { - SPELL_SHADOW_VOLLEY = 32963, - SPELL_CLEAVE = 31779, - SPELL_THUNDERCLAP = 36706, - SPELL_VOID_BOLT = 39329, - SPELL_MARK_OF_KAZZAK = 32960, - SPELL_MARK_OF_KAZZAK_DAMAGE = 32961, - SPELL_ENRAGE = 32964, - SPELL_CAPTURE_SOUL = 32966, - SPELL_TWISTED_REFLECTION = 21063, - SPELL_BERSERK = 32965, + SPELL_SHADOW_VOLLEY = 32963, + SPELL_CLEAVE = 31779, + SPELL_THUNDERCLAP = 36706, + SPELL_VOID_BOLT = 39329, + SPELL_MARK_OF_KAZZAK = 32960, + SPELL_MARK_OF_KAZZAK_DAMAGE = 32961, + SPELL_ENRAGE = 32964, + SPELL_CAPTURE_SOUL = 32966, + SPELL_TWISTED_REFLECTION = 21063, + SPELL_TWISTED_REFLECTION_HEAL = 21064, + SPELL_BERSERK = 32965, }; enum Events @@ -222,8 +223,47 @@ class spell_mark_of_kazzak : public SpellScriptLoader } }; +class spell_twisted_reflection : public SpellScriptLoader +{ + public: + spell_twisted_reflection() : SpellScriptLoader("spell_twisted_reflection") { } + + class spell_twisted_reflection_AuraScript : public AuraScript + { + PrepareAuraScript(spell_twisted_reflection_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_TWISTED_REFLECTION_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + eventInfo.GetActionTarget()->CastSpell(eventInfo.GetActor(), SPELL_TWISTED_REFLECTION_HEAL, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_twisted_reflection_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_twisted_reflection_AuraScript(); + } +}; + void AddSC_boss_doomlordkazzak() { new boss_doomlord_kazzak(); new spell_mark_of_kazzak(); + new spell_twisted_reflection(); } diff --git a/src/server/scripts/Outland/outland_script_loader.cpp b/src/server/scripts/Outland/outland_script_loader.cpp index ed2c21da6c3..e5c025a43cf 100644 --- a/src/server/scripts/Outland/outland_script_loader.cpp +++ b/src/server/scripts/Outland/outland_script_loader.cpp @@ -37,6 +37,7 @@ void AddSC_boss_ambassador_hellmaw(); void AddSC_boss_blackheart_the_inciter(); void AddSC_boss_grandmaster_vorpil(); void AddSC_boss_murmur(); +void AddSC_shadow_labyrinth(); void AddSC_instance_shadow_labyrinth(); // Black Temple @@ -159,6 +160,7 @@ void AddOutlandScripts() AddSC_boss_blackheart_the_inciter(); AddSC_boss_grandmaster_vorpil(); AddSC_boss_murmur(); + AddSC_shadow_labyrinth(); AddSC_instance_shadow_labyrinth(); // Black Temple diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index cf8298bc518..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; }; @@ -470,12 +501,161 @@ enum ExorcismMisc enum ExorcismEvents { EVENT_BARADAS_TALK = 1, + EVENT_RESET = 2, //Colonel Jules EVENT_SUMMON_SKULL = 1, }; /*###### +## npc_colonel_jules +######*/ + +class npc_colonel_jules : public CreatureScript +{ +public: + npc_colonel_jules() : CreatureScript("npc_colonel_jules") { } + + bool OnGossipHello(Player* player, Creature* creature) override + { + if (ENSURE_AI(npc_colonel_jules::npc_colonel_julesAI, creature->AI())->success) + player->KilledMonsterCredit(NPC_COLONEL_JULES, ObjectGuid::Empty); + + SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); + return true; + } + + struct npc_colonel_julesAI : public ScriptedAI + { + npc_colonel_julesAI(Creature* creature) : ScriptedAI(creature), summons(me) + { + Initialize(); + } + + void Initialize() + { + point = 3; + wpreached = false; + success = false; + } + + void Reset() override + { + events.Reset(); + + summons.DespawnAll(); + Initialize(); + } + + bool success; + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_JULES_HOVER: + me->AddAura(SPELL_JULES_GOES_PRONE, me); + me->AddAura(SPELL_JULES_THREATENS_AURA, me); + + me->SetCanFly(true); + me->SetSpeedRate(MOVE_RUN, 0.2f); + + me->SetFacingTo(3.207566f); + me->GetMotionMaster()->MoveJump(exorcismPos[2], 2.0f, 2.0f); + + success = false; + + events.ScheduleEvent(EVENT_SUMMON_SKULL, 10000); + break; + case ACTION_JULES_FLIGHT: + me->RemoveAura(SPELL_JULES_GOES_PRONE); + + me->AddAura(SPELL_JULES_GOES_UPRIGHT, me); + me->AddAura(SPELL_JULES_VOMITS_AURA, me); + + wpreached = true; + me->GetMotionMaster()->MovePoint(point, exorcismPos[point]); + break; + case ACTION_JULES_MOVE_HOME: + wpreached = false; + me->SetSpeedRate(MOVE_RUN, 1.0f); + me->GetMotionMaster()->MovePoint(11, exorcismPos[2]); + + events.CancelEvent(EVENT_SUMMON_SKULL); + break; + } + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + summon->GetMotionMaster()->MoveRandom(10.0f); + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type != POINT_MOTION_TYPE) + return; + + if (id < 10) + wpreached = true; + + if (id == 8) + { + for (uint8 i = 0; i < 2; i++) + DoSummon(NPC_FOUL_PURGE, exorcismPos[8]); + } + + if (id == 10) + { + wpreached = true; + point = 3; + } + } + + void UpdateAI(uint32 diff) override + { + if (wpreached) + { + me->GetMotionMaster()->MovePoint(point, exorcismPos[point]); + point++; + wpreached = false; + } + + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SUMMON_SKULL: + uint8 summonCount = urand(1, 3); + + for (uint8 i = 0; i < summonCount; i++) + me->SummonCreature(NPC_DARKNESS_RELEASED, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 1.5f, 0, TEMPSUMMON_MANUAL_DESPAWN); + + events.ScheduleEvent(EVENT_SUMMON_SKULL, urand(10000, 15000)); + break; + } + } + } + + private: + EventMap events; + SummonList summons; + + uint8 point; + + bool wpreached; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_colonel_julesAI(creature); + } +}; + +/*###### ## npc_barada ######*/ @@ -706,11 +886,11 @@ public: break; case 21: //End - if (Player* player = ObjectAccessor::FindPlayer(playerGUID)) - player->KilledMonsterCredit(NPC_COLONEL_JULES, ObjectGuid::Empty); - if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + { + ENSURE_AI(npc_colonel_jules::npc_colonel_julesAI, jules->AI())->success = true; jules->RemoveAllAuras(); + } me->RemoveAura(SPELL_BARADAS_COMMAND); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED); @@ -718,9 +898,14 @@ public: Talk(SAY_BARADA_8); me->GetMotionMaster()->MoveTargetedHome(); EnterEvadeMode(); + events.ScheduleEvent(EVENT_RESET, Minutes(2)); break; } break; + case EVENT_RESET: + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + ENSURE_AI(npc_colonel_jules::npc_colonel_julesAI, jules->AI())->success = false; + break; } } } @@ -738,142 +923,119 @@ public: } }; -/*###### -## npc_colonel_jules -######*/ +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_colonel_jules : public CreatureScript +class npc_magister_aledis : public CreatureScript { public: - npc_colonel_jules() : CreatureScript("npc_colonel_jules") { } + npc_magister_aledis() : CreatureScript("npc_magister_aledis") { } - struct npc_colonel_julesAI : public ScriptedAI + bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 /*action*/) override { - npc_colonel_julesAI(Creature* creature) : ScriptedAI(creature), summons(me) - { - Initialize(); - } + CloseGossipMenuFor(player); + creature->StopMoving(); + ENSURE_AI(npc_magister_aledis::npc_magister_aledisAI, creature->AI())->StartFight(player); + return true; + } - void Initialize() + struct npc_magister_aledisAI : public ScriptedAI + { + npc_magister_aledisAI(Creature* creature) : ScriptedAI(creature) { } + + void StartFight(Player* player) { - circleRounds = 0; - point = 3; - wpreached = false; + 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 { - events.Reset(); - - summons.DespawnAll(); - Initialize(); + 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 DoAction(int32 action) override + void DamageTaken(Unit* /*attacker*/, uint32 &damage) override { - switch (action) + if (damage > me->GetHealth() || me->HealthBelowPctDamaged(20, damage)) { - case ACTION_JULES_HOVER: - me->AddAura(SPELL_JULES_GOES_PRONE, me); - me->AddAura(SPELL_JULES_THREATENS_AURA, me); - - me->SetCanFly(true); - me->SetSpeedRate(MOVE_RUN, 0.2f); - - me->SetFacingTo(3.207566f); - me->GetMotionMaster()->MoveJump(exorcismPos[2], 2.0f, 2.0f); - - events.ScheduleEvent(EVENT_SUMMON_SKULL, 10000); - break; - case ACTION_JULES_FLIGHT: - circleRounds++; - - me->RemoveAura(SPELL_JULES_GOES_PRONE); - - me->AddAura(SPELL_JULES_GOES_UPRIGHT, me); - me->AddAura(SPELL_JULES_VOMITS_AURA, me); - - wpreached = true; - me->GetMotionMaster()->MovePoint(point, exorcismPos[point]); - break; - case ACTION_JULES_MOVE_HOME: - wpreached = false; - me->SetSpeedRate(MOVE_RUN, 1.0f); - me->GetMotionMaster()->MovePoint(11, exorcismPos[2]); - - events.CancelEvent(EVENT_SUMMON_SKULL); - break; - } - } + damage = 0; - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - summon->GetMotionMaster()->MoveRandom(10.0f); - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type != POINT_MOTION_TYPE) - return; - - if (id < 10) - wpreached = true; - - if (id == 8) - { - for (uint8 i = 0; i < circleRounds; i++) - DoSummon(NPC_FOUL_PURGE, exorcismPos[8]); - } + _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); - if (id == 10) - { - wpreached = true; - point = 3; - circleRounds++; + _events.ScheduleEvent(EVENT_EVADE, Minutes(1)); } } void UpdateAI(uint32 diff) override { - if (wpreached) - { - me->GetMotionMaster()->MovePoint(point, exorcismPos[point]); - point++; - wpreached = false; - } + _events.Update(diff); - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { - case EVENT_SUMMON_SKULL: - uint8 summonCount = urand(1,3); - - for (uint8 i = 0; i < summonCount; i++) - me->SummonCreature(NPC_DARKNESS_RELEASED, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 1.5f, 0, TEMPSUMMON_MANUAL_DESPAWN); - - events.ScheduleEvent(EVENT_SUMMON_SKULL, urand(10000, 15000)); - break; + 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; } } - } - private: - EventMap events; - SummonList summons; + if (!UpdateVictim()) + return; - uint8 circleRounds; - uint8 point; + DoMeleeAttackIfReady(); + } - bool wpreached; + private: + EventMap _events; + ObjectGuid _playerGUID; }; CreatureAI* GetAI(Creature* creature) const override { - return new npc_colonel_julesAI(creature); + return new npc_magister_aledisAI(creature); } }; @@ -883,6 +1045,7 @@ void AddSC_hellfire_peninsula() new npc_ancestral_wolf(); new npc_wounded_blood_elf(); new npc_fel_guard_hound(); - new npc_barada(); 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 321e44c8603..7092300af4d 100644 --- a/src/server/scripts/Pet/pet_hunter.cpp +++ b/src/server/scripts/Pet/pet_hunter.cpp @@ -22,6 +22,8 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" enum HunterSpells { @@ -35,6 +37,21 @@ enum HunterCreatures NPC_HUNTER_VIPER = 19921 }; +enum PetSpellsMisc +{ + SPELL_PET_GUARD_DOG_HAPPINESS = 54445, + SPELL_PET_SILVERBACK_RANK_1 = 62800, + SPELL_PET_SILVERBACK_RANK_2 = 62801, + + SPELL_PET_SWOOP = 52825, + SPELL_PET_CHARGE = 61685, + + PET_ICON_ID_GROWL = 201, + PET_ICON_ID_CLAW = 262, + PET_ICON_ID_BITE = 1680, + PET_ICON_ID_SMACK = 473 +}; + class npc_pet_hunter_snake_trap : public CreatureScript { public: @@ -139,7 +156,204 @@ class npc_pet_hunter_snake_trap : public CreatureScript } }; +// 57627 - Charge +class spell_pet_charge : public SpellScriptLoader +{ + public: + spell_pet_charge() : SpellScriptLoader("spell_pet_charge") { } + + class spell_pet_charge_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pet_charge_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PET_SWOOP) || + !sSpellMgr->GetSpellInfo(SPELL_PET_CHARGE)) + return false; + return true; + } + + void HandleDummy(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + // Prevent console log + PreventDefaultAction(); + + // Remove +% AP aura + Unit* pet = eventInfo.GetActor(); + Aura* aura = pet->GetAura(SPELL_PET_SWOOP, pet->GetGUID()); + if (!aura) + aura = pet->GetAura(SPELL_PET_CHARGE, pet->GetGUID()); + + if (!aura) + return; + + aura->DropCharge(AURA_REMOVE_BY_EXPIRE); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pet_charge_AuraScript::HandleDummy, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pet_charge_AuraScript(); + } +}; + +// -53178 - Guard Dog +class spell_pet_guard_dog : public SpellScriptLoader +{ + public: + spell_pet_guard_dog() : SpellScriptLoader("spell_pet_guard_dog") { } + + class spell_pet_guard_dog_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pet_guard_dog_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PET_GUARD_DOG_HAPPINESS)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + // Growl shares family flags with other spells + // filter by spellIcon instead + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo || spellInfo->SpellIconID != PET_ICON_ID_GROWL) + return false; + + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + caster->CastSpell((Unit*)nullptr, SPELL_PET_GUARD_DOG_HAPPINESS, true, nullptr, aurEff); + + float addThreat = CalculatePct(ASSERT_NOTNULL(eventInfo.GetSpellInfo())->Effects[EFFECT_0].CalcValue(caster), aurEff->GetAmount()); + eventInfo.GetProcTarget()->AddThreat(caster, addThreat); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_pet_guard_dog_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_pet_guard_dog_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pet_guard_dog_AuraScript(); + } +}; + +// -62764 - Silverback +class spell_pet_silverback : public SpellScriptLoader +{ + public: + spell_pet_silverback() : SpellScriptLoader("spell_pet_silverback") { } + + class spell_pet_silverback_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pet_silverback_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PET_GUARD_DOG_HAPPINESS)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + // Growl shares family flags with other spells + // filter by spellIcon instead + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo || spellInfo->SpellIconID != PET_ICON_ID_GROWL) + return false; + + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + static uint32 const triggerSpell[2] = { SPELL_PET_SILVERBACK_RANK_1, SPELL_PET_SILVERBACK_RANK_2 }; + + PreventDefaultAction(); + + uint32 spellId = triggerSpell[GetSpellInfo()->GetRank() - 1]; + eventInfo.GetActor()->CastSpell((Unit*)nullptr, spellId, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_pet_silverback_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_pet_silverback_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pet_silverback_AuraScript(); + } +}; + +// -61680 - Culling the Herd +class spell_pet_culling_the_herd : public SpellScriptLoader +{ + public: + spell_pet_culling_the_herd() : SpellScriptLoader("spell_pet_culling_the_herd") { } + + class spell_pet_culling_the_herd_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pet_culling_the_herd_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + // Claw, Bite and Smack share FamilyFlags with other spells + // filter by spellIcon instead + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return false; + + switch (spellInfo->SpellIconID) + { + case PET_ICON_ID_CLAW: + case PET_ICON_ID_BITE: + case PET_ICON_ID_SMACK: + break; + default: + return false; + } + + return true; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_pet_culling_the_herd_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pet_culling_the_herd_AuraScript(); + } +}; + void AddSC_hunter_pet_scripts() { new npc_pet_hunter_snake_trap(); + new spell_pet_charge(); + new spell_pet_guard_dog(); + new spell_pet_silverback(); + new spell_pet_culling_the_herd(); } diff --git a/src/server/scripts/Pet/pet_priest.cpp b/src/server/scripts/Pet/pet_priest.cpp index a3110ce8f8b..a15b3bd7ffd 100644 --- a/src/server/scripts/Pet/pet_priest.cpp +++ b/src/server/scripts/Pet/pet_priest.cpp @@ -28,7 +28,7 @@ enum PriestSpells { SPELL_PRIEST_GLYPH_OF_SHADOWFIEND = 58228, - SPELL_PRIEST_GLYPH_OF_SHADOWFIEND_MANA = 58227, + SPELL_PRIEST_SHADOWFIEND_DEATH = 57989, SPELL_PRIEST_LIGHTWELL_CHARGES = 59907 }; @@ -70,12 +70,10 @@ class npc_pet_pri_shadowfiend : public CreatureScript { npc_pet_pri_shadowfiendAI(Creature* creature) : PetAI(creature) { } - void JustDied(Unit* /*killer*/) override + void IsSummonedBy(Unit* summoner) override { - if (me->IsSummon()) - if (Unit* owner = me->ToTempSummon()->GetSummoner()) - if (owner->HasAura(SPELL_PRIEST_GLYPH_OF_SHADOWFIEND)) - owner->CastSpell(owner, SPELL_PRIEST_GLYPH_OF_SHADOWFIEND_MANA, true); + if (summoner->HasAura(SPELL_PRIEST_GLYPH_OF_SHADOWFIEND)) + DoCastAOE(SPELL_PRIEST_SHADOWFIEND_DEATH); } }; diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 980c0db19cc..13eec799493 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -73,7 +73,24 @@ enum DeathKnightSpells SPELL_DK_UNHOLY_PRESENCE_TRIGGERED = 49772, SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1 = 49189, SPELL_DK_WILL_OF_THE_NECROPOLIS_AURA_R1 = 52284, - SPELL_DK_GHOUL_THRASH = 47480 + SPELL_DK_GHOUL_THRASH = 47480, + SPELL_DK_GLYPH_OF_SCOURGE_STRIKE_SCRIPT = 69961, + SPELL_DK_BUTCHERY_RUNIC_POWER = 50163, + SPELL_DK_MARK_OF_BLOOD_HEAL = 61607, + SPELL_DK_UNHOLY_BLIGHT_DAMAGE = 50536, + SPELL_DK_GLYPH_OF_UNHOLY_BLIGHT = 63332, + SPELL_DK_VENDETTA_HEAL = 50181, + SPELL_DK_NECROSIS_DAMAGE = 51460, + SPELL_DK_OBLITERATE_OFF_HAND_R1 = 66198, + SPELL_DK_FROST_STRIKE_OFF_HAND_R1 = 66196, + SPELL_DK_PLAGUE_STRIKE_OFF_HAND_R1 = 66216, + SPELL_DK_DEATH_STRIKE_OFF_HAND_R1 = 66188, + SPELL_DK_RUNE_STRIKE_OFF_HAND_R1 = 66217, + SPELL_DK_BLOOD_STRIKE_OFF_HAND_R1 = 66215, + SPELL_DK_RUNIC_RETURN = 61258, + SPELL_DK_WANDERING_PLAGUE_DAMAGE = 50526, + SPELL_DK_DEATH_COIL_R1 = 47541, + SPELL_DK_DEATH_GRIP_INITIAL = 49576 }; enum DeathKnightSpellIcons @@ -83,7 +100,9 @@ enum DeathKnightSpellIcons enum Misc { - NPC_DK_GHOUL = 26125 + NPC_DK_GHOUL = 26125, + NPC_DK_DANCING_RUNE_WEAPON = 27893, + SPELL_CATEGORY_HOWLING_BLAST = 1248 }; // -49200 - Acclimation @@ -110,9 +129,9 @@ public: bool CheckProc(ProcEventInfo& eventInfo) { - if (eventInfo.GetDamageInfo()) + if (DamageInfo* damageInfo = eventInfo.GetDamageInfo()) { - switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask())) + switch (GetFirstSchoolInMask(damageInfo->GetSchoolMask())) { case SPELL_SCHOOL_HOLY: case SPELL_SCHOOL_FIRE: @@ -393,6 +412,38 @@ class spell_dk_anti_magic_zone : public SpellScriptLoader } }; +// -49182 - Blade Barrier +class spell_dk_blade_barrier : public SpellScriptLoader +{ + public: + spell_dk_blade_barrier() : SpellScriptLoader("spell_dk_blade_barrier") { } + + class spell_dk_blade_barrier_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_blade_barrier_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetSpellInfo() != nullptr) + if (Player* player = eventInfo.GetActor()->ToPlayer()) + if (player->getClass() == CLASS_DEATH_KNIGHT && player->IsBaseRuneSlotsOnCooldown(RUNE_BLOOD)) + return true; + + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dk_blade_barrier_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_blade_barrier_AuraScript(); + } +}; + // 48721 - Blood Boil class spell_dk_blood_boil : public SpellScriptLoader { @@ -478,8 +529,12 @@ class spell_dk_blood_gorged : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - int32 bp = int32(eventInfo.GetDamageInfo()->GetDamage() * 1.5f); - GetTarget()->CastCustomSpell(SPELL_DK_BLOOD_GORGED_HEAL, SPELLVALUE_BASE_POINT0, bp, _procTarget, true, NULL, aurEff); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 bp = static_cast<int32>(damageInfo->GetDamage() * 1.5f); + GetTarget()->CastCustomSpell(SPELL_DK_BLOOD_GORGED_HEAL, SPELLVALUE_BASE_POINT0, bp, _procTarget, true, nullptr, aurEff); } void Register() override @@ -525,6 +580,41 @@ class spell_dk_bloodworms : public SpellScriptLoader } }; +// -48979 - Butchery +class spell_dk_butchery : public SpellScriptLoader +{ + public: + spell_dk_butchery() : SpellScriptLoader("spell_dk_butchery") { } + + class spell_dk_butchery_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_butchery_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_BUTCHERY_RUNIC_POWER)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastCustomSpell(SPELL_DK_BUTCHERY_RUNIC_POWER, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dk_butchery_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_butchery_AuraScript(); + } +}; + class CorpseExplosionCheck { public: @@ -649,6 +739,69 @@ class spell_dk_corpse_explosion : public SpellScriptLoader } }; +// 49028 - Dancing Rune Weapon +class spell_dk_dancing_rune_weapon : public SpellScriptLoader +{ + public: + spell_dk_dancing_rune_weapon() : SpellScriptLoader("spell_dk_dancing_rune_weapon") { } + + class spell_dk_dancing_rune_weapon_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_dancing_rune_weapon_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sObjectMgr->GetCreatureTemplate(NPC_DK_DANCING_RUNE_WEAPON)) + return false; + return true; + } + + // This is a port of the old switch hack in Unit.cpp, it's not correct + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = GetCaster(); + if (!caster) + return; + + Unit* drw = nullptr; + for (Unit* controlled : caster->m_Controlled) + { + if (controlled->GetEntry() == NPC_DK_DANCING_RUNE_WEAPON) + { + drw = controlled; + break; + } + } + + if (!drw || !drw->GetVictim()) + return; + + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; + + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 amount = static_cast<int32>(damageInfo->GetDamage()) / 2; + drw->SendSpellNonMeleeDamageLog(drw->GetVictim(), spellInfo->Id, amount, spellInfo->GetSchoolMask(), 0, 0, false, 0, false); + drw->DealDamage(drw->GetVictim(), amount, nullptr, SPELL_DIRECT_DAMAGE, spellInfo->GetSchoolMask(), spellInfo, true); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dk_dancing_rune_weapon_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_dancing_rune_weapon_AuraScript(); + } +}; + class spell_dk_death_and_decay : public SpellScriptLoader { public: @@ -874,6 +1027,84 @@ class spell_dk_death_pact : public SpellScriptLoader } }; +// -54639 - Blood of the North +// -49208 - Reaping +// -49467 - Death Rune Mastery +class spell_dk_death_rune : public SpellScriptLoader +{ + public: + spell_dk_death_rune() : SpellScriptLoader("spell_dk_death_rune") { } + + class spell_dk_death_rune_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_death_rune_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + Unit* caster = eventInfo.GetActor(); + + if (caster->GetTypeId() != TYPEID_PLAYER) + return false; + + Player* player = caster->ToPlayer(); + if (player->getClass() != CLASS_DEATH_KNIGHT) + return false; + + return true; + } + + void HandleProc(ProcEventInfo& eventInfo) + { + Player* player = eventInfo.GetActor()->ToPlayer(); + + AuraEffect* aurEff = GetEffect(EFFECT_0); + if (!aurEff) + return; + + // Reset amplitude - set death rune remove timer to 30s + aurEff->ResetPeriodic(true); + + uint32 runesLeft = 1; + + // Death Rune Mastery + if (GetSpellInfo()->SpellIconID == 2622) + runesLeft = 2; + + for (uint8 i = 0; i < MAX_RUNES && runesLeft; ++i) + { + if (GetSpellInfo()->SpellIconID == 2622) + { + if (player->GetBaseRune(i) == RUNE_BLOOD) + continue; + } + else + { + if (player->GetBaseRune(i) != RUNE_BLOOD) + continue; + } + + if (player->GetRuneCooldown(i) != (player->GetRuneBaseCooldown(i) - player->GetLastRuneGraceTimer(i))) + continue; + + --runesLeft; + // Mark aura as used + player->AddRuneByAuraEffect(i, RUNE_DEATH, aurEff); + } + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dk_death_rune_AuraScript::CheckProc); + OnProc += AuraProcFn(spell_dk_death_rune_AuraScript::HandleProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_death_rune_AuraScript(); + } +}; + // -49998 - Death Strike class spell_dk_death_strike : public SpellScriptLoader { @@ -966,6 +1197,116 @@ class spell_dk_ghoul_explode : public SpellScriptLoader } }; +// 62259 - Glyph of Death Grip +class spell_dk_glyph_of_death_grip : public SpellScriptLoader +{ + public: + spell_dk_glyph_of_death_grip() : SpellScriptLoader("spell_dk_glyph_of_death_grip") { } + + class spell_dk_glyph_of_death_grip_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_glyph_of_death_grip_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_DEATH_GRIP_INITIAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->GetSpellHistory()->ResetCooldown(SPELL_DK_DEATH_GRIP_INITIAL, true); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dk_glyph_of_death_grip_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_glyph_of_death_grip_AuraScript(); + } +}; + +// 58642 - Glyph of Scourge Strike +class spell_dk_glyph_of_scourge_strike : public SpellScriptLoader +{ + public: + spell_dk_glyph_of_scourge_strike() : SpellScriptLoader("spell_dk_glyph_of_scourge_strike") { } + + class spell_dk_glyph_of_scourge_strike_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_glyph_of_scourge_strike_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_GLYPH_OF_SCOURGE_STRIKE_SCRIPT)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), SPELL_DK_GLYPH_OF_SCOURGE_STRIKE_SCRIPT, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dk_glyph_of_scourge_strike_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_glyph_of_scourge_strike_AuraScript(); + } +}; + +// 51209 - Hungering Cold +class spell_dk_hungering_cold : public SpellScriptLoader +{ + public: + spell_dk_hungering_cold() : SpellScriptLoader("spell_dk_hungering_cold") { } + + class spell_dk_hungering_cold_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_hungering_cold_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) // probably melee damage so let's proc + return true; + + return (spellInfo->Dispel != DISPEL_DISEASE); + } + + void HandleDummy(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + // Prevent console spam + PreventDefaultAction(); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dk_hungering_cold_AuraScript::CheckProc); + + OnEffectProc += AuraEffectProcFn(spell_dk_hungering_cold_AuraScript::HandleDummy, EFFECT_1, SPELL_AURA_DUMMY); + OnEffectProc += AuraEffectProcFn(spell_dk_hungering_cold_AuraScript::HandleDummy, EFFECT_2, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_hungering_cold_AuraScript(); + } +}; + // 48792 - Icebound Fortitude class spell_dk_icebound_fortitude : public SpellScriptLoader { @@ -1049,10 +1390,18 @@ class spell_dk_improved_blood_presence : public SpellScriptLoader target->RemoveAura(SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED); } + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + // Prevent console spam + PreventDefaultAction(); + } + void Register() override { AfterEffectApply += AuraEffectApplyFn(spell_dk_improved_blood_presence_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); AfterEffectRemove += AuraEffectRemoveFn(spell_dk_improved_blood_presence_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + + OnEffectProc += AuraEffectProcFn(spell_dk_improved_blood_presence_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); } }; @@ -1091,7 +1440,7 @@ public: { PreventDefaultAction(); if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo()) - eventInfo.GetActor()->CastCustomSpell(SPELL_DK_IMPROVED_BLOOD_PRESENCE_HEAL, SPELLVALUE_BASE_POINT0, CalculatePct(int32(dmgInfo->GetDamage()), aurEff->GetAmount()), + eventInfo.GetActor()->CastCustomSpell(SPELL_DK_IMPROVED_BLOOD_PRESENCE_HEAL, SPELLVALUE_BASE_POINT0, CalculatePct(static_cast<int32>(dmgInfo->GetDamage()), aurEff->GetAmount()), eventInfo.GetActor(), true, nullptr, aurEff); } @@ -1213,6 +1562,127 @@ class spell_dk_improved_unholy_presence : public SpellScriptLoader } }; +// 61257 - Runic Power Back on Snare/Root +class spell_dk_pvp_4p_bonus : public SpellScriptLoader +{ + public: + spell_dk_pvp_4p_bonus() : SpellScriptLoader("spell_dk_pvp_4p_bonus") { } + + class spell_dk_pvp_4p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_pvp_4p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_RUNIC_RETURN)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return false; + + return (spellInfo->GetAllEffectsMechanicMask() & ((1 << MECHANIC_ROOT) | (1 << MECHANIC_SNARE))) != 0; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActionTarget()->CastSpell((Unit*)nullptr, SPELL_DK_RUNIC_RETURN, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dk_pvp_4p_bonus_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dk_pvp_4p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_pvp_4p_bonus_AuraScript(); + } +}; + +// 49005 - Mark of Blood +class spell_dk_mark_of_blood : public SpellScriptLoader +{ + public: + spell_dk_mark_of_blood() : SpellScriptLoader("spell_dk_mark_of_blood") { } + + class spell_dk_mark_of_blood_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_mark_of_blood_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_MARK_OF_BLOOD_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), SPELL_DK_MARK_OF_BLOOD_HEAL, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dk_mark_of_blood_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_mark_of_blood_AuraScript(); + } +}; + +// -51459 - Necrosis +class spell_dk_necrosis : public SpellScriptLoader +{ + public: + spell_dk_necrosis() : SpellScriptLoader("spell_dk_necrosis") { } + + class spell_dk_necrosis_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_necrosis_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_NECROSIS_DAMAGE)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + eventInfo.GetActor()->CastCustomSpell(SPELL_DK_NECROSIS_DAMAGE, SPELLVALUE_BASE_POINT0, amount, eventInfo.GetProcTarget(), true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dk_necrosis_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_necrosis_AuraScript(); + } +}; + // ID - 50842 Pestilence class spell_dk_pestilence : public SpellScriptLoader { @@ -1565,6 +2035,71 @@ class spell_dk_raise_dead : public SpellScriptLoader } }; +// -49188 - Rime +class spell_dk_rime : public SpellScriptLoader +{ + public: + spell_dk_rime() : SpellScriptLoader("spell_dk_rime") { } + + class spell_dk_blade_barrier_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_blade_barrier_AuraScript); + + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + return GetTarget()->GetTypeId() == TYPEID_PLAYER; + } + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + GetTarget()->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool + { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); + return spellInfo && spellInfo->GetCategory() == SPELL_CATEGORY_HOWLING_BLAST; + }, true); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dk_blade_barrier_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dk_blade_barrier_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_blade_barrier_AuraScript(); + } +}; + +// 56817 - Rune strike proc (Serverside spell) +class spell_dk_rune_strike_proc : public SpellScriptLoader +{ + public: + spell_dk_rune_strike_proc() : SpellScriptLoader("spell_dk_rune_strike_proc") { } + + class spell_dk_rune_strike_proc_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_rune_strike_proc_AuraScript); + + void HandleDummy(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + // Prevent console log + PreventDefaultAction(); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dk_rune_strike_proc_AuraScript::HandleDummy, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_rune_strike_proc_AuraScript(); + } +}; + // 59754 Rune Tap - Party class spell_dk_rune_tap_party : public SpellScriptLoader { @@ -1613,7 +2148,7 @@ class spell_dk_scent_of_blood : public SpellScriptLoader { PreventDefaultAction(); GetTarget()->CastSpell(GetTarget(), SPELL_DK_SCENT_OF_BLOOD, true, NULL, aurEff); - GetTarget()->RemoveAuraFromStack(GetSpellInfo()->Id); + ModStackAmount(-1); } void Register() override @@ -1628,6 +2163,37 @@ class spell_dk_scent_of_blood : public SpellScriptLoader } }; +// -49004 - Scent of Blood trigger +class spell_dk_scent_of_blood_trigger : public SpellScriptLoader +{ + public: + spell_dk_scent_of_blood_trigger() : SpellScriptLoader("spell_dk_scent_of_blood_trigger") { } + + class spell_dk_scent_of_blood_trigger_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_scent_of_blood_trigger_AuraScript); + + // Each rank of Scent of Blood adds a trigger spell effect + // thus each effect adds one stack when proccing + // We need to remove the old buff before proccing again + // or we would be adding stacks to a possibly existing aura + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + GetTarget()->RemoveAurasDueToSpell(GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dk_scent_of_blood_trigger_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_scent_of_blood_trigger_AuraScript(); + } +}; + // 55090 - Scourge Strike (55265, 55270, 55271) class spell_dk_scourge_strike : public SpellScriptLoader { @@ -1727,7 +2293,7 @@ class spell_dk_spell_deflection : public SpellScriptLoader void Absorb(AuraEffect* /*aurEff*/, DamageInfo & dmgInfo, uint32 & absorbAmount) { // You have a chance equal to your Parry chance - if ((dmgInfo.GetDamageType() == SPELL_DIRECT_DAMAGE) && roll_chance_f(GetTarget()->GetUnitParryChance())) + if ((dmgInfo.GetDamageType() == SPELL_DIRECT_DAMAGE) && roll_chance_f(GetTarget()->GetFloatValue(PLAYER_PARRY_PERCENTAGE))) absorbAmount = CalculatePct(dmgInfo.GetDamage(), absorbPct); } @@ -1744,6 +2310,188 @@ class spell_dk_spell_deflection : public SpellScriptLoader } }; +// -49018 - Sudden Doom +class spell_dk_sudden_doom : public SpellScriptLoader +{ + public: + spell_dk_sudden_doom() : SpellScriptLoader("spell_dk_sudden_doom") { } + + class spell_dk_sudden_doom_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_sudden_doom_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_DEATH_COIL_R1)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_DK_DEATH_COIL_R1); + uint32 spellId = 0; + + while (spellInfo) + { + if (!caster->HasSpell(spellInfo->Id)) + break; + + spellId = spellInfo->Id; + spellInfo = spellInfo->GetNextRankSpell(); + } + + if (!spellId) + return; + + caster->CastSpell(eventInfo.GetProcTarget(), spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dk_sudden_doom_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_sudden_doom_AuraScript(); + } +}; + +// -65661 Threat of Thassarian +class spell_dk_threat_of_thassarian : public SpellScriptLoader +{ + public: + spell_dk_threat_of_thassarian() : SpellScriptLoader("spell_dk_threat_of_thassarian") { } + + class spell_dk_threat_of_thassarian_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_threat_of_thassarian_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_OBLITERATE_OFF_HAND_R1) || + !sSpellMgr->GetSpellInfo(SPELL_DK_FROST_STRIKE_OFF_HAND_R1) || + !sSpellMgr->GetSpellInfo(SPELL_DK_PLAGUE_STRIKE_OFF_HAND_R1) || + !sSpellMgr->GetSpellInfo(SPELL_DK_DEATH_STRIKE_OFF_HAND_R1) || + !sSpellMgr->GetSpellInfo(SPELL_DK_RUNE_STRIKE_OFF_HAND_R1) || + !sSpellMgr->GetSpellInfo(SPELL_DK_BLOOD_STRIKE_OFF_HAND_R1)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + if (!roll_chance_i(aurEff->GetAmount())) + return; + + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; + + // Must dual wield + Unit* caster = eventInfo.GetActor(); + if (!caster->haveOffhandWeapon()) + return; + + uint32 spellId = 0; + // Plague Strike + if (spellInfo->SpellFamilyFlags[0] & 0x00000001) + spellId = SPELL_DK_PLAGUE_STRIKE_OFF_HAND_R1; + // Death Strike + else if (spellInfo->SpellFamilyFlags[0] & 0x00000010) + spellId = SPELL_DK_DEATH_STRIKE_OFF_HAND_R1; + // Blood Strike + else if (spellInfo->SpellFamilyFlags[0] & 0x00400000) + spellId = SPELL_DK_BLOOD_STRIKE_OFF_HAND_R1; + // Frost Strike + else if (spellInfo->SpellFamilyFlags[1] & 0x00000004) + spellId = SPELL_DK_FROST_STRIKE_OFF_HAND_R1; + // Obliterate + else if (spellInfo->SpellFamilyFlags[1] & 0x00020000) + spellId = SPELL_DK_OBLITERATE_OFF_HAND_R1; + // Rune Strike + else if (spellInfo->SpellFamilyFlags[1] & 0x20000000) + spellId = SPELL_DK_RUNE_STRIKE_OFF_HAND_R1; + + if (!spellId) + return; + + spellId = sSpellMgr->GetSpellWithRank(spellId, spellInfo->GetRank()); + caster->CastSpell(eventInfo.GetProcTarget(), spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dk_threat_of_thassarian_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_threat_of_thassarian_AuraScript(); + } +}; + +// 49194 - Unholy Blight +class spell_dk_unholy_blight : public SpellScriptLoader +{ + public: + spell_dk_unholy_blight() : SpellScriptLoader("spell_dk_unholy_blight") { } + + class spell_dk_unholy_blight_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_unholy_blight_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_UNHOLY_BLIGHT_DAMAGE) || + !sSpellMgr->GetSpellInfo(SPELL_DK_GLYPH_OF_UNHOLY_BLIGHT)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_DK_UNHOLY_BLIGHT_DAMAGE); + int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + if (AuraEffect const* glyph = caster->GetAuraEffect(SPELL_DK_GLYPH_OF_UNHOLY_BLIGHT, EFFECT_0, caster->GetGUID())) + AddPct(amount, glyph->GetAmount()); + + amount /= spellInfo->GetMaxTicks(); + + // Add remaining ticks to healing done + amount += target->GetRemainingPeriodicAmount(caster->GetGUID(), SPELL_DK_UNHOLY_BLIGHT_DAMAGE, SPELL_AURA_PERIODIC_DAMAGE); + + caster->CastCustomSpell(SPELL_DK_UNHOLY_BLIGHT_DAMAGE, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dk_unholy_blight_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_unholy_blight_AuraScript(); + } +}; // 55233 - Vampiric Blood class spell_dk_vampiric_blood : public SpellScriptLoader { @@ -1771,6 +2519,88 @@ class spell_dk_vampiric_blood : public SpellScriptLoader } }; +// -49015 - Vendetta +class spell_dk_vendetta : public SpellScriptLoader +{ + public: + spell_dk_vendetta() : SpellScriptLoader("spell_dk_vendetta") { } + + class spell_dk_vendetta_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_vendetta_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_VENDETTA_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + int32 amount = caster->CountPctFromMaxHealth(aurEff->GetAmount()); + caster->CastCustomSpell(SPELL_DK_VENDETTA_HEAL, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dk_vendetta_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_vendetta_AuraScript(); + } +}; + +// -49217 - Wandering Plague +class spell_dk_wandering_plague : public SpellScriptLoader +{ + public: + spell_dk_wandering_plague() : SpellScriptLoader("spell_dk_wandering_plague") { } + + class spell_dk_wandering_plague_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_wandering_plague_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_WANDERING_PLAGUE_DAMAGE)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + if (!roll_chance_f(caster->GetUnitCriticalChance(BASE_ATTACK, target))) + return; + + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + caster->CastCustomSpell(SPELL_DK_WANDERING_PLAGUE_DAMAGE, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dk_wandering_plague_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_wandering_plague_AuraScript(); + } +}; + // 52284 - Will of the Necropolis class spell_dk_will_of_the_necropolis : public SpellScriptLoader { @@ -1971,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 @@ -2003,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) @@ -2200,30 +3038,48 @@ void AddSC_deathknight_spell_scripts() new spell_dk_anti_magic_shell_raid(); new spell_dk_anti_magic_shell_self(); new spell_dk_anti_magic_zone(); + new spell_dk_blade_barrier(); new spell_dk_blood_boil(); new spell_dk_blood_gorged(); new spell_dk_bloodworms(); + new spell_dk_butchery(); new spell_dk_corpse_explosion(); + new spell_dk_dancing_rune_weapon(); new spell_dk_death_and_decay(); new spell_dk_death_coil(); new spell_dk_death_gate(); new spell_dk_death_grip(); new spell_dk_death_pact(); + new spell_dk_death_rune(); new spell_dk_death_strike(); new spell_dk_ghoul_explode(); + new spell_dk_glyph_of_death_grip(); + new spell_dk_glyph_of_scourge_strike(); + new spell_dk_hungering_cold(); new spell_dk_icebound_fortitude(); new spell_dk_improved_blood_presence(); new spell_dk_improved_blood_presence_triggered(); new spell_dk_improved_frost_presence(); new spell_dk_improved_unholy_presence(); + new spell_dk_pvp_4p_bonus(); + new spell_dk_mark_of_blood(); + new spell_dk_necrosis(); new spell_dk_pestilence(); new spell_dk_presence(); new spell_dk_raise_dead(); + new spell_dk_rime(); + new spell_dk_rune_strike_proc(); new spell_dk_rune_tap_party(); new spell_dk_scent_of_blood(); + new spell_dk_scent_of_blood_trigger(); new spell_dk_scourge_strike(); new spell_dk_spell_deflection(); + new spell_dk_sudden_doom(); + new spell_dk_threat_of_thassarian(); + new spell_dk_unholy_blight(); new spell_dk_vampiric_blood(); + new spell_dk_vendetta(); + new spell_dk_wandering_plague(); new spell_dk_will_of_the_necropolis(); new spell_dk_death_grip_initial(); new spell_dk_raise_ally_initial(); diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index 0bf5ab01f45..56624346772 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -57,7 +57,68 @@ enum DruidSpells SPELL_DRUID_SAVAGE_ROAR = 62071, SPELL_DRUID_T9_FERAL_RELIC_BEAR = 67354, SPELL_DRUID_T9_FERAL_RELIC_CAT = 67355, - SPELL_DRUID_TIGER_S_FURY_ENERGIZE = 51178 + SPELL_DRUID_TIGER_S_FURY_ENERGIZE = 51178, + SPELL_DRUID_T3_PROC_ENERGIZE_MANA = 28722, + SPELL_DRUID_T3_PROC_ENERGIZE_RAGE = 28723, + SPELL_DRUID_T3_PROC_ENERGIZE_ENERGY = 28724, + SPELL_DRUID_BLESSING_OF_THE_CLAW = 28750, + SPELL_DRUID_REVITALIZE_ENERGIZE_MANA = 48542, + SPELL_DRUID_REVITALIZE_ENERGIZE_RAGE = 48541, + SPELL_DRUID_REVITALIZE_ENERGIZE_ENERGY = 48540, + SPELL_DRUID_REVITALIZE_ENERGIZE_RP = 48543, + SPELL_DRUID_GLYPH_OF_INNERVATE_REGEN = 54833, + SPELL_DRUID_GLYPH_OF_STARFIRE_SCRIPT = 54846, + SPELL_DRUID_GLYPH_OF_RIP = 54818, + SPELL_DRUID_RIP_DURATION_LACERATE_DMG = 60141, + SPELL_DRUID_GLYPH_OF_RAKE_TRIGGERED = 54820, + SPELL_DRUID_IMP_LEADER_OF_THE_PACK_R1 = 34297, + SPELL_DRUID_IMP_LEADER_OF_THE_PACK_HEAL = 34299, + SPELL_DRUID_IMP_LEADER_OF_THE_PACK_MANA = 68285, + SPELL_DRUID_EXHILARATE = 28742, + SPELL_DRUID_GLYPH_OF_REJUVENATION_HEAL = 54755, + SPELL_DRUID_INFUSION = 37238, + SPELL_DRUID_BLESSING_OF_REMULOS = 40445, + SPELL_DRUID_BLESSING_OF_ELUNE = 40446, + SPELL_DRUID_BLESSING_OF_CENARIUS = 40452, + SPELL_DRUID_LANGUISH = 71023, + SPELL_DRUID_REJUVENATION_T10_PROC = 70691, + SPELL_DRUID_BALANCE_T10_BONUS = 70718, + SPELL_DRUID_BALANCE_T10_BONUS_PROC = 70721, + SPELL_DRUID_BARKSKIN_01 = 63058 +}; + +// 22812 - Barkskin +class spell_dru_barkskin : public SpellScriptLoader +{ + public: + spell_dru_barkskin() : SpellScriptLoader("spell_dru_barkskin") { } + + class spell_dru_barkskin_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_barkskin_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_BARKSKIN_01)) + return false; + return true; + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->RemoveAurasDueToSpell(SPELL_DRUID_BARKSKIN_01); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_dru_barkskin_AuraScript::OnRemove, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_barkskin_AuraScript(); + } }; // 1178 - Bear Form (Passive) @@ -140,67 +201,90 @@ class spell_dru_dash : public SpellScriptLoader } }; +// -48516 - Eclipse class spell_dru_eclipse : public SpellScriptLoader { -public: - spell_dru_eclipse() : SpellScriptLoader("spell_dru_eclipse") { } - - class spell_dru_eclipse_AuraScript : public AuraScript - { - PrepareAuraScript(spell_dru_eclipse_AuraScript); + public: + spell_dru_eclipse() : SpellScriptLoader("spell_dru_eclipse") { } - bool Validate(SpellInfo const* /*spellInfo*/) override + class spell_dru_eclipse_AuraScript : public AuraScript { - if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_ECLIPSE_LUNAR_PROC)) - return false; - if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_ECLIPSE_SOLAR_PROC)) - return false; - return true; - } + PrepareAuraScript(spell_dru_eclipse_AuraScript); - bool CheckProc(ProcEventInfo& eventInfo) - { - if (!eventInfo.GetSpellInfo()) - return false; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_ECLIPSE_LUNAR_PROC)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_ECLIPSE_SOLAR_PROC)) + return false; + return true; + } - if (eventInfo.GetActor()->HasAura(SPELL_DRUID_ECLIPSE_LUNAR_PROC) || eventInfo.GetActor()->HasAura(SPELL_DRUID_ECLIPSE_SOLAR_PROC)) - return false; + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetActor()->HasAura(SPELL_DRUID_ECLIPSE_LUNAR_PROC) || eventInfo.GetActor()->HasAura(SPELL_DRUID_ECLIPSE_SOLAR_PROC)) + return false; - // Triggered by Wrath? - if (eventInfo.GetSpellInfo()->SpellFamilyFlags[0] & 1) - return roll_chance_f(GetSpellInfo()->ProcChance * 0.6f) && _lunarProcCooldownEnd <= std::chrono::steady_clock::now(); + return true; + } - return _solarProcCooldownEnd <= std::chrono::steady_clock::now(); - } + bool CheckSolar(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo || !(spellInfo->SpellFamilyFlags[0] & 4)) // Starfire + return false; - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - if (eventInfo.GetSpellInfo()->SpellFamilyFlags[0] & 1) + return _solarProcCooldownEnd <= std::chrono::steady_clock::now(); + } + + bool CheckLunar(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) { - _lunarProcCooldownEnd = std::chrono::steady_clock::now() + Seconds(aurEff->GetAmount()); - eventInfo.GetActor()->CastSpell(eventInfo.GetActor(), SPELL_DRUID_ECLIPSE_LUNAR_PROC, TRIGGERED_FULL_MASK, nullptr, aurEff); + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo || !(spellInfo->SpellFamilyFlags[0] & 1)) // Wrath + return false; + + // Reduced lunar proc chance (60% of normal) + if (!roll_chance_i(60)) + return false; + + return _lunarProcCooldownEnd <= std::chrono::steady_clock::now(); } - else + + void ProcSolar(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { - _solarProcCooldownEnd = std::chrono::steady_clock::now() + Seconds(aurEff->GetAmount()); + PreventDefaultAction(); + + _solarProcCooldownEnd = std::chrono::steady_clock::now() + Seconds(30); eventInfo.GetActor()->CastSpell(eventInfo.GetActor(), SPELL_DRUID_ECLIPSE_SOLAR_PROC, TRIGGERED_FULL_MASK, nullptr, aurEff); } - } - void Register() override - { - DoCheckProc += AuraCheckProcFn(spell_dru_eclipse_AuraScript::CheckProc); - OnEffectProc += AuraEffectProcFn(spell_dru_eclipse_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } + void ProcLunar(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); - std::chrono::steady_clock::time_point _lunarProcCooldownEnd = std::chrono::steady_clock::time_point::min(); - std::chrono::steady_clock::time_point _solarProcCooldownEnd = std::chrono::steady_clock::time_point::min(); - }; + _lunarProcCooldownEnd = std::chrono::steady_clock::now() + Seconds(30); + eventInfo.GetActor()->CastSpell(eventInfo.GetActor(), SPELL_DRUID_ECLIPSE_LUNAR_PROC, TRIGGERED_FULL_MASK, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_dru_eclipse_AuraScript(); - } + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dru_eclipse_AuraScript::CheckProc); + + DoCheckEffectProc += AuraCheckEffectProcFn(spell_dru_eclipse_AuraScript::CheckSolar, EFFECT_0, SPELL_AURA_DUMMY); + DoCheckEffectProc += AuraCheckEffectProcFn(spell_dru_eclipse_AuraScript::CheckLunar, EFFECT_1, SPELL_AURA_DUMMY); + + OnEffectProc += AuraEffectProcFn(spell_dru_eclipse_AuraScript::ProcSolar, EFFECT_0, SPELL_AURA_DUMMY); + OnEffectProc += AuraEffectProcFn(spell_dru_eclipse_AuraScript::ProcLunar, EFFECT_1, SPELL_AURA_DUMMY); + } + + std::chrono::steady_clock::time_point _lunarProcCooldownEnd = std::chrono::steady_clock::time_point::min(); + std::chrono::steady_clock::time_point _solarProcCooldownEnd = std::chrono::steady_clock::time_point::min(); + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_eclipse_AuraScript(); + } }; // 5229 - Enrage @@ -354,6 +438,259 @@ public: } }; +// -33943 - Flight Form +class spell_dru_flight_form : public SpellScriptLoader +{ + public: + spell_dru_flight_form() : SpellScriptLoader("spell_dru_flight_form") { } + + class spell_dru_flight_form_SpellScript : public SpellScript + { + PrepareSpellScript(spell_dru_flight_form_SpellScript); + + SpellCastResult CheckCast() + { + Unit* caster = GetCaster(); + if (caster->IsInDisallowedMountForm()) + return SPELL_FAILED_NOT_SHAPESHIFT; + + return SPELL_CAST_OK; + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_dru_flight_form_SpellScript::CheckCast); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_dru_flight_form_SpellScript(); + } +}; + +// 63057 - Glyph of Barkskin +class spell_dru_glyph_of_barkskin : public SpellScriptLoader +{ + public: + spell_dru_glyph_of_barkskin() : SpellScriptLoader("spell_dru_glyph_of_barkskin") { } + + class spell_dru_glyph_of_barkskin_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_glyph_of_barkskin_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_BARKSKIN_01)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_DRUID_BARKSKIN_01, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dru_glyph_of_barkskin_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_glyph_of_barkskin_AuraScript(); + } +}; + +// 54832 - Glyph of Innervate +class spell_dru_glyph_of_innervate : public SpellScriptLoader +{ + public: + spell_dru_glyph_of_innervate() : SpellScriptLoader("spell_dru_glyph_of_innervate") { } + + class spell_dru_glyph_of_innervate_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_glyph_of_innervate_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_GLYPH_OF_INNERVATE_REGEN)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_DRUID_GLYPH_OF_INNERVATE_REGEN); + int32 amount = CalculatePct(static_cast<int32>(caster->GetCreatePowers(POWER_MANA)), aurEff->GetAmount()); + amount /= spellInfo->GetMaxTicks(); + + caster->CastCustomSpell(SPELL_DRUID_GLYPH_OF_INNERVATE_REGEN, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dru_glyph_of_innervate_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_glyph_of_innervate_AuraScript(); + } +}; + +// 54821 - Glyph of Rake +class spell_dru_glyph_of_rake : public SpellScriptLoader +{ + public: + spell_dru_glyph_of_rake() : SpellScriptLoader("spell_dru_glyph_of_rake") { } + + class spell_dru_glyph_of_rake_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_glyph_of_rake_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_GLYPH_OF_RAKE_TRIGGERED)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetProcTarget()->GetTypeId() == TYPEID_UNIT; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), SPELL_DRUID_GLYPH_OF_RAKE_TRIGGERED, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dru_glyph_of_rake_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dru_glyph_of_rake_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_glyph_of_rake_AuraScript(); + } +}; + +// 54754 - Glyph of Rejuvenation +class spell_dru_glyph_of_rejuvenation : public SpellScriptLoader +{ + public: + spell_dru_glyph_of_rejuvenation() : SpellScriptLoader("spell_dru_glyph_of_rejuvenation") { } + + class spell_dru_glyph_of_rejuvenation_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_glyph_of_rejuvenation_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_GLYPH_OF_REJUVENATION_HEAL)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetProcTarget()->HealthBelowPct(50); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + + int32 amount = CalculatePct(static_cast<int32>(healInfo->GetHeal()), aurEff->GetAmount()); + eventInfo.GetActor()->CastCustomSpell(SPELL_DRUID_GLYPH_OF_REJUVENATION_HEAL, SPELLVALUE_BASE_POINT0, amount, eventInfo.GetProcTarget(), true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dru_glyph_of_rejuvenation_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dru_glyph_of_rejuvenation_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_glyph_of_rejuvenation_AuraScript(); + } +}; + +// 54815 - Glyph of Shred +class spell_dru_glyph_of_shred : public SpellScriptLoader +{ + public: + spell_dru_glyph_of_shred() : SpellScriptLoader("spell_dru_glyph_of_shred") { } + + class spell_dru_glyph_of_shred_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_glyph_of_shred_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_GLYPH_OF_RIP) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_RIP_DURATION_LACERATE_DMG)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + // try to find spell Rip on the target + if (AuraEffect const* rip = eventInfo.GetProcTarget()->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DRUID, 0x00800000, 0x0, 0x0, caster->GetGUID())) + { + // Rip's max duration, note: spells which modifies Rip's duration also counted like Glyph of Rip + uint32 countMin = rip->GetBase()->GetMaxDuration(); + + // just Rip's max duration without other spells + uint32 countMax = rip->GetSpellInfo()->GetMaxDuration(); + + // add possible auras' and Glyph of Shred's max duration + countMax += 3 * aurEff->GetAmount() * IN_MILLISECONDS; // Glyph of Shred -> +6 seconds + countMax += caster->HasAura(SPELL_DRUID_GLYPH_OF_RIP) ? 4 * IN_MILLISECONDS : 0; // Glyph of Rip -> +4 seconds + countMax += caster->HasAura(SPELL_DRUID_RIP_DURATION_LACERATE_DMG) ? 4 * IN_MILLISECONDS : 0; // T7 set bonus -> +4 seconds + + // if min < max -> that means caster didn't cast 3 shred yet + // so set Rip's duration and max duration + if (countMin < countMax) + { + rip->GetBase()->SetDuration(rip->GetBase()->GetDuration() + aurEff->GetAmount() * IN_MILLISECONDS); + rip->GetBase()->SetMaxDuration(countMin + aurEff->GetAmount() * IN_MILLISECONDS); + } + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dru_glyph_of_shred_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_glyph_of_shred_AuraScript(); + } +}; + // 54846 - Glyph of Starfire class spell_dru_glyph_of_starfire : public SpellScriptLoader { @@ -375,6 +712,7 @@ class spell_dru_glyph_of_starfire : public SpellScriptLoader { Unit* caster = GetCaster(); if (Unit* unitTarget = GetHitUnit()) + { if (AuraEffect const* aurEff = unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DRUID, 0x00000002, 0, 0, caster->GetGUID())) { Aura* aura = aurEff->GetBase(); @@ -392,6 +730,7 @@ class spell_dru_glyph_of_starfire : public SpellScriptLoader aura->SetMaxDuration(countMin + 3000); } } + } } void Register() override @@ -406,6 +745,41 @@ class spell_dru_glyph_of_starfire : public SpellScriptLoader } }; +// 54845 - Glyph of Starfire +class spell_dru_glyph_of_starfire_dummy : public SpellScriptLoader +{ + public: + spell_dru_glyph_of_starfire_dummy() : SpellScriptLoader("spell_dru_glyph_of_starfire_dummy") { } + + class spell_dru_glyph_of_starfire_dummy_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_glyph_of_starfire_dummy_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_GLYPH_OF_STARFIRE_SCRIPT)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), SPELL_DRUID_GLYPH_OF_STARFIRE_SCRIPT, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dru_glyph_of_starfire_dummy_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_glyph_of_starfire_dummy_AuraScript(); + } +}; + // 34246 - Idol of the Emerald Queen // 60779 - Idol of Lush Moss class spell_dru_idol_lifebloom : public SpellScriptLoader @@ -501,6 +875,65 @@ class spell_dru_insect_swarm : public SpellScriptLoader } }; +// 24932 - Leader of the Pack +class spell_dru_leader_of_the_pack : public SpellScriptLoader +{ + public: + spell_dru_leader_of_the_pack() : SpellScriptLoader("spell_dru_leader_of_the_pack") { } + + class spell_dru_leader_of_the_pack_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_leader_of_the_pack_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_IMP_LEADER_OF_THE_PACK_R1) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_IMP_LEADER_OF_THE_PACK_HEAL) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_IMP_LEADER_OF_THE_PACK_MANA)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + if (!aurEff->GetAmount()) + return; + + Unit* caster = eventInfo.GetActor(); + if (caster->GetSpellHistory()->HasCooldown(SPELL_DRUID_IMP_LEADER_OF_THE_PACK_HEAL)) + return; + + int32 amount = caster->CountPctFromMaxHealth(aurEff->GetAmount()); + caster->CastCustomSpell(SPELL_DRUID_IMP_LEADER_OF_THE_PACK_HEAL, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + + // Because of how proc system works, we can't store proc cd on db, it would be applied to entire aura + // so aura could only proc once per 6 seconds, independently of caster + caster->GetSpellHistory()->AddCooldown(SPELL_DRUID_IMP_LEADER_OF_THE_PACK_HEAL, 0, Seconds(6)); + + // only proc on self + if (aurEff->GetCasterGUID() != caster->GetGUID()) + return; + + AuraEffect const* impLotpMana = caster->GetAuraEffectOfRankedSpell(SPELL_DRUID_IMP_LEADER_OF_THE_PACK_R1, EFFECT_0, aurEff->GetCasterGUID()); + ASSERT(impLotpMana); + + int32 manaAmount = CalculatePct(static_cast<int32>(caster->GetMaxPower(POWER_MANA)), impLotpMana->GetSpellInfo()->Effects[EFFECT_1].CalcValue()); + caster->CastCustomSpell(SPELL_DRUID_IMP_LEADER_OF_THE_PACK_MANA, SPELLVALUE_BASE_POINT0, manaAmount, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dru_leader_of_the_pack_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_leader_of_the_pack_AuraScript(); + } +}; + // -33763 - Lifebloom class spell_dru_lifebloom : public SpellScriptLoader { @@ -603,8 +1036,13 @@ class spell_dru_living_seed : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - int32 amount = CalculatePct(eventInfo.GetHealInfo()->GetHeal(), aurEff->GetAmount()); - GetTarget()->CastCustomSpell(SPELL_DRUID_LIVING_SEED_PROC, SPELLVALUE_BASE_POINT0, amount, eventInfo.GetProcTarget(), true, NULL, aurEff); + + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + + int32 amount = CalculatePct(healInfo->GetHeal(), aurEff->GetAmount()); + GetTarget()->CastCustomSpell(SPELL_DRUID_LIVING_SEED_PROC, SPELLVALUE_BASE_POINT0, amount, eventInfo.GetProcTarget(), true, nullptr, aurEff); } void Register() override @@ -705,6 +1143,43 @@ class spell_dru_moonkin_form_passive : public SpellScriptLoader } }; +// 16864 - Omen of Clarity +class spell_dru_omen_of_clarity : public SpellScriptLoader +{ + public: + spell_dru_omen_of_clarity() : SpellScriptLoader("spell_dru_omen_of_clarity") { } + + class spell_dru_omen_of_clarity_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_omen_of_clarity_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_BALANCE_T10_BONUS) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_BALANCE_T10_BONUS_PROC)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + Unit* target = GetTarget(); + if (target->HasAura(SPELL_DRUID_BALANCE_T10_BONUS)) + target->CastSpell((Unit*)nullptr, SPELL_DRUID_BALANCE_T10_BONUS_PROC, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dru_omen_of_clarity_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_omen_of_clarity_AuraScript(); + } +}; + // 48391 - Owlkin Frenzy class spell_dru_owlkin_frenzy : public SpellScriptLoader { @@ -812,6 +1287,68 @@ class spell_dru_primal_tenacity : public SpellScriptLoader } }; +// -48539 - Revitalize +class spell_dru_revitalize : public SpellScriptLoader +{ + public: + spell_dru_revitalize() : SpellScriptLoader("spell_dru_revitalize") { } + + class spell_dru_revitalize_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_revitalize_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_REVITALIZE_ENERGIZE_MANA) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_REVITALIZE_ENERGIZE_RAGE) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_REVITALIZE_ENERGIZE_ENERGY) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_REVITALIZE_ENERGIZE_RP)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + if (!roll_chance_i(aurEff->GetAmount())) + return; + + Unit* target = eventInfo.GetProcTarget(); + uint32 spellId; + + switch (target->getPowerType()) + { + case POWER_MANA: + spellId = SPELL_DRUID_REVITALIZE_ENERGIZE_MANA; + break; + case POWER_RAGE: + spellId = SPELL_DRUID_REVITALIZE_ENERGIZE_RAGE; + break; + case POWER_ENERGY: + spellId = SPELL_DRUID_REVITALIZE_ENERGIZE_ENERGY; + break; + case POWER_RUNIC_POWER: + spellId = SPELL_DRUID_REVITALIZE_ENERGIZE_RP; + break; + default: + return; + } + + eventInfo.GetActor()->CastSpell(target, spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dru_revitalize_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_revitalize_AuraScript(); + } +}; + // -1079 - Rip class spell_dru_rip : public SpellScriptLoader { @@ -860,7 +1397,7 @@ class spell_dru_rip : public SpellScriptLoader } }; -// 62606 - Savage Defense +// 62600 - Savage Defense class spell_dru_savage_defense : public SpellScriptLoader { public: @@ -870,37 +1407,24 @@ class spell_dru_savage_defense : public SpellScriptLoader { PrepareAuraScript(spell_dru_savage_defense_AuraScript); - public: - spell_dru_savage_defense_AuraScript() - { - absorbPct = 0; - } - - private: - uint32 absorbPct; - - bool Load() override + bool Validate(SpellInfo const* spellInfo) override { - absorbPct = GetSpellInfo()->Effects[EFFECT_0].CalcValue(GetCaster()); + if (!sSpellMgr->GetSpellInfo(spellInfo->Effects[EFFECT_0].TriggerSpell)) + return false; return true; } - void CalculateAmount(AuraEffect const* /*aurEff*/, int32 & amount, bool & /*canBeRecalculated*/) - { - // Set absorbtion amount to unlimited - amount = -1; - } - - void Absorb(AuraEffect* aurEff, DamageInfo & /*dmgInfo*/, uint32 & absorbAmount) + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { - absorbAmount = uint32(CalculatePct(GetTarget()->GetTotalAttackPowerValue(BASE_ATTACK), absorbPct)); - aurEff->SetAmount(0); + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + int32 amount = static_cast<int32>(CalculatePct(caster->GetTotalAttackPowerValue(BASE_ATTACK), aurEff->GetAmount())); + caster->CastCustomSpell(GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); } void Register() override { - DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_savage_defense_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB); - OnEffectAbsorb += AuraEffectAbsorbFn(spell_dru_savage_defense_AuraScript::Absorb, EFFECT_0); + OnEffectProc += AuraEffectProcFn(spell_dru_savage_defense_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); } }; @@ -1088,7 +1612,7 @@ class spell_dru_survival_instincts : public SpellScriptLoader { Unit* target = GetTarget(); int32 bp0 = target->CountPctFromMaxHealth(aurEff->GetAmount()); - target->CastCustomSpell(target, SPELL_DRUID_SURVIVAL_INSTINCTS, &bp0, NULL, NULL, true); + target->CastCustomSpell(target, SPELL_DRUID_SURVIVAL_INSTINCTS, &bp0, nullptr, nullptr, true, nullptr, aurEff); } void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) @@ -1129,7 +1653,7 @@ class spell_dru_swift_flight_passive : public SpellScriptLoader return GetCaster()->GetTypeId() == TYPEID_PLAYER; } - void CalculateAmount(AuraEffect const* /*aurEff*/, int32 & amount, bool & /*canBeRecalculated*/) + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool & /*canBeRecalculated*/) { if (Player* caster = GetCaster()->ToPlayer()) if (caster->Has310Flyer(false)) @@ -1148,37 +1672,6 @@ class spell_dru_swift_flight_passive : public SpellScriptLoader } }; -// -33943 - Flight Form -class spell_dru_flight_form : public SpellScriptLoader -{ - public: - spell_dru_flight_form() : SpellScriptLoader("spell_dru_flight_form") { } - - class spell_dru_flight_form_SpellScript : public SpellScript - { - PrepareSpellScript(spell_dru_flight_form_SpellScript); - - SpellCastResult CheckCast() - { - Unit* caster = GetCaster(); - if (caster->IsInDisallowedMountForm()) - return SPELL_FAILED_NOT_SHAPESHIFT; - - return SPELL_CAST_OK; - } - - void Register() override - { - OnCheckCast += SpellCheckCastFn(spell_dru_flight_form_SpellScript::CheckCast); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_dru_flight_form_SpellScript(); - } -}; - // -5217 - Tiger's Fury class spell_dru_tiger_s_fury : public SpellScriptLoader { @@ -1236,6 +1729,247 @@ class spell_dru_typhoon : public SpellScriptLoader } }; +// 28716 - Rejuvenation +class spell_dru_t3_2p_bonus : public SpellScriptLoader +{ + public: + spell_dru_t3_2p_bonus() : SpellScriptLoader("spell_dru_t3_2p_bonus") { } + + class spell_dru_t3_2p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_t3_2p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_T3_PROC_ENERGIZE_MANA) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_T3_PROC_ENERGIZE_RAGE) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_T3_PROC_ENERGIZE_ENERGY)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + if (!roll_chance_i(50)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* target = eventInfo.GetProcTarget(); + uint32 spellId; + + switch (target->getPowerType()) + { + case POWER_MANA: + spellId = SPELL_DRUID_T3_PROC_ENERGIZE_MANA; + break; + case POWER_RAGE: + spellId = SPELL_DRUID_T3_PROC_ENERGIZE_RAGE; + break; + case POWER_ENERGY: + spellId = SPELL_DRUID_T3_PROC_ENERGIZE_ENERGY; + break; + default: + return; + } + + eventInfo.GetActor()->CastSpell(target, spellId, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dru_t3_2p_bonus_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dru_t3_2p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_t3_2p_bonus_AuraScript(); + } +}; + +// 28744 - Regrowth +class spell_dru_t3_6p_bonus : public SpellScriptLoader +{ + public: + spell_dru_t3_6p_bonus() : SpellScriptLoader("spell_dru_t3_6p_bonus") { } + + class spell_dru_t3_6p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_t3_6p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_BLESSING_OF_THE_CLAW)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), SPELL_DRUID_BLESSING_OF_THE_CLAW, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dru_t3_6p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_t3_6p_bonus_AuraScript(); + } +}; + +// 28719 - Healing Touch +class spell_dru_t3_8p_bonus : public SpellScriptLoader +{ + public: + spell_dru_t3_8p_bonus() : SpellScriptLoader("spell_dru_t3_8p_bonus") { } + + class spell_dru_t3_8p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_t3_8p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_EXHILARATE)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; + + Unit* caster = eventInfo.GetActor(); + int32 amount = CalculatePct(spellInfo->CalcPowerCost(caster, spellInfo->GetSchoolMask()), aurEff->GetAmount()); + caster->CastCustomSpell(SPELL_DRUID_EXHILARATE, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dru_t3_8p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_t3_8p_bonus_AuraScript(); + } +}; + +// 37288 - Mana Restore +// 37295 - Mana Restore +class spell_dru_t4_2p_bonus : public SpellScriptLoader +{ + public: + spell_dru_t4_2p_bonus() : SpellScriptLoader("spell_dru_t4_2p_bonus") { } + + class spell_dru_t4_2p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_t4_2p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_INFUSION)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_DRUID_INFUSION, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dru_t4_2p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_t4_2p_bonus_AuraScript(); + } +}; + +// 40442 - Druid Tier 6 Trinket +class spell_dru_item_t6_trinket : public SpellScriptLoader +{ + public: + spell_dru_item_t6_trinket() : SpellScriptLoader("spell_dru_item_t6_trinket") { } + + class spell_dru_item_t6_trinket_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_item_t6_trinket_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_BLESSING_OF_REMULOS) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_BLESSING_OF_ELUNE) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_BLESSING_OF_CENARIUS)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; + + uint32 spellId; + int32 chance; + + // Starfire + if (spellInfo->SpellFamilyFlags[0] & 0x00000004) + { + spellId = SPELL_DRUID_BLESSING_OF_REMULOS; + chance = 25; + } + // Rejuvenation + else if (spellInfo->SpellFamilyFlags[0] & 0x00000010) + { + spellId = SPELL_DRUID_BLESSING_OF_ELUNE; + chance = 25; + } + // Mangle (Bear) and Mangle (Cat) + else if (spellInfo->SpellFamilyFlags[1] & 0x00000440) + { + spellId = SPELL_DRUID_BLESSING_OF_CENARIUS; + chance = 40; + } + else + return; + + if (roll_chance_i(chance)) + eventInfo.GetActor()->CastSpell((Unit*)nullptr, spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dru_item_t6_trinket_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_item_t6_trinket_AuraScript(); + } +}; + // 67353 - T9 Feral Relic (Idol of Mutilation) class spell_dru_t9_feral_relic : public SpellScriptLoader { @@ -1307,6 +2041,55 @@ public: } }; +// 70723 - Item - Druid T10 Balance 4P Bonus +class spell_dru_t10_balance_4p_bonus : public SpellScriptLoader +{ + public: + spell_dru_t10_balance_4p_bonus() : SpellScriptLoader("spell_dru_t10_balance_4p_bonus") { } + + class spell_dru_t10_balance_4p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_t10_balance_4p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_LANGUISH)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_DRUID_LANGUISH); + int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + amount /= spellInfo->GetMaxTicks(); + // Add remaining ticks to damage done + amount += target->GetRemainingPeriodicAmount(caster->GetGUID(), SPELL_DRUID_LANGUISH, SPELL_AURA_PERIODIC_DAMAGE); + + caster->CastCustomSpell(SPELL_DRUID_LANGUISH, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_dru_t10_balance_4p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_t10_balance_4p_bonus_AuraScript(); + } +}; + // 70691 - Item T10 Restoration 4P Bonus class spell_dru_t10_restoration_4p_bonus : public SpellScriptLoader { @@ -1362,6 +2145,61 @@ class spell_dru_t10_restoration_4p_bonus : public SpellScriptLoader } }; +// 70664 - Druid T10 Restoration 4P Bonus (Rejuvenation) +class spell_dru_t10_restoration_4p_bonus_dummy : public SpellScriptLoader +{ + public: + spell_dru_t10_restoration_4p_bonus_dummy() : SpellScriptLoader("spell_dru_t10_restoration_4p_bonus_dummy") { } + + class spell_dru_t10_restoration_4p_bonus_dummy_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_t10_restoration_4p_bonus_dummy_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_REJUVENATION_T10_PROC)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo || spellInfo->Id == SPELL_DRUID_REJUVENATION_T10_PROC) + return false; + + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return false; + + Player* caster = eventInfo.GetActor()->ToPlayer(); + if (!caster) + return false; + + return caster->GetGroup() || caster != eventInfo.GetProcTarget(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + int32 amount = static_cast<int32>(eventInfo.GetHealInfo()->GetHeal()); + eventInfo.GetActor()->CastCustomSpell(SPELL_DRUID_REJUVENATION_T10_PROC, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dru_t10_restoration_4p_bonus_dummy_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dru_t10_restoration_4p_bonus_dummy_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_t10_restoration_4p_bonus_dummy_AuraScript(); + } +}; + class RaidCheck { public: @@ -1434,22 +2272,33 @@ class spell_dru_wild_growth : public SpellScriptLoader void AddSC_druid_spell_scripts() { + new spell_dru_barkskin(); new spell_dru_bear_form_passive(); new spell_dru_dash(); new spell_dru_eclipse(); new spell_dru_enrage(); new spell_dru_forms_trinket(); + new spell_dru_flight_form(); + new spell_dru_glyph_of_barkskin(); + new spell_dru_glyph_of_innervate(); + new spell_dru_glyph_of_rake(); + new spell_dru_glyph_of_rejuvenation(); + new spell_dru_glyph_of_shred(); new spell_dru_glyph_of_starfire(); + new spell_dru_glyph_of_starfire_dummy(); new spell_dru_idol_lifebloom(); new spell_dru_innervate(); new spell_dru_insect_swarm(); + new spell_dru_leader_of_the_pack(); new spell_dru_lifebloom(); new spell_dru_living_seed(); new spell_dru_living_seed_proc(); new spell_dru_moonkin_form_passive(); + new spell_dru_omen_of_clarity(); new spell_dru_owlkin_frenzy(); new spell_dru_predatory_strikes(); new spell_dru_primal_tenacity(); + new spell_dru_revitalize(); new spell_dru_rip(); new spell_dru_savage_defense(); new spell_dru_savage_roar(); @@ -1457,10 +2306,16 @@ void AddSC_druid_spell_scripts() new spell_dru_starfall_dummy(); new spell_dru_survival_instincts(); new spell_dru_swift_flight_passive(); - new spell_dru_flight_form(); new spell_dru_tiger_s_fury(); new spell_dru_typhoon(); + new spell_dru_t3_2p_bonus(); + new spell_dru_t3_6p_bonus(); + new spell_dru_t3_8p_bonus(); + new spell_dru_t4_2p_bonus(); + new spell_dru_item_t6_trinket(); new spell_dru_t9_feral_relic(); + new spell_dru_t10_balance_4p_bonus(); new spell_dru_t10_restoration_4p_bonus(); + new spell_dru_t10_restoration_4p_bonus_dummy(); new spell_dru_wild_growth(); } diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 168284b31bc..d0fcd1080bb 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -113,7 +113,8 @@ class spell_gen_adaptive_warding : public SpellScriptLoader bool CheckProc(ProcEventInfo& eventInfo) { - if (eventInfo.GetDamageInfo()->GetSpellInfo()) // eventInfo.GetSpellInfo() + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetSpellInfo()) return false; // find Mage Armor @@ -156,7 +157,7 @@ class spell_gen_adaptive_warding : public SpellScriptLoader default: return; } - GetTarget()->CastSpell(GetTarget(), spellId, true, NULL, aurEff); + GetTarget()->CastSpell(GetTarget(), spellId, true, nullptr, aurEff); } void Register() override @@ -1099,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); } }; @@ -1443,51 +1452,6 @@ class spell_gen_ds_flush_knockback : public SpellScriptLoader } }; -enum DummyTrigger -{ - SPELL_PERSISTANT_SHIELD_TRIGGERED = 26470, - SPELL_PERSISTANT_SHIELD = 26467 -}; - -class spell_gen_dummy_trigger : public SpellScriptLoader -{ - public: - spell_gen_dummy_trigger() : SpellScriptLoader("spell_gen_dummy_trigger") { } - - class spell_gen_dummy_trigger_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_dummy_trigger_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_PERSISTANT_SHIELD_TRIGGERED) || - !sSpellMgr->GetSpellInfo(SPELL_PERSISTANT_SHIELD)) - return false; - return true; - } - - void HandleDummy(SpellEffIndex /* effIndex */) - { - int32 damage = GetEffectValue(); - Unit* caster = GetCaster(); - if (Unit* target = GetHitUnit()) - if (SpellInfo const* triggeredByAuraSpell = GetTriggeringSpell()) - if (triggeredByAuraSpell->Id == SPELL_PERSISTANT_SHIELD_TRIGGERED) - caster->CastCustomSpell(target, SPELL_PERSISTANT_SHIELD_TRIGGERED, &damage, NULL, NULL, true); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_dummy_trigger_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_gen_dummy_trigger_SpellScript(); - } -}; - class spell_gen_dungeon_credit : public SpellScriptLoader { public: @@ -2357,7 +2321,8 @@ class spell_gen_obsidian_armor : public SpellScriptLoader bool CheckProc(ProcEventInfo& eventInfo) { - if (eventInfo.GetDamageInfo()->GetSpellInfo()) // eventInfo.GetSpellInfo() + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetSpellInfo()) return false; if (GetFirstSchoolInMask(eventInfo.GetSchoolMask()) == SPELL_SCHOOL_NORMAL) @@ -2763,6 +2728,41 @@ class spell_gen_orc_disguise : public SpellScriptLoader } }; +class spell_gen_proc_below_pct_damaged : public SpellScriptLoader +{ + public: + spell_gen_proc_below_pct_damaged(const char* name) : SpellScriptLoader(name) { } + + class spell_gen_proc_below_pct_damaged_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gen_proc_below_pct_damaged_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return false; + + int32 pct = GetSpellInfo()->Effects[EFFECT_0].CalcValue(); + + if (eventInfo.GetActionTarget()->HealthBelowPctDamaged(pct, damageInfo->GetDamage())) + return true; + + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_gen_proc_below_pct_damaged_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_gen_proc_below_pct_damaged_AuraScript(); + } +}; + enum ParachuteSpells { SPELL_PARACHUTE = 45472, @@ -3390,41 +3390,53 @@ class spell_gen_tournament_pennant : public SpellScriptLoader } }; - - enum PvPTrinketTriggeredSpells { SPELL_WILL_OF_THE_FORSAKEN_COOLDOWN_TRIGGER = 72752, SPELL_WILL_OF_THE_FORSAKEN_COOLDOWN_TRIGGER_WOTF = 72757 }; +template <uint32 TriggeredSpellId> class spell_pvp_trinket_wotf_shared_cd : public SpellScriptLoader { public: - spell_pvp_trinket_wotf_shared_cd() : SpellScriptLoader("spell_pvp_trinket_wotf_shared_cd") { } + spell_pvp_trinket_wotf_shared_cd(char const* ScriptName) : SpellScriptLoader(ScriptName) { } + template <uint32 Triggered> class spell_pvp_trinket_wotf_shared_cd_SpellScript : public SpellScript { PrepareSpellScript(spell_pvp_trinket_wotf_shared_cd_SpellScript); - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } - bool Validate(SpellInfo const* /*spellInfo*/) override { - if (!sSpellMgr->GetSpellInfo(SPELL_WILL_OF_THE_FORSAKEN_COOLDOWN_TRIGGER) || - !sSpellMgr->GetSpellInfo(SPELL_WILL_OF_THE_FORSAKEN_COOLDOWN_TRIGGER_WOTF)) + if (!sSpellMgr->GetSpellInfo(Triggered)) return false; return true; } void HandleScript() { - // This is only needed because spells cast from spell_linked_spell are triggered by default - // Spell::SendSpellCooldown() skips all spells with TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD - GetCaster()->GetSpellHistory()->StartCooldown(GetSpellInfo(), 0, GetSpell()); + /* + * @workaround: PendingCast flag normally means 'triggered' spell, however + * if the spell is cast triggered, the core won't send SMSG_SPELL_GO packet + * so client never registers the cooldown (see Spell::IsNeedSendToClient) + * + * ServerToClient: SMSG_SPELL_GO (0x0132) Length: 42 ConnIdx: 0 Time: 07/19/2010 02:32:35.000 Number: 362675 + * Caster GUID: Full: Player + * Caster Unit GUID: Full: Player + * Cast Count: 0 + * Spell ID: 72752 (72752) + * Cast Flags: PendingCast, Unknown3, Unknown7 (265) + * Time: 3901468825 + * Hit Count: 1 + * [0] Hit GUID: Player + * Miss Count: 0 + * Target Flags: Unit (2) + * Target GUID: 0x0 + */ + + // Spell flags need further research, until then just cast not triggered + GetCaster()->CastSpell((Unit*)nullptr, Triggered, false); } void Register() override @@ -3435,7 +3447,7 @@ class spell_pvp_trinket_wotf_shared_cd : public SpellScriptLoader SpellScript* GetSpellScript() const override { - return new spell_pvp_trinket_wotf_shared_cd_SpellScript(); + return new spell_pvp_trinket_wotf_shared_cd_SpellScript<TriggeredSpellId>(); } }; @@ -3535,6 +3547,53 @@ class spell_gen_upper_deck_create_foam_sword : public SpellScriptLoader } }; +enum VampiricTouch +{ + SPELL_VAMPIRIC_TOUCH_HEAL = 52724 +}; + +// 52723 - Vampiric Touch +// 60501 - Vampiric Touch +class spell_gen_vampiric_touch : public SpellScriptLoader +{ + public: + spell_gen_vampiric_touch() : SpellScriptLoader("spell_gen_vampiric_touch") { } + + class spell_gen_vampiric_touch_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gen_vampiric_touch_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_VAMPIRIC_TOUCH_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + Unit* caster = eventInfo.GetActor(); + int32 bp = damageInfo->GetDamage() / 2; + caster->CastCustomSpell(SPELL_VAMPIRIC_TOUCH_HEAL, SPELLVALUE_BASE_POINT0, bp, caster, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_gen_vampiric_touch_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_gen_vampiric_touch_AuraScript(); + } +}; + enum VehicleScaling { SPELL_GEAR_SCALING = 66668 @@ -4262,8 +4321,10 @@ class spell_gen_pony_mount_check : public SpellScriptLoader void HandleEffectPeriodic(AuraEffect const* /*aurEff*/) { Unit* caster = GetCaster(); + if (!caster) + return; Player* owner = caster->GetOwner()->ToPlayer(); - if (!caster || !owner || !owner->HasAchieved(ACHIEV_PONY_UP)) + if (!owner || !owner->HasAchieved(ACHIEV_PONY_UP)) return; if (owner->IsMounted()) @@ -4322,7 +4383,6 @@ void AddSC_generic_spell_scripts() new spell_gen_despawn_self(); new spell_gen_divine_storm_cd_reset(); new spell_gen_ds_flush_knockback(); - new spell_gen_dummy_trigger(); new spell_gen_dungeon_credit(); new spell_gen_elune_candle(); new spell_gen_gadgetzan_transporter_backfire(); @@ -4350,6 +4410,12 @@ void AddSC_generic_spell_scripts() new spell_gen_on_tournament_mount(); new spell_gen_oracle_wolvar_reputation(); new spell_gen_orc_disguise(); + new spell_gen_proc_below_pct_damaged("spell_item_soul_harvesters_charm"); + new spell_gen_proc_below_pct_damaged("spell_item_commendation_of_kaelthas"); + new spell_gen_proc_below_pct_damaged("spell_item_corpse_tongue_coin"); + new spell_gen_proc_below_pct_damaged("spell_item_corpse_tongue_coin_heroic"); + new spell_gen_proc_below_pct_damaged("spell_item_petrified_twilight_scale"); + new spell_gen_proc_below_pct_damaged("spell_item_petrified_twilight_scale_heroic"); new spell_gen_parachute(); new spell_gen_pet_summoned(); new spell_gen_profession_research(); @@ -4364,9 +4430,11 @@ void AddSC_generic_spell_scripts() new spell_gen_throw_shield(); new spell_gen_tournament_duel(); new spell_gen_tournament_pennant(); - new spell_pvp_trinket_wotf_shared_cd(); + new spell_pvp_trinket_wotf_shared_cd<SPELL_WILL_OF_THE_FORSAKEN_COOLDOWN_TRIGGER>("spell_pvp_trinket_shared_cd"); + new spell_pvp_trinket_wotf_shared_cd<SPELL_WILL_OF_THE_FORSAKEN_COOLDOWN_TRIGGER_WOTF>("spell_wotf_shared_cd"); new spell_gen_turkey_marker(); new spell_gen_upper_deck_create_foam_sword(); + new spell_gen_vampiric_touch(); new spell_gen_vehicle_scaling(); new spell_gen_vendor_bark_trigger(); new spell_gen_wg_water(); diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 84fe9297344..b341c5799ed 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -60,8 +60,14 @@ enum HunterSpells SPELL_HUNTER_VIPER_ATTACK_SPEED = 60144, SPELL_DRAENEI_GIFT_OF_THE_NAARU = 59543, SPELL_ROAR_OF_SACRIFICE_TRIGGERED = 67481, - SPELL_LOCK_AND_LOAD_TRIGGER = 56453, - SPELL_LOCK_AND_LOAD_MARKER = 67544 + SPELL_HUNTER_LOCK_AND_LOAD_TRIGGER = 56453, + SPELL_HUNTER_LOCK_AND_LOAD_MARKER = 67544, + SPELL_HUNTER_KILL_COMMAND_HUNTER = 34027, + SPELL_HUNTER_THRILL_OF_THE_HUNT_MANA = 34720, + SPELL_REPLENISHMENT = 57669, + SPELL_HUNTER_RAPID_RECUPERATION_MANA_R1 = 56654, + SPELL_HUNTER_RAPID_RECUPERATION_MANA_R2 = 58882, + SPELL_HUNTER_GLYPH_OF_MEND_PET_HAPPINESS = 57894 }; // 13161 - Aspect of the Beast @@ -209,53 +215,51 @@ class spell_hun_chimera_shot : public SpellScriptLoader flag96 familyFlag = aura->GetSpellInfo()->SpellFamilyFlags; if (!(familyFlag[1] & 0x00000080 || familyFlag[0] & 0x0000C000)) continue; - if (AuraEffect* aurEff = aura->GetEffect(0)) + if (AuraEffect const* aurEff = aura->GetEffect(EFFECT_0)) { // Serpent Sting - Instantly deals 40% of the damage done by your Serpent Sting. if (familyFlag[0] & 0x4000) { - int32 TickCount = aurEff->GetTotalTicks(); spellId = SPELL_HUNTER_CHIMERA_SHOT_SERPENT; + + // first, calculate damage of basic tick (C&P from AuraEffect::HandlePeriodicDamageAurasTick) basePoint = (aurEff->GetAmount() + aurEff->GetBonusAmount()) * aurEff->GetDonePct(); - ApplyPct(basePoint, TickCount * 40); - basePoint = unitTarget->SpellDamageBonusTaken(caster, aura->GetSpellInfo(), basePoint, DOT, aura->GetStackAmount()); if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(aura->GetSpellInfo()->Id, SPELLMOD_DOT, basePoint); + modOwner->ApplySpellMod<SPELLMOD_DOT>(aurEff->GetId(), basePoint); + basePoint = unitTarget->SpellDamageBonusTaken(caster, aurEff->GetSpellInfo(), basePoint, DOT, aura->GetStackAmount()); - aurEff->SetBonusAmount(caster->SpellDamageBonusDone(unitTarget, aurEff->GetSpellInfo(), 0, DOT)); + // then, multiply to get damage potential + basePoint *= aurEff->GetTotalTicks(); + ApplyPct(basePoint, 40); } // Viper Sting - Instantly restores mana to you equal to 60% of the total amount drained by your Viper Sting. else if (familyFlag[1] & 0x00000080) { - int32 TickCount = aura->GetEffect(0)->GetTotalTicks(); spellId = SPELL_HUNTER_CHIMERA_SHOT_VIPER; - // Amount of one aura tick - basePoint = int32(CalculatePct(unitTarget->GetMaxPower(POWER_MANA), aurEff->GetAmount())); - int32 casterBasePoint = aurEff->GetAmount() * unitTarget->GetMaxPower(POWER_MANA) / 50; /// @todo WTF? caster uses unitTarget? - if (basePoint > casterBasePoint) - basePoint = casterBasePoint; - ApplyPct(basePoint, TickCount * 60); + // % of mana drained in max duration + basePoint = aurEff->GetAmount() * aurEff->GetTotalTicks(); + + // max value + int32 maxManaReturn = CalculatePct(static_cast<int32>(caster->GetMaxPower(POWER_MANA)), basePoint * 2); + ApplyPct(basePoint, unitTarget->GetMaxPower(POWER_MANA)); + if (basePoint > maxManaReturn) + basePoint = maxManaReturn; + + ApplyPct(basePoint, 60); } // Scorpid Sting - Attempts to Disarm the target for 10 sec. This effect cannot occur more than once per 1 minute. else if (familyFlag[0] & 0x00008000) spellId = SPELL_HUNTER_CHIMERA_SHOT_SCORPID; - // ?? nothing say in spell desc (possibly need addition check) - //if (familyFlag & 0x0000010000000000LL || // dot - // familyFlag & 0x0000100000000000LL) // stun - //{ - // spellId = 53366; // 53366 Chimera Shot - Wyvern - //} // Refresh aura duration aura->RefreshDuration(); } break; } + if (spellId) - caster->CastCustomSpell(unitTarget, spellId, &basePoint, 0, 0, true); - if (spellId == SPELL_HUNTER_CHIMERA_SHOT_SCORPID && caster->ToPlayer()) // Scorpid Sting - Add 1 minute cooldown - caster->GetSpellHistory()->AddCooldown(spellId, 0, std::chrono::minutes(1)); + caster->CastCustomSpell(spellId, SPELLVALUE_BASE_POINT0, basePoint, unitTarget, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD)); } } @@ -271,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 { @@ -364,6 +432,76 @@ class spell_hun_glyph_of_arcane_shot : public SpellScriptLoader } }; +// 57870 - Glyph of Mend Pet +class spell_hun_glyph_of_mend_pet : public SpellScriptLoader +{ + public: + spell_hun_glyph_of_mend_pet() : SpellScriptLoader("spell_hun_glyph_of_mend_pet") { } + + class spell_hun_glyph_of_mend_pet_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hun_glyph_of_mend_pet_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_GLYPH_OF_MEND_PET_HAPPINESS)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetProcTarget()->CastSpell((Unit*)nullptr, SPELL_HUNTER_GLYPH_OF_MEND_PET_HAPPINESS, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_hun_glyph_of_mend_pet_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hun_glyph_of_mend_pet_AuraScript(); + } +}; + +// -53290 - Hunting Party +class spell_hun_hunting_party : public SpellScriptLoader +{ + public: + spell_hun_hunting_party() : SpellScriptLoader("spell_hun_hunting_party") { } + + class spell_hun_hunting_party_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hun_hunting_party_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_REPLENISHMENT)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_REPLENISHMENT, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_hun_hunting_party_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hun_hunting_party_AuraScript(); + } +}; + // -19572 - Improved Mend Pet class spell_hun_improved_mend_pet : public SpellScriptLoader { @@ -442,6 +580,46 @@ class spell_hun_invigoration : public SpellScriptLoader } }; +// 58914 - Kill Command +class spell_hun_kill_command_pet : public SpellScriptLoader +{ + public: + spell_hun_kill_command_pet() : SpellScriptLoader("spell_hun_kill_command_pet") { } + + class spell_hun_kill_command_pet_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hun_kill_command_pet_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_KILL_COMMAND_HUNTER)) + return false; + return true; + } + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + // prevent charge drop (aura has both proc charge and stacks) + PreventDefaultAction(); + + if (Unit* owner = eventInfo.GetActor()->GetOwner()) + owner->RemoveAuraFromStack(SPELL_HUNTER_KILL_COMMAND_HUNTER); + + ModStackAmount(-1); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_hun_kill_command_pet_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hun_kill_command_pet_AuraScript(); + } +}; + // 53478 - Last Stand Pet class spell_hun_last_stand_pet : public SpellScriptLoader { @@ -490,48 +668,57 @@ class spell_hun_lock_and_load : public SpellScriptLoader bool Validate(SpellInfo const* /*spellInfo*/) override { - if (!sSpellMgr->GetSpellInfo(SPELL_LOCK_AND_LOAD_TRIGGER) || - !sSpellMgr->GetSpellInfo(SPELL_LOCK_AND_LOAD_MARKER)) + if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_LOCK_AND_LOAD_TRIGGER) || + !sSpellMgr->GetSpellInfo(SPELL_HUNTER_LOCK_AND_LOAD_MARKER)) return false; return true; } bool CheckProc(ProcEventInfo& eventInfo) { - if (eventInfo.GetActor()->HasAura(SPELL_LOCK_AND_LOAD_MARKER)) + if (eventInfo.GetActor()->HasAura(SPELL_HUNTER_LOCK_AND_LOAD_MARKER)) return false; - return true; } - template <uint32 mask> - void HandleProcs(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + bool CheckTrapProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { - PreventDefaultAction(); + if (!(eventInfo.GetTypeMask() & PROC_FLAG_DONE_TRAP_ACTIVATION)) + return false; - if (!(eventInfo.GetTypeMask() & mask)) - return; + // Do not proc on traps for immolation/explosive trap + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !(damageInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_FROST)) + return false; - // Additional check: do not proc on traps for immolation/explosive trap - // (But still do it for the periodic damage part) - if (mask == PROC_FLAG_DONE_TRAP_ACTIVATION) - if (!(eventInfo.GetDamageInfo()->GetSchoolMask() & SPELL_SCHOOL_MASK_FROST)) - return; + return roll_chance_i(aurEff->GetAmount()); + } - if (!roll_chance_i(aurEff->GetAmount())) - return; + bool CheckPeriodicProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + if (!(eventInfo.GetTypeMask() & PROC_FLAG_DONE_PERIODIC)) + return false; + + return roll_chance_i(aurEff->GetAmount()); + } + + void HandleProc(ProcEventInfo& eventInfo) + { + PreventDefaultAction(); Unit* caster = eventInfo.GetActor(); - caster->CastSpell(caster, SPELL_LOCK_AND_LOAD_TRIGGER, true); - caster->CastSpell(caster, SPELL_LOCK_AND_LOAD_MARKER, true); + caster->CastSpell(caster, SPELL_HUNTER_LOCK_AND_LOAD_TRIGGER, true); + caster->CastSpell(caster, SPELL_HUNTER_LOCK_AND_LOAD_MARKER, true); } void Register() override { DoCheckProc += AuraCheckProcFn(spell_hun_lock_and_load_AuraScript::CheckProc); - OnEffectProc += AuraEffectProcFn(spell_hun_lock_and_load_AuraScript::HandleProcs<PROC_FLAG_DONE_TRAP_ACTIVATION>, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); - OnEffectProc += AuraEffectProcFn(spell_hun_lock_and_load_AuraScript::HandleProcs<PROC_FLAG_DONE_PERIODIC>, EFFECT_1, SPELL_AURA_DUMMY); + DoCheckEffectProc += AuraCheckEffectProcFn(spell_hun_lock_and_load_AuraScript::CheckTrapProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + DoCheckEffectProc += AuraCheckEffectProcFn(spell_hun_lock_and_load_AuraScript::CheckPeriodicProc, EFFECT_1, SPELL_AURA_DUMMY); + + OnProc += AuraProcFn(spell_hun_lock_and_load_AuraScript::HandleProc); } }; @@ -558,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); } @@ -753,11 +968,13 @@ class spell_hun_pet_heart_of_the_phoenix : public SpellScriptLoader { Unit* caster = GetCaster(); if (Unit* owner = caster->GetOwner()) + { if (!caster->HasAura(SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_DEBUFF)) { owner->CastCustomSpell(SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_TRIGGERED, SPELLVALUE_BASE_POINT0, 100, caster, true); caster->CastSpell(caster, SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_DEBUFF, true); } + } } void Register() override @@ -868,6 +1085,64 @@ class spell_hun_rapid_recuperation : public SpellScriptLoader } }; +// -53228 - Rapid Recuperation (talent aura) +class spell_hun_rapid_recuperation_trigger : public SpellScriptLoader +{ + public: + spell_hun_rapid_recuperation_trigger() : SpellScriptLoader("spell_hun_rapid_recuperation_trigger") { } + + class spell_hun_rapid_recuperation_trigger_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hun_rapid_recuperation_trigger_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_RAPID_RECUPERATION_MANA_R1) || + !sSpellMgr->GetSpellInfo(SPELL_HUNTER_RAPID_RECUPERATION_MANA_R2)) + return false; + return true; + } + + void HandleRapidFireProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + // Proc only from Rapid Fire + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo || !(spellInfo->SpellFamilyFlags[0] & 0x00000020)) + { + PreventDefaultAction(); + return; + } + } + + void HandleRapidKillingProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + static uint32 const triggerSpells[2] = { SPELL_HUNTER_RAPID_RECUPERATION_MANA_R1, SPELL_HUNTER_RAPID_RECUPERATION_MANA_R2 }; + + PreventDefaultAction(); + + // Proc only from Rapid Killing + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo || !(spellInfo->SpellFamilyFlags[1] & 0x01000000)) + return; + + uint8 rank = GetSpellInfo()->GetRank(); + uint32 spellId = triggerSpells[rank - 1]; + eventInfo.GetActor()->CastSpell((Unit*)nullptr, spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_hun_rapid_recuperation_trigger_AuraScript::HandleRapidFireProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + OnEffectProc += AuraEffectProcFn(spell_hun_rapid_recuperation_trigger_AuraScript::HandleRapidKillingProc, EFFECT_1, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hun_rapid_recuperation_trigger_AuraScript(); + } +}; + // 23989 - Readiness class spell_hun_readiness : public SpellScriptLoader { @@ -930,9 +1205,16 @@ class spell_hun_roar_of_sacrifice : public SpellScriptLoader return true; } - bool CheckProc(ProcEventInfo& eventInfo) + bool CheckProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { - return GetCaster() && (eventInfo.GetDamageInfo()->GetSchoolMask() & GetEffect(EFFECT_1)->GetMiscValue()) != 0; + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !(damageInfo->GetSchoolMask() & aurEff->GetMiscValue())) + return false; + + if (!GetCaster()) + return false; + + return true; } void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) @@ -945,7 +1227,7 @@ class spell_hun_roar_of_sacrifice : public SpellScriptLoader void Register() override { - DoCheckProc += AuraCheckProcFn(spell_hun_roar_of_sacrifice_AuraScript::CheckProc); + DoCheckEffectProc += AuraCheckEffectProcFn(spell_hun_roar_of_sacrifice_AuraScript::CheckProc, EFFECT_1, SPELL_AURA_DUMMY); OnEffectProc += AuraEffectProcFn(spell_hun_roar_of_sacrifice_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); } }; @@ -1135,6 +1417,64 @@ class spell_hun_target_only_pet_and_owner : public SpellScriptLoader } }; +// -34497 - Thrill of the Hunt +class spell_hun_thrill_of_the_hunt : public SpellScriptLoader +{ + public: + spell_hun_thrill_of_the_hunt() : SpellScriptLoader("spell_hun_thrill_of_the_hunt") { } + + class spell_hun_thrill_of_the_hunt_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hun_thrill_of_the_hunt_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_THRILL_OF_THE_HUNT_MANA)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; + + Unit* caster = eventInfo.GetActor(); + int32 amount = 0; + + // Explosive Shot + if (spellInfo->SpellFamilyFlags[2] & 0x200) + { + if (AuraEffect const* explosiveShot = eventInfo.GetProcTarget()->GetAuraEffect(SPELL_AURA_PERIODIC_DUMMY, SPELLFAMILY_HUNTER, 0x00000000, 0x80000000, 0x00000000, caster->GetGUID())) + { + // due to Lock and Load SpellInfo::CalcPowerCost might return 0, so just calculate it manually + amount = CalculatePct(static_cast<int32>(CalculatePct(caster->GetCreateMana(), explosiveShot->GetSpellInfo()->ManaCostPercentage)), aurEff->GetAmount()); + amount /= explosiveShot->GetSpellInfo()->GetMaxTicks(); + } + } + else + amount = CalculatePct(static_cast<int32>(spellInfo->CalcPowerCost(caster, spellInfo->GetSchoolMask())), aurEff->GetAmount()); + + if (!amount) + return; + + caster->CastCustomSpell(SPELL_HUNTER_THRILL_OF_THE_HUNT_MANA, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_hun_thrill_of_the_hunt_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hun_thrill_of_the_hunt_AuraScript(); + } +}; + // 67151 - T9 4P Bonus class spell_hun_t9_4p_bonus : public SpellScriptLoader { @@ -1201,7 +1541,7 @@ class spell_hun_viper_attack_speed : public SpellScriptLoader void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) { if (GetTarget()->HasAura(SPELL_HUNTER_ASPECT_OF_THE_VIPER)) - GetTarget()->CastSpell(GetTarget(), SPELL_HUNTER_VICIOUS_VIPER, true, NULL, aurEff); + GetTarget()->CastSpell(GetTarget(), SPELL_HUNTER_VICIOUS_VIPER, true, nullptr, aurEff); } void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) @@ -1228,10 +1568,15 @@ 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(); + new spell_hun_hunting_party(); new spell_hun_improved_mend_pet(); new spell_hun_invigoration(); + new spell_hun_kill_command_pet(); new spell_hun_last_stand_pet(); new spell_hun_lock_and_load(); new spell_hun_masters_call(); @@ -1241,12 +1586,14 @@ void AddSC_hunter_spell_scripts() new spell_hun_pet_heart_of_the_phoenix(); new spell_hun_piercing_shots(); new spell_hun_rapid_recuperation(); + new spell_hun_rapid_recuperation_trigger(); new spell_hun_readiness(); new spell_hun_roar_of_sacrifice(); new spell_hun_scatter_shot(); new spell_hun_sniper_training(); new spell_hun_tame_beast(); new spell_hun_target_only_pet_and_owner(); + new spell_hun_thrill_of_the_hunt(); new spell_hun_t9_4p_bonus(); new spell_hun_viper_attack_speed(); } diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index 562625e22f5..9ef5c5d0b4d 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -115,6 +115,143 @@ class spell_item_aegis_of_preservation : public SpellScriptLoader } }; +enum AlchemistStone +{ + SPELL_ALCHEMISTS_STONE_EXTRA_HEAL = 21399, + SPELL_ALCHEMISTS_STONE_EXTRA_MANA = 21400 +}; + +// Item - 13503: Alchemist's Stone +// Item - 35748: Guardian's Alchemist Stone +// Item - 35749: Sorcerer's Alchemist Stone +// Item - 35750: Redeemer's Alchemist Stone +// Item - 35751: Assassin's Alchemist Stone +// Item - 44322: Mercurial Alchemist Stone +// Item - 44323: Indestructible Alchemist's Stone +// Item - 44324: Mighty Alchemist's Stone + +// 17619 - Alchemist's Stone +class spell_item_alchemists_stone : public SpellScriptLoader +{ + public: + spell_item_alchemists_stone() : SpellScriptLoader("spell_item_alchemists_stone") { } + + class spell_item_alchemists_stone_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_alchemists_stone_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_ALCHEMISTS_STONE_EXTRA_HEAL) || + !sSpellMgr->GetSpellInfo(SPELL_ALCHEMISTS_STONE_EXTRA_MANA)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; + + Unit* caster = eventInfo.GetActionTarget(); + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + uint32 spellId; + switch (spellInfo->Effects[i].Effect) + { + case SPELL_EFFECT_HEAL: + spellId = SPELL_ALCHEMISTS_STONE_EXTRA_HEAL; + break; + case SPELL_EFFECT_ENERGIZE: + spellId = SPELL_ALCHEMISTS_STONE_EXTRA_MANA; + break; + default: + continue; + } + + int32 amount = CalculatePct(spellInfo->Effects[i].CalcValue(caster), 40); + caster->CastCustomSpell(spellId, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_alchemists_stone_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_alchemists_stone_AuraScript(); + } +}; + +enum AngerCapacitor +{ + SPELL_MOTE_OF_ANGER = 71432, + SPELL_MANIFEST_ANGER_MAIN_HAND = 71433, + SPELL_MANIFEST_ANGER_OFF_HAND = 71434 +}; + +// Item - 50351: Tiny Abomination in a Jar +// 71406 - Anger Capacitor + +// Item - 50706: Tiny Abomination in a Jar (Heroic) +// 71545 - Anger Capacitor +template <uint8 StackAmount> +class spell_item_anger_capacitor : public SpellScriptLoader +{ + public: + spell_item_anger_capacitor(char const* ScriptName) : SpellScriptLoader(ScriptName) { } + + template <uint8 Stacks> + class spell_item_anger_capacitor_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_anger_capacitor_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MOTE_OF_ANGER) || + !sSpellMgr->GetSpellInfo(SPELL_MANIFEST_ANGER_MAIN_HAND) || + !sSpellMgr->GetSpellInfo(SPELL_MANIFEST_ANGER_OFF_HAND)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + + caster->CastSpell((Unit*)nullptr, SPELL_MOTE_OF_ANGER, true); + Aura const* motes = caster->GetAura(SPELL_MOTE_OF_ANGER); + if (!motes || motes->GetStackAmount() < Stacks) + return; + + caster->RemoveAurasDueToSpell(SPELL_MOTE_OF_ANGER); + uint32 spellId = SPELL_MANIFEST_ANGER_MAIN_HAND; + if (Player* player = caster->ToPlayer()) + if (player->GetWeaponForAttack(OFF_ATTACK, true) && urand(0, 1)) + spellId = SPELL_MANIFEST_ANGER_OFF_HAND; + + caster->CastSpell(target, spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_anger_capacitor_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_anger_capacitor_AuraScript<StackAmount>(); + } +}; + // 26400 - Arcane Shroud class spell_item_arcane_shroud : public SpellScriptLoader { @@ -144,6 +281,170 @@ class spell_item_arcane_shroud : public SpellScriptLoader } }; +// Item - 12846: Argent Dawn Commission +// Item - 13209: Seal of the Dawn +// Item - 19812: Rune of the Dawn + +// 17670 - Argent Dawn Commission +class spell_item_argent_dawn_commission : public SpellScriptLoader +{ + public: + spell_item_argent_dawn_commission() : SpellScriptLoader("spell_item_argent_dawn_commission") { } + + class spell_item_argent_dawn_commission_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_argent_dawn_commission_AuraScript); + + void HandleDummy(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + // Prevent console log + PreventDefaultAction(); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_argent_dawn_commission_AuraScript::HandleDummy, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_argent_dawn_commission_AuraScript(); + } +}; + +enum AuraOfMadness +{ + SPELL_SOCIOPATH = 39511, // Sociopath: +35 strength(Paladin, Rogue, Druid, Warrior) + SPELL_DELUSIONAL = 40997, // Delusional: +70 attack power(Rogue, Hunter, Paladin, Warrior, Druid) + SPELL_KLEPTOMANIA = 40998, // Kleptomania: +35 agility(Warrior, Rogue, Paladin, Hunter, Druid) + SPELL_MEGALOMANIA = 40999, // Megalomania: +41 damage / healing(Druid, Shaman, Priest, Warlock, Mage, Paladin) + SPELL_PARANOIA = 41002, // Paranoia: +35 spell / melee / ranged crit strike rating(All classes) + SPELL_MANIC = 41005, // Manic: +35 haste(spell, melee and ranged) (All classes) + SPELL_NARCISSISM = 41009, // Narcissism: +35 intellect(Druid, Shaman, Priest, Warlock, Mage, Paladin, Hunter) + SPELL_MARTYR_COMPLEX = 41011, // Martyr Complex: +35 stamina(All classes) + SPELL_DEMENTIA = 41404, // Dementia: Every 5 seconds either gives you +5/-5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin) + + SPELL_DEMENTIA_POS = 41406, + SPELL_DEMENTIA_NEG = 41409, + + SAY_MADNESS = 21954 +}; + +// Item - 31859: Darkmoon Card: Madness +// 39446 - Aura of Madness +class spell_item_aura_of_madness : public SpellScriptLoader +{ + public: + spell_item_aura_of_madness() : SpellScriptLoader("spell_item_aura_of_madness") { } + + class spell_item_aura_of_madness_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_aura_of_madness_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SOCIOPATH) || + !sSpellMgr->GetSpellInfo(SPELL_DELUSIONAL) || + !sSpellMgr->GetSpellInfo(SPELL_KLEPTOMANIA) || + !sSpellMgr->GetSpellInfo(SPELL_MEGALOMANIA) || + !sSpellMgr->GetSpellInfo(SPELL_PARANOIA) || + !sSpellMgr->GetSpellInfo(SPELL_MANIC) || + !sSpellMgr->GetSpellInfo(SPELL_NARCISSISM) || + !sSpellMgr->GetSpellInfo(SPELL_MARTYR_COMPLEX) || + !sSpellMgr->GetSpellInfo(SPELL_DEMENTIA) || + !sObjectMgr->GetBroadcastText(SAY_MADNESS)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + static std::vector<uint32> const triggeredSpells[MAX_CLASSES] = + { + //CLASS_NONE + { }, + //CLASS_WARRIOR + { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_MARTYR_COMPLEX }, + //CLASS_PALADIN + { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, + //CLASS_HUNTER + { SPELL_DELUSIONAL, SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, + //CLASS_ROGUE + { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_MARTYR_COMPLEX }, + //CLASS_PRIEST + { SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, + //CLASS_DEATH_KNIGHT + { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_MARTYR_COMPLEX }, + //CLASS_SHAMAN + { SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, + //CLASS_MAGE + { SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, + //CLASS_WARLOCK + { SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, + //CLASS_UNK + { }, + //CLASS_DRUID + { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA } + }; + + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + uint32 spellId = Trinity::Containers::SelectRandomContainerElement(triggeredSpells[caster->getClass()]); + caster->CastSpell(caster, spellId, true, nullptr, aurEff); + + if (roll_chance_i(10)) + caster->Unit::Say(SAY_MADNESS); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_aura_of_madness_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_aura_of_madness_AuraScript(); + } +}; + +// 41404 - Dementia +class spell_item_dementia : public SpellScriptLoader +{ + public: + spell_item_dementia() : SpellScriptLoader("spell_item_dementia") { } + + class spell_item_dementia_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_dementia_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DEMENTIA_POS) || + !sSpellMgr->GetSpellInfo(SPELL_DEMENTIA_NEG)) + return false; + return true; + } + + void HandlePeriodicDummy(AuraEffect const* aurEff) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), RAND(SPELL_DEMENTIA_POS, SPELL_DEMENTIA_NEG), true, nullptr, aurEff); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_item_dementia_AuraScript::HandlePeriodicDummy, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_dementia_AuraScript(); + } +}; + // 64411 - Blessing of Ancient Kings (Val'anyr, Hammer of Ancient Kings) enum BlessingOfAncientKings { @@ -175,7 +476,11 @@ class spell_item_blessing_of_ancient_kings : public SpellScriptLoader { PreventDefaultAction(); - int32 absorb = int32(CalculatePct(eventInfo.GetHealInfo()->GetHeal(), 15.0f)); + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + + int32 absorb = int32(CalculatePct(healInfo->GetHeal(), 15.0f)); if (AuraEffect* protEff = eventInfo.GetProcTarget()->GetAuraEffect(SPELL_PROTECTION_OF_ANCIENT_KINGS, 0, eventInfo.GetActor()->GetGUID())) { // The shield can grow to a maximum size of 20,000 damage absorbtion @@ -185,7 +490,7 @@ class spell_item_blessing_of_ancient_kings : public SpellScriptLoader protEff->GetBase()->RefreshDuration(); } else - GetTarget()->CastCustomSpell(SPELL_PROTECTION_OF_ANCIENT_KINGS, SPELLVALUE_BASE_POINT0, absorb, eventInfo.GetProcTarget(), true, NULL, aurEff); + GetTarget()->CastCustomSpell(SPELL_PROTECTION_OF_ANCIENT_KINGS, SPELLVALUE_BASE_POINT0, absorb, eventInfo.GetProcTarget(), true, nullptr, aurEff); } void Register() override @@ -201,6 +506,168 @@ class spell_item_blessing_of_ancient_kings : public SpellScriptLoader } }; +enum DeadlyPrecision +{ + SPELL_DEADLY_PRECISION = 71564 +}; + +// 71564 - Deadly Precision +class spell_item_deadly_precision : public SpellScriptLoader +{ + public: + spell_item_deadly_precision() : SpellScriptLoader("spell_item_deadly_precision") { } + + class spell_item_deadly_precision_charm_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_deadly_precision_charm_AuraScript); + + void HandleStackDrop(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->RemoveAuraFromStack(GetId(), GetTarget()->GetGUID()); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_deadly_precision_charm_AuraScript::HandleStackDrop, EFFECT_0, SPELL_AURA_MOD_RATING); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_deadly_precision_charm_AuraScript(); + } +}; + +// 71563 - Deadly Precision Dummy +class spell_item_deadly_precision_dummy : public SpellScriptLoader +{ + public: + spell_item_deadly_precision_dummy() : SpellScriptLoader("spell_item_deadly_precision_dummy") { } + + class spell_item_deadly_precision_dummy_SpellScript : public SpellScript + { + PrepareSpellScript(spell_item_deadly_precision_dummy_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DEADLY_PRECISION)) + return false; + return true; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_DEADLY_PRECISION); + GetCaster()->CastCustomSpell(spellInfo->Id, SPELLVALUE_AURA_STACK, spellInfo->StackAmount, GetCaster(), true); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_item_deadly_precision_dummy_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_item_deadly_precision_dummy_SpellScript(); + } +}; + +enum DeathbringersWill +{ + SPELL_STRENGTH_OF_THE_TAUNKA = 71484, // +600 Strength + SPELL_AGILITY_OF_THE_VRYKUL = 71485, // +600 Agility + SPELL_POWER_OF_THE_TAUNKA = 71486, // +1200 Attack Power + SPELL_AIM_OF_THE_IRON_DWARVES = 71491, // +600 Critical + SPELL_SPEED_OF_THE_VRYKUL = 71492, // +600 Haste + + SPELL_AGILITY_OF_THE_VRYKUL_HERO = 71556, // +700 Agility + SPELL_POWER_OF_THE_TAUNKA_HERO = 71558, // +1400 Attack Power + SPELL_AIM_OF_THE_IRON_DWARVES_HERO = 71559, // +700 Critical + SPELL_SPEED_OF_THE_VRYKUL_HERO = 71560, // +700 Haste + SPELL_STRENGTH_OF_THE_TAUNKA_HERO = 71561 // +700 Strength +}; + +// Item - 50362: Deathbringer's Will +// 71519 - Item - Icecrown 25 Normal Melee Trinket + +// Item - 50363: Deathbringer's Will +// 71562 - Item - Icecrown 25 Heroic Melee Trinket +template <uint32 StrengthSpellId, uint32 AgilitySpellId, uint32 APSpellId, uint32 CriticalSpellId, uint32 HasteSpellId> +class spell_item_deathbringers_will : public SpellScriptLoader +{ + public: + spell_item_deathbringers_will(char const* ScriptName) : SpellScriptLoader(ScriptName) { } + + template <uint32 Strength, uint32 Agility, uint32 AttackPower, uint32 Critical, uint32 Haste> + class spell_item_deathbringers_will_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_deathbringers_will_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(Strength) || + !sSpellMgr->GetSpellInfo(Agility) || + !sSpellMgr->GetSpellInfo(AttackPower) || + !sSpellMgr->GetSpellInfo(Critical) || + !sSpellMgr->GetSpellInfo(Haste)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + static std::vector<uint32> const triggeredSpells[MAX_CLASSES] = + { + //CLASS_NONE + { }, + //CLASS_WARRIOR + { Strength, Critical, Haste }, + //CLASS_PALADIN + { Strength, Critical, Haste }, + //CLASS_HUNTER + { Agility, Critical, AttackPower }, + //CLASS_ROGUE + { Agility, Haste, AttackPower }, + //CLASS_PRIEST + { }, + //CLASS_DEATH_KNIGHT + { Strength, Critical, Haste }, + //CLASS_SHAMAN + { Agility, Haste, AttackPower }, + //CLASS_MAGE + { }, + //CLASS_WARLOCK + { }, + //CLASS_UNK + { }, + //CLASS_DRUID + { Strength, Agility, Haste } + }; + + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + std::vector<uint32> const& randomSpells = triggeredSpells[caster->getClass()]; + if (randomSpells.empty()) + return; + + uint32 spellId = Trinity::Containers::SelectRandomContainerElement(randomSpells); + caster->CastSpell(caster, spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_deathbringers_will_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_deathbringers_will_AuraScript<StrengthSpellId, AgilitySpellId, APSpellId, CriticalSpellId, HasteSpellId>(); + } +}; + // 47770 - Roll Dice class spell_item_decahedral_dwarven_dice : public SpellScriptLoader { @@ -400,6 +867,46 @@ class spell_item_deviate_fish : public SpellScriptLoader } }; +enum DiscerningEyeBeastMisc +{ + SPELL_DISCERNING_EYE_BEAST = 59914 +}; + +// 59915 - Discerning Eye of the Beast Dummy +class spell_item_discerning_eye_beast_dummy : public SpellScriptLoader +{ + public: + spell_item_discerning_eye_beast_dummy() : SpellScriptLoader("spell_item_discerning_eye_beast_dummy") { } + + class spell_item_discerning_eye_beast_dummy_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_discerning_eye_beast_dummy_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DISCERNING_EYE_BEAST)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_DISCERNING_EYE_BEAST, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_discerning_eye_beast_dummy_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_discerning_eye_beast_dummy_AuraScript(); + } +}; + // 71610, 71641 - Echoes of Light (Althor's Abacus) class spell_item_echoes_of_light : public SpellScriptLoader { @@ -458,6 +965,7 @@ class spell_item_fate_rune_of_unsurpassed_vigor : public SpellScriptLoader void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) { + PreventDefaultAction(); GetTarget()->CastSpell(GetTarget(), SPELL_UNSURPASSED_VIGOR, true); } @@ -543,6 +1051,53 @@ class spell_item_flask_of_the_north : public SpellScriptLoader } }; +enum FrozenShadoweave +{ + SPELL_SHADOWMEND = 39373 +}; + +// 39372 - Frozen Shadoweave +// Frozen Shadoweave set 3p bonus +class spell_item_frozen_shadoweave : public SpellScriptLoader +{ + public: + spell_item_frozen_shadoweave() : SpellScriptLoader("spell_item_frozen_shadoweave") { } + + class spell_item_frozen_shadoweave_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_frozen_shadoweave_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHADOWMEND)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + Unit* caster = eventInfo.GetActor(); + caster->CastCustomSpell(SPELL_SHADOWMEND, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_frozen_shadoweave_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_frozen_shadoweave_AuraScript(); + } +}; + // http://www.wowhead.com/item=10645 Gnomish Death Ray // 13280 Gnomish Death Ray enum GnomishDeathRay @@ -573,9 +1128,9 @@ class spell_item_gnomish_death_ray : public SpellScriptLoader if (Unit* target = GetHitUnit()) { if (urand(0, 99) < 15) - caster->CastSpell(caster, SPELL_GNOMISH_DEATH_RAY_SELF, true, NULL); // failure + caster->CastSpell(caster, SPELL_GNOMISH_DEATH_RAY_SELF, true); // failure else - caster->CastSpell(target, SPELL_GNOMISH_DEATH_RAY_TARGET, true, NULL); + caster->CastSpell(target, SPELL_GNOMISH_DEATH_RAY_TARGET, true); } } @@ -591,6 +1146,159 @@ class spell_item_gnomish_death_ray : public SpellScriptLoader } }; +// Item 23004 - Idol of Longevity +// 28847 - Healing Touch Refund +enum IdolOfLongevity +{ + SPELL_HEALING_TOUCH_MANA = 28848 +}; + +class spell_item_healing_touch_refund : public SpellScriptLoader +{ + public: + spell_item_healing_touch_refund() : SpellScriptLoader("spell_item_healing_touch_refund") { } + + class spell_item_healing_touch_refund_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_healing_touch_refund_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_HEALING_TOUCH_MANA)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_HEALING_TOUCH_MANA, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_healing_touch_refund_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_healing_touch_refund_AuraScript(); + } +}; + +enum Heartpierce +{ + SPELL_INVIGORATION_MANA = 71881, + SPELL_INVIGORATION_ENERGY = 71882, + SPELL_INVIGORATION_RAGE = 71883, + SPELL_INVIGORATION_RP = 71884, + + SPELL_INVIGORATION_RP_HERO = 71885, + SPELL_INVIGORATION_RAGE_HERO = 71886, + SPELL_INVIGORATION_ENERGY_HERO = 71887, + SPELL_INVIGORATION_MANA_HERO = 71888 +}; + +// Item - 49982: Heartpierce +// 71880 - Item - Icecrown 25 Normal Dagger Proc + +// Item - 50641: Heartpierce (Heroic) +// 71892 - Item - Icecrown 25 Heroic Dagger Proc +template <uint32 EnergySpellId, uint32 ManaSpellId, uint32 RageSpellId, uint32 RPSpellId> +class spell_item_heartpierce : public SpellScriptLoader +{ + public: + spell_item_heartpierce(char const* ScriptName) : SpellScriptLoader(ScriptName) { } + + template <uint32 Energy, uint32 Mana, uint32 Rage, uint32 RunicPower> + class spell_item_heartpierce_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_heartpierce_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(Energy) || + !sSpellMgr->GetSpellInfo(Mana) || + !sSpellMgr->GetSpellInfo(Rage) || + !sSpellMgr->GetSpellInfo(RunicPower)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + + uint32 spellId; + switch (caster->getPowerType()) + { + case POWER_MANA: + spellId = Mana; + break; + case POWER_ENERGY: + spellId = Energy; + break; + case POWER_RAGE: + spellId = Rage; + break; + // Death Knights can't use daggers, but oh well + case POWER_RUNIC_POWER: + spellId = RunicPower; + break; + default: + return; + } + + caster->CastSpell((Unit*)nullptr, spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_heartpierce_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_heartpierce_AuraScript<EnergySpellId, ManaSpellId, RageSpellId, RPSpellId>(); + } +}; + +// 40971 - Bonus Healing (Crystal Spire of Karabor) +class spell_item_crystal_spire_of_karabor : public SpellScriptLoader +{ + public: + spell_item_crystal_spire_of_karabor() : SpellScriptLoader("spell_item_crystal_spire_of_karabor") { } + + class spell_item_crystal_spire_of_karabor_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_crystal_spire_of_karabor_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + int32 pct = GetSpellInfo()->Effects[EFFECT_0].BasePoints; + if (HealInfo* healInfo = eventInfo.GetHealInfo()) + if (Unit* healTarget = healInfo->GetTarget()) + if (healTarget->GetHealth() - healInfo->GetEffectiveHeal() <= healTarget->CountPctFromMaxHealth(pct)) + return true; + + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_item_crystal_spire_of_karabor_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_crystal_spire_of_karabor_AuraScript(); + } +}; + // http://www.wowhead.com/item=27388 Mr. Pinchy // 33060 Make a Wish enum MakeAWish @@ -649,6 +1357,53 @@ class spell_item_make_a_wish : public SpellScriptLoader } }; +enum MarkOfConquest +{ + SPELL_MARK_OF_CONQUEST_ENERGIZE = 39599 +}; + +// Item - 27920: Mark of Conquest +// Item - 27921: Mark of Conquest +// 33510 - Health Restore +class spell_item_mark_of_conquest : public SpellScriptLoader +{ + public: + spell_item_mark_of_conquest() : SpellScriptLoader("spell_item_mark_of_conquest") { } + + class spell_item_mark_of_conquest_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_mark_of_conquest_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MARK_OF_CONQUEST_ENERGIZE)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + if (eventInfo.GetTypeMask() & (PROC_FLAG_DONE_RANGED_AUTO_ATTACK | PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS)) + { + // in that case, do not cast heal spell + PreventDefaultAction(); + // but mana instead + eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_MARK_OF_CONQUEST_ENERGIZE, true, nullptr, aurEff); + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_mark_of_conquest_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_mark_of_conquest_AuraScript(); + } +}; + // http://www.wowhead.com/item=32686 Mingo's Fortune Giblets // 40802 Mingo's Fortune Generator class spell_item_mingos_fortune_generator : public SpellScriptLoader @@ -735,8 +1490,12 @@ class spell_item_necrotic_touch : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - int32 bp = CalculatePct(int32(eventInfo.GetDamageInfo()->GetDamage()), aurEff->GetAmount()); - GetTarget()->CastCustomSpell(SPELL_ITEM_NECROTIC_TOUCH_PROC, SPELLVALUE_BASE_POINT0, bp, eventInfo.GetProcTarget(), true, NULL, aurEff); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 bp = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + GetTarget()->CastCustomSpell(SPELL_ITEM_NECROTIC_TOUCH_PROC, SPELLVALUE_BASE_POINT0, bp, eventInfo.GetProcTarget(), true, nullptr, aurEff); } void Register() override @@ -788,7 +1547,7 @@ class spell_item_net_o_matic : public SpellScriptLoader else if (roll < 4) // 2% for 20 sec root, charge to target (off-like chance unknown) spellId = SPELL_NET_O_MATIC_TRIGGERED2; - GetCaster()->CastSpell(target, spellId, true, NULL); + GetCaster()->CastSpell(target, spellId, true, nullptr); } } @@ -844,7 +1603,7 @@ class spell_item_noggenfogger_elixir : public SpellScriptLoader case 2: spellId = SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED2; break; } - caster->CastSpell(caster, spellId, true, NULL); + caster->CastSpell(caster, spellId, true, nullptr); } void Register() override @@ -859,6 +1618,138 @@ class spell_item_noggenfogger_elixir : public SpellScriptLoader } }; +// 29601 - Enlightenment (Pendant of the Violet Eye) +class spell_item_pendant_of_the_violet_eye : public SpellScriptLoader +{ + public: + spell_item_pendant_of_the_violet_eye() : SpellScriptLoader("spell_item_pendant_of_the_violet_eye") { } + + class spell_item_pendant_of_the_violet_eye_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_pendant_of_the_violet_eye_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + return spellInfo->PowerType == POWER_MANA || (spellInfo->ManaCost != 0 && spellInfo->ManaCostPercentage != 0 && spellInfo->ManaCostPerlevel != 0); + + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_item_pendant_of_the_violet_eye_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_pendant_of_the_violet_eye_AuraScript(); + } +}; + +enum PersistentShieldMisc +{ + SPELL_PERSISTENT_SHIELD_TRIGGERED = 26470 +}; + +// 26467 - Persistent Shield +class spell_item_persistent_shield : public SpellScriptLoader +{ + public: + spell_item_persistent_shield() : SpellScriptLoader("spell_item_persistent_shield") { } + + class spell_item_persistent_shield_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_persistent_shield_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PERSISTENT_SHIELD_TRIGGERED)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetHealInfo() && eventInfo.GetHealInfo()->GetHeal(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + int32 bp0 = CalculatePct(eventInfo.GetHealInfo()->GetHeal(), 15); + + // Scarab Brooch does not replace stronger shields + if (AuraEffect const* shield = target->GetAuraEffect(SPELL_PERSISTENT_SHIELD_TRIGGERED, EFFECT_0, caster->GetGUID())) + if (shield->GetAmount() > bp0) + return; + + caster->CastCustomSpell(SPELL_PERSISTENT_SHIELD_TRIGGERED, SPELLVALUE_BASE_POINT0, bp0, target, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_item_persistent_shield_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_item_persistent_shield_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_persistent_shield_AuraScript(); + } +}; + +enum PetHealing +{ + SPELL_HEALTH_LINK = 37382 +}; + +// 37381 - Pet Healing +// Hunter T5 2P Bonus +// Warlock T5 2P Bonus +class spell_item_pet_healing : public SpellScriptLoader +{ + public: + spell_item_pet_healing() : SpellScriptLoader("spell_item_pet_healing") { } + + class spell_item_pet_healing_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_pet_healing_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_HEALTH_LINK)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 bp = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + Unit* caster = eventInfo.GetActor(); + caster->CastCustomSpell(SPELL_HEALTH_LINK, SPELLVALUE_BASE_POINT0, bp, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_pet_healing_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_pet_healing_AuraScript(); + } +}; + // 17512 - Piccolo of the Flaming Fire class spell_item_piccolo_of_the_flaming_fire : public SpellScriptLoader { @@ -1071,6 +1962,8 @@ class spell_item_shadows_fate : public SpellScriptLoader void HandleProc(ProcEventInfo& procInfo) { + PreventDefaultAction(); + Unit* caster = procInfo.GetActor(); Unit* target = GetCaster(); if (!caster || !target) @@ -1286,6 +2179,91 @@ class spell_item_six_demon_bag : public SpellScriptLoader } }; +enum SwiftHandJusticeMisc +{ + SPELL_SWIFT_HAND_OF_JUSTICE_HEAL = 59913 +}; + +// 59906 - Swift Hand of Justice Dummy +class spell_item_swift_hand_justice_dummy : public SpellScriptLoader +{ + public: + spell_item_swift_hand_justice_dummy() : SpellScriptLoader("spell_item_swift_hand_justice_dummy") { } + + class spell_item_swift_hand_justice_dummy_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_swift_hand_justice_dummy_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SWIFT_HAND_OF_JUSTICE_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + int32 amount = caster->CountPctFromMaxHealth(aurEff->GetAmount()); + caster->CastCustomSpell(SPELL_SWIFT_HAND_OF_JUSTICE_HEAL, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_swift_hand_justice_dummy_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_swift_hand_justice_dummy_AuraScript(); + } +}; + +enum TotemOfFlowingWater +{ + SPELL_LESSER_HEALING_WAVE_MANA = 28850 +}; + +// Item - 23005: Totem of Flowing Water +// 28849 - Lesser Healing Wave +class spell_item_totem_of_flowing_water : public SpellScriptLoader +{ + public: + spell_item_totem_of_flowing_water() : SpellScriptLoader("spell_item_totem_of_flowing_water") { } + + class spell_item_totem_of_flowing_water_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_totem_of_flowing_water_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_LESSER_HEALING_WAVE_MANA)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_LESSER_HEALING_WAVE_MANA, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_totem_of_flowing_water_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_totem_of_flowing_water_AuraScript(); + } + +}; + // 28862 - The Eye of Diminution class spell_item_the_eye_of_diminution : public SpellScriptLoader { @@ -2731,6 +3709,64 @@ public: } }; +enum ShardOfTheScale +{ + SPELL_PURIFIED_CAUTERIZING_HEAL = 69733, + SPELL_PURIFIED_SEARING_FLAMES = 69729, + + SPELL_SHINY_CAUTERIZING_HEAL = 69734, + SPELL_SHINY_SEARING_FLAMES = 69730 +}; + +// Item - 49310: Purified Shard of the Scale +// 69755 - Purified Shard of the Scale - Equip Effect + +// Item - 49488: Shiny Shard of the Scale +// 69739 - Shiny Shard of the Scale - Equip Effect +template <uint32 HealProcSpellId, uint32 DamageProcSpellId> +class spell_item_shard_of_the_scale : public SpellScriptLoader +{ + public: + spell_item_shard_of_the_scale(char const* ScriptName) : SpellScriptLoader(ScriptName) { } + + template <uint32 HealProc, uint32 DamageProc> + class spell_item_shard_of_the_scale_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_shard_of_the_scale_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(HealProc) || + !sSpellMgr->GetSpellInfo(DamageProc)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + + if (eventInfo.GetTypeMask() & PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS) + caster->CastSpell(target, HealProc, true, nullptr, aurEff); + + if (eventInfo.GetTypeMask() & PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG) + caster->CastSpell(target, DamageProc, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_shard_of_the_scale_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_shard_of_the_scale_AuraScript<HealProcSpellId, DamageProcSpellId>(); + } +}; + enum SoulPreserver { SPELL_SOUL_PRESERVER_DRUID = 60512, @@ -2795,6 +3831,90 @@ public: } }; +enum ExaltedSunwellNeck +{ + SPELL_LIGHTS_WRATH = 45479, // Light's Wrath if Exalted by Aldor + SPELL_ARCANE_BOLT = 45429, // Arcane Bolt if Exalted by Scryers + + SPELL_LIGHTS_STRENGTH = 45480, // Light's Strength if Exalted by Aldor + SPELL_ARCANE_STRIKE = 45428, // Arcane Strike if Exalted by Scryers + + SPELL_LIGHTS_WARD = 45432, // Light's Ward if Exalted by Aldor + SPELL_ARCANE_INSIGHT = 45431, // Arcane Insight if Exalted by Scryers + + SPELL_LIGHTS_SALVATION = 45478, // Light's Salvation if Exalted by Aldor + SPELL_ARCANE_SURGE = 45430, // Arcane Surge if Exalted by Scryers + + FACTION_ALDOR = 932, + FACTION_SCRYERS = 934 +}; + +// Item - 34678: Shattered Sun Pendant of Acumen +// 45481 - Sunwell Exalted Caster Neck + +// Item - 34679: Shattered Sun Pendant of Might +// 45482 - Sunwell Exalted Melee Neck + +// Item - 34680: Shattered Sun Pendant of Resolve +// 45483 - Sunwell Exalted Tank Neck + +// Item - 34677: Shattered Sun Pendant of Restoration +// 45484 Sunwell Exalted Healer Neck +template <uint32 AldorSpellId, uint32 ScryersSpellId> +class spell_item_sunwell_neck : public SpellScriptLoader +{ + public: + spell_item_sunwell_neck(char const* ScriptName) : SpellScriptLoader(ScriptName) { } + + template <uint32 Aldors, uint32 Scryers> + class spell_item_sunwell_neck_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_sunwell_neck_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sFactionStore.LookupEntry(FACTION_ALDOR) || + !sFactionStore.LookupEntry(FACTION_SCRYERS) || + !sSpellMgr->GetSpellInfo(Aldors) || + !sSpellMgr->GetSpellInfo(Scryers)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetActor()->GetTypeId() != TYPEID_PLAYER) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Player* player = eventInfo.GetActor()->ToPlayer(); + Unit* target = eventInfo.GetProcTarget(); + + // Aggression checks are in the spell system... just cast and forget + if (player->GetReputationRank(FACTION_ALDOR) == REP_EXALTED) + player->CastSpell(target, Aldors, true, nullptr, aurEff); + + if (player->GetReputationRank(FACTION_SCRYERS) == REP_EXALTED) + player->CastSpell(target, Scryers, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_item_sunwell_neck_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_item_sunwell_neck_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_sunwell_neck_AuraScript<AldorSpellId, ScryersSpellId>(); + } +}; + class spell_item_toy_train_set_pulse : public SpellScriptLoader { public: @@ -3190,6 +4310,11 @@ class spell_item_taunt_flag_targeting : public SpellScriptLoader void FilterTargets(std::list<WorldObject*>& targets) { + targets.remove_if([](WorldObject* obj) -> bool + { + return obj->GetTypeId() != TYPEID_PLAYER && obj->GetTypeId() != TYPEID_CORPSE; + }); + if (targets.empty()) { FinishCast(SPELL_FAILED_NO_VALID_TARGETS); @@ -3221,6 +4346,182 @@ 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, + SPELL_RESTLESS_STRENGTH_AURA_STACK = 24662 +}; + +// Item - 19950: Zandalarian Hero Charm +// 24658 - Unstable Power + +// Item - 19949: Zandalarian Hero Medallion +// 24661 - Restless Strength +class spell_item_zandalarian_charm : public SpellScriptLoader +{ + public: + spell_item_zandalarian_charm(char const* ScriptName, uint32 SpellId) : SpellScriptLoader(ScriptName), _spellId(SpellId) { } + + class spell_item_zandalarian_charm_AuraScript : public AuraScript + { + friend class spell_item_zandalarian_charm; + spell_item_zandalarian_charm_AuraScript(uint32 SpellId) : AuraScript(), _spellId(SpellId) { } + + PrepareAuraScript(spell_item_zandalarian_charm_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(_spellId)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + if (spellInfo->Id != m_scriptSpellId) + return true; + + return false; + } + + void HandleStackDrop(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->RemoveAuraFromStack(_spellId); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_item_zandalarian_charm_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_item_zandalarian_charm_AuraScript::HandleStackDrop, EFFECT_0, SPELL_AURA_DUMMY); + } + + uint32 _spellId; + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_zandalarian_charm_AuraScript(_spellId); + } + + private: + uint32 _spellId; +}; + void AddSC_item_spell_scripts() { // 23074 Arcanite Dragonling @@ -3234,22 +4535,42 @@ void AddSC_item_spell_scripts() new spell_item_aegis_of_preservation(); new spell_item_arcane_shroud(); + new spell_item_alchemists_stone(); + new spell_item_anger_capacitor<8>("spell_item_tiny_abomination_in_a_jar"); + new spell_item_anger_capacitor<7>("spell_item_tiny_abomination_in_a_jar_hero"); + new spell_item_argent_dawn_commission(); + new spell_item_aura_of_madness(); + new spell_item_dementia(); new spell_item_blessing_of_ancient_kings(); + new spell_item_deadly_precision(); + new spell_item_deadly_precision_dummy(); + new spell_item_deathbringers_will<SPELL_STRENGTH_OF_THE_TAUNKA, SPELL_AGILITY_OF_THE_VRYKUL, SPELL_POWER_OF_THE_TAUNKA, SPELL_AIM_OF_THE_IRON_DWARVES, SPELL_SPEED_OF_THE_VRYKUL>("spell_item_deathbringers_will_normal"); + new spell_item_deathbringers_will<SPELL_STRENGTH_OF_THE_TAUNKA_HERO, SPELL_AGILITY_OF_THE_VRYKUL_HERO, SPELL_POWER_OF_THE_TAUNKA_HERO, SPELL_AIM_OF_THE_IRON_DWARVES_HERO, SPELL_SPEED_OF_THE_VRYKUL_HERO>("spell_item_deathbringers_will_heroic"); new spell_item_decahedral_dwarven_dice(); new spell_item_defibrillate("spell_item_goblin_jumper_cables", 67, SPELL_GOBLIN_JUMPER_CABLES_FAIL); new spell_item_defibrillate("spell_item_goblin_jumper_cables_xl", 50, SPELL_GOBLIN_JUMPER_CABLES_XL_FAIL); new spell_item_defibrillate("spell_item_gnomish_army_knife", 33); new spell_item_desperate_defense(); new spell_item_deviate_fish(); + new spell_item_discerning_eye_beast_dummy(); new spell_item_echoes_of_light(); new spell_item_fate_rune_of_unsurpassed_vigor(); new spell_item_flask_of_the_north(); + new spell_item_frozen_shadoweave(); new spell_item_gnomish_death_ray(); + new spell_item_healing_touch_refund(); + new spell_item_heartpierce<SPELL_INVIGORATION_ENERGY, SPELL_INVIGORATION_MANA, SPELL_INVIGORATION_RAGE, SPELL_INVIGORATION_RP>("spell_item_heartpierce"); + new spell_item_heartpierce<SPELL_INVIGORATION_ENERGY_HERO, SPELL_INVIGORATION_MANA_HERO, SPELL_INVIGORATION_RAGE_HERO, SPELL_INVIGORATION_RP_HERO>("spell_item_heartpierce_hero"); + new spell_item_crystal_spire_of_karabor(); new spell_item_make_a_wish(); + new spell_item_mark_of_conquest(); new spell_item_mingos_fortune_generator(); new spell_item_necrotic_touch(); new spell_item_net_o_matic(); new spell_item_noggenfogger_elixir(); + new spell_item_pendant_of_the_violet_eye(); + new spell_item_persistent_shield(); + new spell_item_pet_healing(); new spell_item_piccolo_of_the_flaming_fire(); new spell_item_savory_deviate_delight(); new spell_item_scroll_of_recall(); @@ -3258,6 +4579,8 @@ void AddSC_item_spell_scripts() new spell_item_shadowmourne(); new spell_item_shadowmourne_soul_fragment(); new spell_item_six_demon_bag(); + new spell_item_swift_hand_justice_dummy(); + new spell_item_totem_of_flowing_water(); new spell_item_the_eye_of_diminution(); new spell_item_underbelly_elixir(); new spell_item_worn_troll_dice(); @@ -3291,7 +4614,13 @@ void AddSC_item_spell_scripts() new spell_item_chicken_cover(); new spell_item_muisek_vessel(); new spell_item_greatmothers_soulcatcher(); + new spell_item_shard_of_the_scale<SPELL_PURIFIED_CAUTERIZING_HEAL, SPELL_PURIFIED_SEARING_FLAMES>("spell_item_purified_shard_of_the_scale"); + new spell_item_shard_of_the_scale<SPELL_SHINY_CAUTERIZING_HEAL, SPELL_SHINY_SEARING_FLAMES>("spell_item_shiny_shard_of_the_scale"); new spell_item_soul_preserver(); + new spell_item_sunwell_neck<SPELL_LIGHTS_WRATH, SPELL_ARCANE_BOLT>("spell_item_sunwell_exalted_caster_neck"); + new spell_item_sunwell_neck<SPELL_LIGHTS_STRENGTH, SPELL_ARCANE_STRIKE>("spell_item_sunwell_exalted_melee_neck"); + new spell_item_sunwell_neck<SPELL_LIGHTS_WARD, SPELL_ARCANE_INSIGHT>("spell_item_sunwell_exalted_tank_neck"); + new spell_item_sunwell_neck<SPELL_LIGHTS_SALVATION, SPELL_ARCANE_SURGE>("spell_item_sunwell_exalted_healer_neck"); new spell_item_toy_train_set_pulse(); new spell_item_death_choice(); new spell_item_trinket_stack("spell_item_lightning_capacitor", SPELL_LIGHTNING_CAPACITOR_STACK, SPELL_LIGHTNING_CAPACITOR_TRIGGER); @@ -3302,4 +4631,9 @@ 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_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index bacbe31630c..5c496024599 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -50,6 +50,20 @@ enum MageSpells SPELL_MAGE_SUMMON_WATER_ELEMENTAL_PERMANENT = 70908, SPELL_MAGE_SUMMON_WATER_ELEMENTAL_TEMPORARY = 70907, SPELL_MAGE_GLYPH_OF_BLAST_WAVE = 62126, + SPELL_MAGE_CHILLED = 12484, + SPELL_MAGE_MANA_SURGE = 37445, + SPELL_MAGE_MAGIC_ABSORPTION_MANA = 29442, + SPELL_MAGE_ARCANE_POTENCY_RANK_1 = 57529, + SPELL_MAGE_ARCANE_POTENCY_RANK_2 = 57531, + SPELL_MAGE_HOT_STREAK_PROC = 48108, + SPELL_MAGE_ARCANE_SURGE = 37436, + SPELL_MAGE_COMBUSTION_PROC = 28682, + SPELL_MAGE_EMPOWERED_FIRE_PROC = 67545, + SPELL_MAGE_T10_2P_BONUS = 70752, + SPELL_MAGE_T10_2P_BONUS_EFFECT = 70753, + SPELL_MAGE_T8_4P_BONUS = 64869, + SPELL_MAGE_MISSILE_BARRAGE = 44401, + SPELL_MAGE_FINGERS_OF_FROST_AURASTATE_AURA = 44544 }; enum MageSpellIcons @@ -82,6 +96,46 @@ class spell_mage_incanters_absorbtion_base_AuraScript : public AuraScript } }; +// -31571 - Arcane Potency +class spell_mage_arcane_potency : public SpellScriptLoader +{ + public: + spell_mage_arcane_potency() : SpellScriptLoader("spell_mage_arcane_potency") { } + + class spell_mage_arcane_potency_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_arcane_potency_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_ARCANE_POTENCY_RANK_1) || + !sSpellMgr->GetSpellInfo(SPELL_MAGE_ARCANE_POTENCY_RANK_2)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + static uint32 const triggerSpell[2] = { SPELL_MAGE_ARCANE_POTENCY_RANK_1, SPELL_MAGE_ARCANE_POTENCY_RANK_2 }; + + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + uint32 spellId = triggerSpell[GetSpellInfo()->GetRank() - 1]; + caster->CastSpell(caster, spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_mage_arcane_potency_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_arcane_potency_AuraScript(); + } +}; + // -11113 - Blast Wave class spell_mage_blast_wave : public SpellScriptLoader { @@ -153,6 +207,38 @@ public: } }; +// -54747 - Burning Determination +// 54748 - Burning Determination +class spell_mage_burning_determination : public SpellScriptLoader +{ + public: + spell_mage_burning_determination() : SpellScriptLoader("spell_mage_burning_determination") { } + + class spell_mage_burning_determination_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_burning_determination_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + if (spellInfo->GetAllEffectsMechanicMask() & ((1 << MECHANIC_INTERRUPT) | (1 << MECHANIC_SILENCE))) + return true; + + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_mage_burning_determination_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_burning_determination_AuraScript(); + } +}; + // -44449 - Burnout class spell_mage_burnout : public SpellScriptLoader { @@ -172,17 +258,21 @@ class spell_mage_burnout : public SpellScriptLoader bool CheckProc(ProcEventInfo& eventInfo) { - return eventInfo.GetDamageInfo()->GetSpellInfo() != nullptr; + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetSpellInfo()) + return false; + + return true; } void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - int32 mana = int32(eventInfo.GetDamageInfo()->GetSpellInfo()->CalcPowerCost(GetTarget(), eventInfo.GetDamageInfo()->GetSchoolMask())); + int32 mana = eventInfo.GetDamageInfo()->GetSpellInfo()->CalcPowerCost(GetTarget(), eventInfo.GetDamageInfo()->GetSchoolMask()); mana = CalculatePct(mana, aurEff->GetAmount()); - GetTarget()->CastCustomSpell(SPELL_MAGE_BURNOUT, SPELLVALUE_BASE_POINT0, mana, GetTarget(), true, NULL, aurEff); + GetTarget()->CastCustomSpell(SPELL_MAGE_BURNOUT, SPELLVALUE_BASE_POINT0, mana, GetTarget(), true, nullptr, aurEff); } void Register() override @@ -235,6 +325,208 @@ class spell_mage_cold_snap : public SpellScriptLoader } }; +// 11129 - Combustion +class spell_mage_combustion : public SpellScriptLoader +{ + public: + spell_mage_combustion() : SpellScriptLoader("spell_mage_combustion") { } + + class spell_mage_combustion_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_combustion_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_COMBUSTION_PROC)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + // Do not take charges, add a stack of crit buff + if (!(eventInfo.GetHitMask() & PROC_HIT_CRITICAL)) + { + eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_MAGE_COMBUSTION_PROC, true); + return false; + } + + return true; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_mage_combustion_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_combustion_AuraScript(); + } +}; + +// -11185 - Improved Blizzard +class spell_mage_imp_blizzard : public SpellScriptLoader +{ + public: + spell_mage_imp_blizzard() : SpellScriptLoader("spell_mage_imp_blizzard") { } + + class spell_mage_imp_blizzard_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_imp_blizzard_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_CHILLED)) + return false; + return true; + } + + void HandleChill(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + uint32 triggerSpellId = sSpellMgr->GetSpellWithRank(SPELL_MAGE_CHILLED, GetSpellInfo()->GetRank()); + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), triggerSpellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_mage_imp_blizzard_AuraScript::HandleChill, EFFECT_0, SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_imp_blizzard_AuraScript(); + } +}; + +// 37447 - Improved Mana Gems +// 61062 - Improved Mana Gems +class spell_mage_imp_mana_gems : public SpellScriptLoader +{ + public: + spell_mage_imp_mana_gems() : SpellScriptLoader("spell_mage_imp_mana_gems") { } + + class spell_mage_imp_mana_gems_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_imp_mana_gems_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_MANA_SURGE)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_MAGE_MANA_SURGE, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_mage_imp_mana_gems_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_imp_mana_gems_AuraScript(); + } +}; + +// -31656 - Empowered Fire +class spell_mage_empowered_fire : public SpellScriptLoader +{ + public: + spell_mage_empowered_fire() : SpellScriptLoader("spell_mage_empowered_fire") { } + + class spell_mage_empowered_fire_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_empowered_fire_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_EMPOWERED_FIRE_PROC)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + if (spellInfo->Id == SPELL_MAGE_IGNITE) + return true; + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + + Unit* target = GetTarget(); + int32 bp0 = int32(CalculatePct(target->GetCreateMana(), aurEff->GetAmount())); + target->CastCustomSpell(SPELL_MAGE_EMPOWERED_FIRE_PROC, SPELLVALUE_BASE_POINT0, bp0, target, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_mage_empowered_fire_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_mage_empowered_fire_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_ADD_FLAT_MODIFIER); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_empowered_fire_AuraScript(); + } +}; + +// 74396 - Fingers of Frost +class spell_mage_fingers_of_frost : public SpellScriptLoader +{ + public: + spell_mage_fingers_of_frost() : SpellScriptLoader("spell_mage_fingers_of_frost") { } + + class spell_mage_fingers_of_frost_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_fingers_of_frost_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_FINGERS_OF_FROST_AURASTATE_AURA)) + return false; + return true; + } + + void HandleDummy(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->RemoveAuraFromStack(GetId()); + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->RemoveAurasDueToSpell(SPELL_MAGE_FINGERS_OF_FROST_AURASTATE_AURA); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_mage_fingers_of_frost_AuraScript::HandleDummy, EFFECT_0, SPELL_AURA_DUMMY); + AfterEffectRemove += AuraEffectRemoveFn(spell_mage_fingers_of_frost_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_fingers_of_frost_AuraScript(); + } +}; + // -543 - Fire Ward // -6143 - Frost Ward class spell_mage_fire_frost_ward : public SpellScriptLoader @@ -354,6 +646,222 @@ class spell_mage_focus_magic : public SpellScriptLoader } }; +// 44401 - Missile Barrage +// 48108 - Hot Streak +// 57761 - Fireball! +class spell_mage_gen_extra_effects : public SpellScriptLoader +{ + public: + spell_mage_gen_extra_effects() : SpellScriptLoader("spell_mage_gen_extra_effects") { } + + class spell_mage_gen_extra_effects_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_gen_extra_effects_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_T10_2P_BONUS) || + !sSpellMgr->GetSpellInfo(SPELL_MAGE_T10_2P_BONUS_EFFECT) || + !sSpellMgr->GetSpellInfo(SPELL_MAGE_T8_4P_BONUS)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + Unit* caster = eventInfo.GetActor(); + // Prevent double proc for Arcane missiles + if (caster == eventInfo.GetProcTarget()) + return false; + + // Proc chance is unknown, we'll just use dummy aura amount + if (AuraEffect const* aurEff = caster->GetAuraEffect(SPELL_MAGE_T8_4P_BONUS, EFFECT_0)) + if (roll_chance_i(aurEff->GetAmount())) + return false; + + return true; + } + + void HandleProc(ProcEventInfo& eventInfo) + { + Unit* caster = eventInfo.GetActor(); + + if (caster->HasAura(SPELL_MAGE_T10_2P_BONUS)) + caster->CastSpell((Unit*)nullptr, SPELL_MAGE_T10_2P_BONUS_EFFECT, true); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_mage_gen_extra_effects_AuraScript::CheckProc); + OnProc += AuraProcFn(spell_mage_gen_extra_effects_AuraScript::HandleProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_gen_extra_effects_AuraScript(); + } +}; + +// 56375 - Glyph of Polymorph +class spell_mage_glyph_of_polymorph : public SpellScriptLoader +{ + public: + spell_mage_glyph_of_polymorph() : SpellScriptLoader("spell_mage_glyph_of_polymorph") { } + + class spell_mage_glyph_of_polymorph_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_glyph_of_polymorph_AuraScript); + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* target = eventInfo.GetProcTarget(); + target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE, ObjectGuid::Empty, target->GetAura(32409)); // SW:D shall not be removed. + target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); + target->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_mage_glyph_of_polymorph_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_glyph_of_polymorph_AuraScript(); + } +}; + +// 56374 - Glyph of Icy Veins +class spell_mage_glyph_of_icy_veins : public SpellScriptLoader +{ + public: + spell_mage_glyph_of_icy_veins() : SpellScriptLoader("spell_mage_glyph_of_icy_veins") { } + + class spell_mage_glyph_of_icy_veins_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_glyph_of_icy_veins_AuraScript); + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + caster->RemoveAurasByType(SPELL_AURA_HASTE_SPELLS, ObjectGuid::Empty, 0, true, false); + caster->RemoveAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_mage_glyph_of_icy_veins_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_glyph_of_icy_veins_AuraScript(); + } +}; + +// 56372 - Glyph of Ice Block +class spell_mage_glyph_of_ice_block : public SpellScriptLoader +{ + public: + spell_mage_glyph_of_ice_block() : SpellScriptLoader("spell_mage_glyph_of_ice_block") { } + + class spell_mage_glyph_of_ice_block_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_glyph_of_ice_block_AuraScript); + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + caster->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool + { + SpellInfo const* cdSpell = sSpellMgr->GetSpellInfo(itr->first); + if (!cdSpell || cdSpell->SpellFamilyName != SPELLFAMILY_MAGE + || !(cdSpell->SpellFamilyFlags[0] & 0x00000040)) + return false; + return true; + }, true); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_mage_glyph_of_ice_block_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_glyph_of_ice_block_AuraScript(); + } +}; + +// -44445 - Hot Streak +class spell_mage_hot_streak : public SpellScriptLoader +{ + public: + spell_mage_hot_streak() : SpellScriptLoader("spell_mage_hot_streak") { } + + class spell_mage_hot_streak_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_hot_streak_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_HOT_STREAK_PROC)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + AuraEffect* counter = GetEffect(EFFECT_1); + if (!counter) + return; + + // Count spell criticals in a row in second aura + if (eventInfo.GetHitMask() & PROC_HIT_CRITICAL) + { + counter->SetAmount(counter->GetAmount() * 2); + if (counter->GetAmount() < 100) // not enough + return; + + // roll chance + if (!roll_chance_i(aurEff->GetAmount())) + return; + + Unit* caster = eventInfo.GetActor(); + caster->CastSpell(caster, SPELL_MAGE_HOT_STREAK_PROC, true, nullptr, aurEff); + } + + // reset counter + counter->SetAmount(25); + } + + void HandleDummy(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + // Prevent console spam + PreventDefaultAction(); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_mage_hot_streak_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + OnEffectProc += AuraEffectProcFn(spell_mage_hot_streak_AuraScript::HandleDummy, EFFECT_1, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_hot_streak_AuraScript(); + } +}; + // -11426 - Ice Barrier class spell_mage_ice_barrier : public SpellScriptLoader { @@ -435,7 +943,7 @@ class spell_mage_ignite : public SpellScriptLoader bool CheckProc(ProcEventInfo& eventInfo) { - return eventInfo.GetProcTarget() != nullptr; + return eventInfo.GetDamageInfo() && eventInfo.GetProcTarget(); } void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) @@ -447,7 +955,7 @@ class spell_mage_ignite : public SpellScriptLoader int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), pct) / igniteDot->GetMaxTicks()); amount += eventInfo.GetProcTarget()->GetRemainingPeriodicAmount(eventInfo.GetActor()->GetGUID(), SPELL_MAGE_IGNITE, SPELL_AURA_PERIODIC_DAMAGE); - GetTarget()->CastCustomSpell(SPELL_MAGE_IGNITE, SPELLVALUE_BASE_POINT0, amount, eventInfo.GetProcTarget(), true, NULL, aurEff); + GetTarget()->CastCustomSpell(SPELL_MAGE_IGNITE, SPELLVALUE_BASE_POINT0, amount, eventInfo.GetProcTarget(), true, nullptr, aurEff); } void Register() override @@ -502,6 +1010,43 @@ class spell_mage_living_bomb : public SpellScriptLoader } }; +// -29441 - Magic Absorption +class spell_mage_magic_absorption : public SpellScriptLoader +{ + public: + spell_mage_magic_absorption() : SpellScriptLoader("spell_mage_magic_absorption") { } + + class spell_mage_magic_absorption_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_magic_absorption_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_MAGIC_ABSORPTION_MANA)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActionTarget(); + int32 bp = CalculatePct(static_cast<int32>(caster->GetMaxPower(POWER_MANA)), aurEff->GetAmount()); + caster->CastCustomSpell(SPELL_MAGE_MAGIC_ABSORPTION_MANA, SPELLVALUE_BASE_POINT0, bp, caster, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_mage_magic_absorption_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_magic_absorption_AuraScript(); + } +}; + // -1463 - Mana Shield class spell_mage_mana_shield : public SpellScriptLoader { @@ -512,6 +1057,14 @@ class spell_mage_mana_shield : public SpellScriptLoader { PrepareAuraScript(spell_mage_mana_shield_AuraScript); + bool Validate(SpellInfo const* spellInfo) override + { + if (!spell_mage_incanters_absorbtion_base_AuraScript::Validate(spellInfo) || + !sSpellMgr->GetSpellInfo(SPELL_MAGE_ARCANE_SURGE)) + return false; + return true; + } + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated) { canBeRecalculated = false; @@ -527,10 +1080,18 @@ class spell_mage_mana_shield : public SpellScriptLoader } } + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + Unit* caster = eventInfo.GetActionTarget(); + caster->CastSpell(caster, SPELL_MAGE_ARCANE_SURGE, true, nullptr, aurEff); + } + void Register() override { DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_mage_mana_shield_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MANA_SHIELD); AfterEffectManaShield += AuraEffectManaShieldFn(spell_mage_mana_shield_AuraScript::Trigger, EFFECT_0); + + OnEffectProc += AuraEffectProcFn(spell_mage_mana_shield_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_MANA_SHIELD); } }; @@ -559,18 +1120,22 @@ class spell_mage_master_of_elements : public SpellScriptLoader bool CheckProc(ProcEventInfo& eventInfo) { - return eventInfo.GetDamageInfo()->GetSpellInfo() != nullptr; + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetSpellInfo()) + return false; + + return true; } void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - int32 mana = int32(eventInfo.GetDamageInfo()->GetSpellInfo()->CalcPowerCost(GetTarget(), eventInfo.GetDamageInfo()->GetSchoolMask())); + int32 mana = eventInfo.GetDamageInfo()->GetSpellInfo()->CalcPowerCost(GetTarget(), eventInfo.GetDamageInfo()->GetSchoolMask()); mana = CalculatePct(mana, aurEff->GetAmount()); if (mana > 0) - GetTarget()->CastCustomSpell(SPELL_MAGE_MASTER_OF_ELEMENTS_ENERGIZE, SPELLVALUE_BASE_POINT0, mana, GetTarget(), true, NULL, aurEff); + GetTarget()->CastCustomSpell(SPELL_MAGE_MASTER_OF_ELEMENTS_ENERGIZE, SPELLVALUE_BASE_POINT0, mana, GetTarget(), true, nullptr, aurEff); } void Register() override @@ -586,6 +1151,42 @@ class spell_mage_master_of_elements : public SpellScriptLoader } }; +// -44404 - Missile Barrage +class spell_mage_missile_barrage : public SpellScriptLoader +{ + public: + spell_mage_missile_barrage() : SpellScriptLoader("spell_mage_missile_barrage") { } + + class spell_mage_missile_barrage_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_missile_barrage_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return false; + + // Arcane Blast - full chance + if (spellInfo->SpellFamilyFlags[0] & 0x20000000) + return true; + + // Rest of spells have half chance + return roll_chance_i(50); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_mage_missile_barrage_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_missile_barrage_AuraScript(); + } +}; + enum SilvermoonPolymorph { NPC_AUROSALIA = 18744, @@ -683,17 +1284,31 @@ class spell_mage_summon_water_elemental : public SpellScriptLoader void AddSC_mage_spell_scripts() { + new spell_mage_arcane_potency(); new spell_mage_blast_wave(); new spell_mage_blazing_speed(); + new spell_mage_burning_determination(); new spell_mage_burnout(); new spell_mage_cold_snap(); + new spell_mage_combustion(); + new spell_mage_imp_blizzard(); + new spell_mage_imp_mana_gems(); + new spell_mage_empowered_fire(); + new spell_mage_fingers_of_frost(); new spell_mage_fire_frost_ward(); new spell_mage_focus_magic(); + new spell_mage_gen_extra_effects(); + new spell_mage_glyph_of_polymorph(); + new spell_mage_glyph_of_icy_veins(); + new spell_mage_glyph_of_ice_block(); + new spell_mage_hot_streak(); new spell_mage_ice_barrier(); new spell_mage_ignite(); new spell_mage_living_bomb(); + new spell_mage_magic_absorption(); new spell_mage_mana_shield(); new spell_mage_master_of_elements(); + new spell_mage_missile_barrage(); new spell_mage_polymorph_cast_visual(); new spell_mage_summon_water_elemental(); } diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index 6de95af8d8f..693990edd0a 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -70,6 +70,9 @@ enum PaladinSpells SPELL_PALADIN_JUDGEMENT_OF_LIGHT = 20185, SPELL_PALADIN_JUDGEMENT_OF_WISDOM = 20186, + SPELL_PALADIN_JUDGEMENT_OF_LIGHT_HEAL = 20267, + SPELL_PALADIN_JUDGEMENT_OF_WISDOM_MANA = 20268, + SPELL_PALADIN_GLYPH_OF_SALVATION = 63225, SPELL_PALADIN_RIGHTEOUS_DEFENSE_TAUNT = 31790, @@ -89,7 +92,41 @@ enum PaladinSpells SPELL_PALADIN_AURA_MASTERY_IMMUNE = 64364, SPELL_GENERIC_ARENA_DAMPENING = 74410, - SPELL_GENERIC_BATTLEGROUND_DAMPENING = 74411 + SPELL_GENERIC_BATTLEGROUND_DAMPENING = 74411, + + SPELL_PALADIN_SACRED_SHIELD = 53601, + SPELL_PALADIN_T9_HOLY_4P_BONUS = 67191, + SPELL_PALADIN_FLASH_OF_LIGHT_PROC = 66922, + + SPELL_PALADIN_JUDGEMENTS_OF_THE_JUST_PROC = 68055, + + SPELL_PALADIN_GLYPH_OF_DIVINITY_PROC = 54986, + + SPELL_PALADIN_JUDGEMENTS_OF_THE_WISE_MANA = 31930, + SPELL_REPLENISHMENT = 57669, + SPELL_PALADIN_RIGHTEOUS_VENGEANCE_DAMAGE = 61840, + SPELL_PALADIN_SHEATH_OF_LIGHT_HEAL = 54203, + SPELL_PALADIN_SACRED_SHIELD_TRIGGER = 58597, + SPELL_PALADIN_T8_HOLY_4P_BONUS = 64895, + SPELL_PALADIN_HEART_OF_THE_CRUSADER_EFF_R1 = 21183, + + SPELL_PALADIN_HOLY_POWER_ARMOR = 28790, + SPELL_PALADIN_HOLY_POWER_ATTACK_POWER = 28791, + SPELL_PALADIN_HOLY_POWER_SPELL_POWER = 28793, + SPELL_PALADIN_HOLY_POWER_MP5 = 28795, + + SPELL_PALADIN_HOLY_VENGEANCE = 31803, + SPELL_PALADIN_SEAL_OF_VENGEANCE_DAMAGE = 42463, + SPELL_PALADIN_BLOOD_CORRUPTION = 53742, + SPELL_PALADIN_SEAL_OF_CORRUPTION_DAMAGE = 53739, + + SPELL_PALADIN_SPIRITUAL_ATTUNEMENT_MANA = 31786, + + SPELL_PALADIN_ENDURING_LIGHT = 40471, + SPELL_PALADIN_ENDURING_JUDGEMENT = 40472, + + SPELL_PALADIN_GLYPH_OF_HOLY_LIGHT_HEAL = 54968, + SPELL_PALADIN_HOLY_MENDING = 64891 }; enum PaladinSpellIcons @@ -119,9 +156,16 @@ class spell_pal_ardent_defender : public SpellScriptLoader enum Spell { - PAL_SPELL_ARDENT_DEFENDER_HEAL = 66235, + PAL_SPELL_ARDENT_DEFENDER_HEAL = 66235 }; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(PAL_SPELL_ARDENT_DEFENDER_HEAL)) + return false; + return true; + } + bool Load() override { healPct = GetSpellInfo()->Effects[EFFECT_1].CalcValue(); @@ -155,7 +199,7 @@ class spell_pal_ardent_defender : public SpellScriptLoader : float(defenseSkillValue) / float(reqDefForMaxHeal); int32 healAmount = int32(victim->CountPctFromMaxHealth(uint32(healPct * pctFromDefense))); - victim->CastCustomSpell(victim, PAL_SPELL_ARDENT_DEFENDER_HEAL, &healAmount, NULL, NULL, true, NULL, aurEff); + victim->CastCustomSpell(PAL_SPELL_ARDENT_DEFENDER_HEAL, SPELLVALUE_BASE_POINT0, healAmount, victim, true, nullptr, aurEff); victim->GetSpellHistory()->AddCooldown(PAL_SPELL_ARDENT_DEFENDER_HEAL, 0, std::chrono::minutes(2)); } else if (remainingHealth < int32(allowedHealth)) @@ -416,6 +460,37 @@ class spell_pal_blessing_of_sanctuary : public SpellScriptLoader } }; +// -31871 - Divine Purpose +class spell_pal_divine_purpose : public SpellScriptLoader +{ + public: + spell_pal_divine_purpose() : SpellScriptLoader("spell_pal_divine_purpose") { } + + class spell_pal_divine_purpose_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_divine_purpose_AuraScript); + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + if (!roll_chance_i(aurEff->GetAmount())) + return; + + eventInfo.GetProcTarget()->RemoveAurasWithMechanic(1 << MECHANIC_STUN, AURA_REMOVE_BY_ENEMY_SPELL); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_divine_purpose_AuraScript::HandleProc, EFFECT_2, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_divine_purpose_AuraScript(); + } +}; + // 64205 - Divine Sacrifice class spell_pal_divine_sacrifice : public SpellScriptLoader { @@ -630,9 +705,13 @@ class spell_pal_eye_for_an_eye : public SpellScriptLoader void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + // return damage % to attacker but < 50% own total health - int32 damage = int32(std::min(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount()), GetTarget()->GetMaxHealth() / 2)); - GetTarget()->CastCustomSpell(SPELL_PALADIN_EYE_FOR_AN_EYE_DAMAGE, SPELLVALUE_BASE_POINT0, damage, eventInfo.GetProcTarget(), true, NULL, aurEff); + int32 damage = std::min(CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()), static_cast<int32>(GetTarget()->GetMaxHealth()) / 2); + GetTarget()->CastCustomSpell(SPELL_PALADIN_EYE_FOR_AN_EYE_DAMAGE, SPELLVALUE_BASE_POINT0, damage, eventInfo.GetProcTarget(), true, nullptr, aurEff); } void Register() override @@ -647,6 +726,50 @@ class spell_pal_eye_for_an_eye : public SpellScriptLoader } }; +// 54939 - Glyph of Divinity +class spell_pal_glyph_of_divinity : public SpellScriptLoader +{ + public: + spell_pal_glyph_of_divinity() : SpellScriptLoader("spell_pal_glyph_of_divinity") { } + + class spell_pal_glyph_of_divinity_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_glyph_of_divinity_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_GLYPH_OF_DIVINITY_PROC)) + return false; + return true; + } + + void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + // Lay on Hands (Rank 1) does not have mana effect + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo || spellInfo->Effects[EFFECT_1].Effect != SPELL_EFFECT_ENERGIZE) + return; + + Unit* caster = eventInfo.GetActor(); + if (caster == eventInfo.GetProcTarget()) + return; + + int32 mana = spellInfo->Effects[EFFECT_1].CalcValue() * 2; + caster->CastCustomSpell(SPELL_PALADIN_GLYPH_OF_DIVINITY_PROC, SPELLVALUE_BASE_POINT1, mana, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_glyph_of_divinity_AuraScript::OnProc, EFFECT_0, SPELL_AURA_ADD_PCT_MODIFIER); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_glyph_of_divinity_AuraScript(); + } +}; + // 54968 - Glyph of Holy Light class spell_pal_glyph_of_holy_light : public SpellScriptLoader { @@ -680,6 +803,49 @@ class spell_pal_glyph_of_holy_light : public SpellScriptLoader } }; +// 54937 - Glyph of Holy Light (dummy aura) +class spell_pal_glyph_of_holy_light_dummy : public SpellScriptLoader +{ + public: + spell_pal_glyph_of_holy_light_dummy() : SpellScriptLoader("spell_pal_glyph_of_holy_light_dummy") { } + + class spell_pal_glyph_of_holy_light_dummy_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_glyph_of_holy_light_dummy_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_GLYPH_OF_HOLY_LIGHT_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + int32 amount = CalculatePct(static_cast<int32>(healInfo->GetHeal()), aurEff->GetAmount()); + + caster->CastCustomSpell(SPELL_PALADIN_GLYPH_OF_HOLY_LIGHT_HEAL, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_glyph_of_holy_light_dummy_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_glyph_of_holy_light_dummy_AuraScript(); + } +}; + // 63521 - Guarded by The Light class spell_pal_guarded_by_the_light : public SpellScriptLoader { @@ -800,6 +966,43 @@ class spell_pal_hand_of_salvation : public SpellScriptLoader } }; +// -20335 - Heart of the Crusader +class spell_pal_heart_of_the_crusader : public SpellScriptLoader +{ + public: + spell_pal_heart_of_the_crusader() : SpellScriptLoader("spell_pal_heart_of_the_crusader") { } + + class spell_pal_heart_of_the_crusader_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_heart_of_the_crusader_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_HEART_OF_THE_CRUSADER_EFF_R1)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + uint32 spellId = sSpellMgr->GetSpellWithRank(SPELL_PALADIN_HEART_OF_THE_CRUSADER_EFF_R1, GetSpellInfo()->GetRank()); + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_heart_of_the_crusader_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_heart_of_the_crusader_AuraScript(); + } +}; + // -20473 - Holy Shock class spell_pal_holy_shock : public SpellScriptLoader { @@ -992,7 +1195,7 @@ class spell_pal_improved_aura : public SpellScriptLoader uint32 _spellId; }; -// 63510 - Improved Concentraction Aura (Area Aura) +// 63510 - Improved Concentration Aura (Area Aura) // 63514 - Improved Devotion Aura (Area Aura) // 63531 - Sanctified Retribution (Area Aura) class spell_pal_improved_aura_effect : public SpellScriptLoader @@ -1034,6 +1237,108 @@ class spell_pal_improved_aura_effect : public SpellScriptLoader } }; +// -20234 - Improved Lay on Hands +class spell_pal_improved_lay_of_hands : public SpellScriptLoader +{ + public: + spell_pal_improved_lay_of_hands() : SpellScriptLoader("spell_pal_improved_lay_of_hands") { } + + class spell_pal_improved_lay_of_hands_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_improved_lay_of_hands_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(); + eventInfo.GetActionTarget()->CastSpell(eventInfo.GetActionTarget(), GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, true, nullptr, aurEff, GetTarget()->GetGUID()); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_improved_lay_of_hands_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_improved_lay_of_hands_AuraScript(); + } +}; + +// -53569 - Infusion of Light +class spell_pal_infusion_of_light : public SpellScriptLoader +{ + public: + spell_pal_infusion_of_light() : SpellScriptLoader("spell_pal_infusion_of_light") { } + + class spell_pal_infusion_of_light_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_infusion_of_light_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_SACRED_SHIELD) || + !sSpellMgr->GetSpellInfo(SPELL_PALADIN_T9_HOLY_4P_BONUS) || + !sSpellMgr->GetSpellInfo(SPELL_PALADIN_FLASH_OF_LIGHT_PROC)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + { + // Flash of Light HoT on Flash of Light when Sacred Shield active + if (spellInfo->SpellFamilyFlags[0] & 0x40000000 && spellInfo->SpellIconID == 242) + { + PreventDefaultAction(); + + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + + Unit* procTarget = eventInfo.GetActionTarget(); + if (procTarget && procTarget->HasAura(SPELL_PALADIN_SACRED_SHIELD)) + { + 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 + if (AuraEffect const* aurEff = target->GetAuraEffect(SPELL_PALADIN_T9_HOLY_4P_BONUS, 0)) + AddPct(bp0, aurEff->GetAmount()); + + target->CastCustomSpell(SPELL_PALADIN_FLASH_OF_LIGHT_PROC, SPELLVALUE_BASE_POINT0, bp0, procTarget, true, nullptr, aurEff); + } + } + // but should not proc on non-critical Holy Shocks + else if ((spellInfo->SpellFamilyFlags[0] & 0x200000 || spellInfo->SpellFamilyFlags[1] & 0x10000) && !(eventInfo.GetHitMask() & PROC_HIT_CRITICAL)) + PreventDefaultAction(); + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_infusion_of_light_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_infusion_of_light_AuraScript(); + } +}; + // 37705 - Healing Discount class spell_pal_item_healing_discount : public SpellScriptLoader { @@ -1069,6 +1374,65 @@ class spell_pal_item_healing_discount : public SpellScriptLoader } }; +// 40470 - Paladin Tier 6 Trinket +class spell_pal_item_t6_trinket : public SpellScriptLoader +{ + public: + spell_pal_item_t6_trinket() : SpellScriptLoader("spell_pal_item_t6_trinket") { } + + class spell_pal_item_t6_trinket_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_item_t6_trinket_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_ENDURING_LIGHT) || + !sSpellMgr->GetSpellInfo(SPELL_PALADIN_ENDURING_JUDGEMENT)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; + + uint32 spellId; + int32 chance; + + // Holy Light & Flash of Light + if (spellInfo->SpellFamilyFlags[0] & 0xC0000000) + { + spellId = SPELL_PALADIN_ENDURING_LIGHT; + chance = 15; + } + // Judgements + else if (spellInfo->SpellFamilyFlags[0] & 0x00800000) + { + spellId = SPELL_PALADIN_ENDURING_JUDGEMENT; + chance = 50; + } + else + return; + + if (roll_chance_i(chance)) + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_item_t6_trinket_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_item_t6_trinket_AuraScript(); + } +}; + // 53407 - Judgement of Justice // 20271 - Judgement of Light // 53408 - Judgement of Wisdom @@ -1101,11 +1465,13 @@ class spell_pal_judgement : public SpellScriptLoader for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i) { if ((*i)->GetSpellInfo()->GetSpellSpecific() == SPELL_SPECIFIC_SEAL && (*i)->GetEffIndex() == EFFECT_2) + { if (sSpellMgr->GetSpellInfo((*i)->GetAmount())) { spellId2 = (*i)->GetAmount(); break; } + } } GetCaster()->CastSpell(GetHitUnit(), _spellId, true); @@ -1159,6 +1525,164 @@ class spell_pal_judgement_of_command : public SpellScriptLoader } }; +// 20185 - Judgement of Light +class spell_pal_judgement_of_light_heal : public SpellScriptLoader +{ + public: + spell_pal_judgement_of_light_heal() : SpellScriptLoader("spell_pal_judgement_of_light_heal") { } + + class spell_pal_judgement_of_light_heal_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_judgement_of_light_heal_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_JUDGEMENT_OF_LIGHT_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + 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, nullptr, aurEff, GetCasterGUID()); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_judgement_of_light_heal_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_judgement_of_light_heal_AuraScript(); + } +}; + +// 20186 - Judgement of Wisdom +class spell_pal_judgement_of_wisdom_mana : public SpellScriptLoader +{ + public: + spell_pal_judgement_of_wisdom_mana() : SpellScriptLoader("spell_pal_judgement_of_wisdom_mana") { } + + class spell_pal_judgement_of_wisdom_mana_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_judgement_of_wisdom_mana_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_JUDGEMENT_OF_WISDOM_MANA)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetProcTarget()->getPowerType() == POWER_MANA; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + 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, nullptr, aurEff, GetCasterGUID()); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_pal_judgement_of_wisdom_mana_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_pal_judgement_of_wisdom_mana_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_judgement_of_wisdom_mana_AuraScript(); + } +}; + +// -53695 - Judgements of the Just +class spell_pal_judgements_of_the_just : public SpellScriptLoader +{ + public: + spell_pal_judgements_of_the_just() : SpellScriptLoader("spell_pal_judgements_of_the_just") { } + + class spell_pal_judgements_of_the_just_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_judgements_of_the_just_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_JUDGEMENTS_OF_THE_JUST_PROC)) + return false; + return true; + } + + void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + GetTarget()->CastSpell(eventInfo.GetActionTarget(), SPELL_PALADIN_JUDGEMENTS_OF_THE_JUST_PROC, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_judgements_of_the_just_AuraScript::OnProc, EFFECT_0, SPELL_AURA_ADD_FLAT_MODIFIER); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_judgements_of_the_just_AuraScript(); + } +}; + +// -31876 - Judgements of the Wise +class spell_pal_judgements_of_the_wise : public SpellScriptLoader +{ + public: + spell_pal_judgements_of_the_wise() : SpellScriptLoader("spell_pal_judgements_of_the_wise") { } + + class spell_pal_judgements_of_the_wise_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_judgements_of_the_wise_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_REPLENISHMENT) || + !sSpellMgr->GetSpellInfo(SPELL_PALADIN_JUDGEMENTS_OF_THE_WISE_MANA)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + caster->CastSpell((Unit*)nullptr, SPELL_PALADIN_JUDGEMENTS_OF_THE_WISE_MANA, true, nullptr, aurEff); + caster->CastSpell((Unit*)nullptr, SPELL_REPLENISHMENT, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_judgements_of_the_wise_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_judgements_of_the_wise_AuraScript(); + } +}; + // -633 - Lay on Hands class spell_pal_lay_on_hands : public SpellScriptLoader { @@ -1251,8 +1775,12 @@ class spell_pal_light_s_beacon : public SpellScriptLoader if (!procSpell) return; + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + uint32 healSpellId = procSpell->IsRankOf(sSpellMgr->AssertSpellInfo(SPELL_PALADIN_HOLY_LIGHT)) ? SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_1 : SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_3; - uint32 heal = CalculatePct(eventInfo.GetHealInfo()->GetHeal(), aurEff->GetAmount()); + uint32 heal = CalculatePct(healInfo->GetHeal(), aurEff->GetAmount()); Unit* beaconTarget = GetCaster(); if (!beaconTarget || !beaconTarget->HasAura(SPELL_PALADIN_BEACON_OF_LIGHT, eventInfo.GetActor()->GetGUID())) @@ -1261,7 +1789,7 @@ class spell_pal_light_s_beacon : public SpellScriptLoader /// @todo: caster must be the healed unit to perform distance checks correctly /// but that will break animation on clientside /// caster in spell packets must be the healing unit - eventInfo.GetActor()->CastCustomSpell(healSpellId, SPELLVALUE_BASE_POINT0, heal, beaconTarget, true); + eventInfo.GetActor()->CastCustomSpell(healSpellId, SPELLVALUE_BASE_POINT0, heal, beaconTarget, true, nullptr, aurEff); } void Register() override @@ -1341,6 +1869,55 @@ class spell_pal_righteous_defense : public SpellScriptLoader } }; +// -53380 - Righteous Vengeance +class spell_pal_righteous_vengeance : public SpellScriptLoader +{ + public: + spell_pal_righteous_vengeance() : SpellScriptLoader("spell_pal_righteous_vengeance") { } + + class spell_pal_righteous_vengeance_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_righteous_vengeance_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_RIGHTEOUS_VENGEANCE_DAMAGE)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_PALADIN_RIGHTEOUS_VENGEANCE_DAMAGE); + int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + amount /= spellInfo->GetMaxTicks(); + // Add remaining ticks to damage done + amount += target->GetRemainingPeriodicAmount(caster->GetGUID(), SPELL_PALADIN_RIGHTEOUS_VENGEANCE_DAMAGE, SPELL_AURA_PERIODIC_DAMAGE); + + caster->CastCustomSpell(SPELL_PALADIN_RIGHTEOUS_VENGEANCE_DAMAGE, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_righteous_vengeance_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_righteous_vengeance_AuraScript(); + } +}; + // 58597 - Sacred Shield class spell_pal_sacred_shield : public SpellScriptLoader { @@ -1385,6 +1962,59 @@ class spell_pal_sacred_shield : public SpellScriptLoader } }; +// 53601 - Sacred Shield (dummy) +class spell_pal_sacred_shield_dummy : public SpellScriptLoader +{ + public: + spell_pal_sacred_shield_dummy() : SpellScriptLoader("spell_pal_sacred_shield_dummy") { } + + class spell_pal_sacred_shield_dummy_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_sacred_shield_dummy_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_SACRED_SHIELD_TRIGGER) || + !sSpellMgr->GetSpellInfo(SPELL_PALADIN_T8_HOLY_4P_BONUS)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = GetCaster(); + if (!caster) + return; + + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + if (_cooldownEnd > now) + return; + + Seconds cooldown(aurEff->GetAmount()); + if (AuraEffect const* bonus = caster->GetAuraEffect(SPELL_PALADIN_T8_HOLY_4P_BONUS, EFFECT_0, caster->GetGUID())) + cooldown = Seconds(bonus->GetAmount()); + + _cooldownEnd = now + cooldown; + caster->CastSpell(eventInfo.GetActionTarget(), SPELL_PALADIN_SACRED_SHIELD_TRIGGER, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_sacred_shield_dummy_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + + // Cooldown tracking can't be done in DB because of T8 bonus + std::chrono::steady_clock::time_point _cooldownEnd = std::chrono::steady_clock::time_point::min(); + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_sacred_shield_dummy_AuraScript(); + } +}; + // 20154, 21084 - Seal of Righteousness - melee proc dummy (addition ${$MWS*(0.022*$AP+0.044*$SPH)} damage) class spell_pal_seal_of_righteousness : public SpellScriptLoader { @@ -1431,6 +2061,342 @@ class spell_pal_seal_of_righteousness : public SpellScriptLoader } }; +// 31801 - Seal of Vengeance +// 53736 - Seal of Corruption +template <uint32 DoTSpellId, uint32 DamageSpellId> +class spell_pal_seal_of_vengeance : public SpellScriptLoader +{ + public: + spell_pal_seal_of_vengeance(char const* ScriptName) : SpellScriptLoader(ScriptName) { } + + template <uint32 DoTSpell, uint32 DamageSpell> + class spell_pal_seal_of_vengeance_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_seal_of_vengeance_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(DoTSpell) || + !sSpellMgr->GetSpellInfo(DamageSpell)) + return false; + return true; + } + + /* + When an auto-attack lands (does not dodge/parry/miss) that can proc a seal the of the following things happen independently of each other (see 2 roll system). + + 1) A "hidden strike" which uses melee combat mechanics occurs. If it lands it refreshes/stacks SoV DoT. Only white swings can trigger a refresh or stack. (This hidden strike mechanic can also proc things like berserking..) + 2) A weapon damage based proc will occur if you used a special (CS/DS/judge) or if you have a 5 stack (from auto attacks). This attack can not be avoided. + + Remember #2 happens regardless of #1 landing, it just requires the initial attack (autos, cs, etc) to land. + + Stack Number % of Weapon Damage % with SotP + 0 0% 0% + 1 6.6% 7.6% + 2 13.2% 15.2% + 3 19.8% 22.8% + 4 26.4% 30.4% + 5 33% 38% + */ + + void HandleApplyDoT(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + if (!(eventInfo.GetTypeMask() & PROC_FLAG_DONE_MELEE_AUTO_ATTACK)) + return; + + // don't cast triggered, spell already has SPELL_ATTR4_CAN_CAST_WHILE_CASTING attr + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), DoTSpell, TRIGGERED_DONT_RESET_PERIODIC_TIMER, nullptr, aurEff); + } + + void HandleSeal(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + + // get current aura on target, if any + AuraEffect const* sealDot = target->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_PALADIN, 0x00000000, 0x00000800, 0x00000000, caster->GetGUID()); + if (!sealDot) + return; + + uint8 const stacks = sealDot->GetBase()->GetStackAmount(); + uint8 const maxStacks = sealDot->GetSpellInfo()->StackAmount; + + if (stacks < maxStacks && !(eventInfo.GetTypeMask() & PROC_FLAG_DONE_SPELL_MELEE_DMG_CLASS)) + return; + + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(DamageSpell); + int32 amount = spellInfo->Effects[EFFECT_0].CalcValue(); + amount *= stacks; + amount /= maxStacks; + + caster->CastCustomSpell(DamageSpell, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_seal_of_vengeance_AuraScript::HandleApplyDoT, EFFECT_0, SPELL_AURA_DUMMY); + OnEffectProc += AuraEffectProcFn(spell_pal_seal_of_vengeance_AuraScript::HandleSeal, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_seal_of_vengeance_AuraScript<DoTSpellId, DamageSpellId>(); + } +}; + +// 20375 - Seal of Command +// 21084 - Seal of Righteousness +// 31801 - Seal of Vengeance +// 31892 - Seal of Blood +// 33127 - Seal of Command +// 38008 - Seal of Blood +// 41459 - Seal of Blood +// 53720 - Seal of the Martyr +// 53736 - Seal of Corruption +class spell_pal_seals : public SpellScriptLoader +{ + public: + spell_pal_seals() : SpellScriptLoader("spell_pal_seals") { } + + class spell_pal_seals_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_seals_AuraScript); + + // Effect 2 is used by Judgement code, we prevent the proc to avoid console logging of unknown spell trigger + bool CheckDummyProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + return false; + } + + void Register() override + { + DoCheckEffectProc += AuraCheckEffectProcFn(spell_pal_seals_AuraScript::CheckDummyProc, EFFECT_2, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_seals_AuraScript(); + } +}; + +// -31785 - Spiritual Attunement +class spell_pal_spiritual_attunement : public SpellScriptLoader +{ + public: + spell_pal_spiritual_attunement() : SpellScriptLoader("spell_pal_spiritual_attunement") { } + + class spell_pal_spiritual_attunement_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_spiritual_attunement_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_SPIRITUAL_ATTUNEMENT_MANA)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + // "when healed by other friendly targets' spells" + if (eventInfo.GetProcTarget() == eventInfo.GetActionTarget()) + return false; + + return eventInfo.GetHealInfo() && eventInfo.GetHealInfo()->GetEffectiveHeal(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + HealInfo* healInfo = eventInfo.GetHealInfo(); + int32 amount = CalculatePct(static_cast<int32>(healInfo->GetEffectiveHeal()), aurEff->GetAmount()); + + eventInfo.GetActionTarget()->CastCustomSpell(SPELL_PALADIN_SPIRITUAL_ATTUNEMENT_MANA, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_pal_spiritual_attunement_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_pal_spiritual_attunement_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_spiritual_attunement_AuraScript(); + } +}; + +// -53501 - Sheath of Light +class spell_pal_sheath_of_light : public SpellScriptLoader +{ + public: + spell_pal_sheath_of_light() : SpellScriptLoader("spell_pal_sheath_of_light") { } + + class spell_pal_sheath_of_light_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_sheath_of_light_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_SHEATH_OF_LIGHT_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_PALADIN_SHEATH_OF_LIGHT_HEAL); + int32 amount = CalculatePct(static_cast<int32>(healInfo->GetHeal()), aurEff->GetAmount()); + amount /= spellInfo->GetMaxTicks(); + // Add remaining ticks to healing done + amount += target->GetRemainingPeriodicAmount(caster->GetGUID(), SPELL_PALADIN_SHEATH_OF_LIGHT_HEAL, SPELL_AURA_PERIODIC_HEAL); + + caster->CastCustomSpell(SPELL_PALADIN_SHEATH_OF_LIGHT_HEAL, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_sheath_of_light_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_sheath_of_light_AuraScript(); + } +}; + +// 28789 - Holy Power +class spell_pal_t3_6p_bonus : public SpellScriptLoader +{ + public: + spell_pal_t3_6p_bonus() : SpellScriptLoader("spell_pal_t3_6p_bonus") { } + + class spell_pal_t3_6p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_t3_6p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_HOLY_POWER_ARMOR) || + !sSpellMgr->GetSpellInfo(SPELL_PALADIN_HOLY_POWER_ATTACK_POWER) || + !sSpellMgr->GetSpellInfo(SPELL_PALADIN_HOLY_POWER_SPELL_POWER) || + !sSpellMgr->GetSpellInfo(SPELL_PALADIN_HOLY_POWER_MP5)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + uint32 spellId; + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + + switch (target->getClass()) + { + case CLASS_PALADIN: + case CLASS_PRIEST: + case CLASS_SHAMAN: + case CLASS_DRUID: + spellId = SPELL_PALADIN_HOLY_POWER_MP5; + break; + case CLASS_MAGE: + case CLASS_WARLOCK: + spellId = SPELL_PALADIN_HOLY_POWER_SPELL_POWER; + break; + case CLASS_HUNTER: + case CLASS_ROGUE: + spellId = SPELL_PALADIN_HOLY_POWER_ATTACK_POWER; + break; + case CLASS_WARRIOR: + spellId = SPELL_PALADIN_HOLY_POWER_ARMOR; + break; + default: + return; + } + + caster->CastSpell(target, spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_t3_6p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_t3_6p_bonus_AuraScript(); + } +}; + +// 64890 Item - Paladin T8 Holy 2P Bonus +class spell_pal_t8_2p_bonus : public SpellScriptLoader +{ + public: + spell_pal_t8_2p_bonus() : SpellScriptLoader("spell_pal_t8_2p_bonus") { } + + class spell_pal_t8_2p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_t8_2p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_HOLY_MENDING)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_PALADIN_HOLY_MENDING); + int32 amount = CalculatePct(static_cast<int32>(healInfo->GetHeal()), aurEff->GetAmount()); + amount /= spellInfo->GetMaxTicks(); + // Add remaining ticks to healing done + amount += target->GetRemainingPeriodicAmount(caster->GetGUID(), SPELL_PALADIN_HOLY_MENDING, SPELL_AURA_PERIODIC_HEAL); + + caster->CastCustomSpell(SPELL_PALADIN_HOLY_MENDING, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_t8_2p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_t8_2p_bonus_AuraScript(); + } +}; + void AddSC_paladin_spell_scripts() { new spell_pal_ardent_defender(); @@ -1439,15 +2405,19 @@ void AddSC_paladin_spell_scripts() new spell_pal_avenging_wrath(); new spell_pal_blessing_of_faith(); new spell_pal_blessing_of_sanctuary(); + new spell_pal_divine_purpose(); new spell_pal_divine_sacrifice(); new spell_pal_divine_storm(); new spell_pal_divine_storm_dummy(); new spell_pal_exorcism_and_holy_wrath_damage(); new spell_pal_eye_for_an_eye(); + new spell_pal_glyph_of_divinity(); new spell_pal_glyph_of_holy_light(); + new spell_pal_glyph_of_holy_light_dummy(); new spell_pal_guarded_by_the_light(); new spell_pal_hand_of_sacrifice(); new spell_pal_hand_of_salvation(); + new spell_pal_heart_of_the_crusader(); new spell_pal_holy_shock(); new spell_pal_illumination(); new spell_pal_improved_aura("spell_pal_improved_concentraction_aura", SPELL_PALADIN_IMPROVED_CONCENTRACTION_AURA); @@ -1457,14 +2427,30 @@ void AddSC_paladin_spell_scripts() new spell_pal_improved_aura_effect("spell_pal_improved_concentraction_aura_effect"); new spell_pal_improved_aura_effect("spell_pal_improved_devotion_aura_effect"); new spell_pal_improved_aura_effect("spell_pal_sanctified_retribution_effect"); + new spell_pal_improved_lay_of_hands(); + new spell_pal_infusion_of_light(); new spell_pal_item_healing_discount(); + new spell_pal_item_t6_trinket(); new spell_pal_judgement("spell_pal_judgement_of_justice", SPELL_PALADIN_JUDGEMENT_OF_JUSTICE); new spell_pal_judgement("spell_pal_judgement_of_light", SPELL_PALADIN_JUDGEMENT_OF_LIGHT); new spell_pal_judgement("spell_pal_judgement_of_wisdom", SPELL_PALADIN_JUDGEMENT_OF_WISDOM); new spell_pal_judgement_of_command(); + new spell_pal_judgement_of_light_heal(); + new spell_pal_judgement_of_wisdom_mana(); + new spell_pal_judgements_of_the_just(); + new spell_pal_judgements_of_the_wise(); new spell_pal_lay_on_hands(); new spell_pal_light_s_beacon(); new spell_pal_righteous_defense(); + new spell_pal_righteous_vengeance(); new spell_pal_sacred_shield(); + new spell_pal_sacred_shield_dummy(); new spell_pal_seal_of_righteousness(); + new spell_pal_seal_of_vengeance<SPELL_PALADIN_HOLY_VENGEANCE, SPELL_PALADIN_SEAL_OF_VENGEANCE_DAMAGE>("spell_pal_seal_of_vengeance"); + new spell_pal_seal_of_vengeance<SPELL_PALADIN_BLOOD_CORRUPTION, SPELL_PALADIN_SEAL_OF_CORRUPTION_DAMAGE>("spell_pal_seal_of_corruption"); + new spell_pal_seals(); + new spell_pal_spiritual_attunement(); + new spell_pal_sheath_of_light(); + new spell_pal_t3_6p_bonus(); + new spell_pal_t8_2p_bonus(); } diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp index 9e2d265aa9c..8a4bdeedccc 100644 --- a/src/server/scripts/Spells/spell_priest.cpp +++ b/src/server/scripts/Spells/spell_priest.cpp @@ -46,6 +46,20 @@ enum PriestSpells SPELL_PRIEST_SHADOW_WORD_DEATH = 32409, SPELL_PRIEST_T9_HEALING_2P = 67201, SPELL_PRIEST_VAMPIRIC_TOUCH_DISPEL = 64085, + SPELL_PRIEST_GLYPH_OF_SHADOWFIEND_MANA = 58227, + SPELL_REPLENISHMENT = 57669, + SPELL_PRIEST_BODY_AND_SOUL_POISON_TRIGGER = 64136, + SPELL_PRIEST_ABOLISH_DISEASE = 552, + SPELL_PRIEST_VAMPIRIC_EMBRACE_HEAL = 15290, + SPELL_PRIEST_DIVINE_BLESSING = 40440, + SPELL_PRIEST_DIVINE_WRATH = 40441, + SPELL_PRIEST_GLYPH_OF_DISPEL_MAGIC_HEAL = 56131, + SPELL_PRIEST_ORACULAR_HEAL = 26170, + SPELL_PRIEST_ARMOR_OF_FAITH = 28810, + SPELL_PRIEST_BLESSED_HEALING = 70772, + SPELL_PRIEST_MIND_BLAST_R1 = 8092, + SPELL_PRIEST_SHADOW_WORD_DEATH_R1 = 32379, + SPELL_PRIEST_MIND_FLAY_DAMAGE = 58381 }; enum PriestSpellIcons @@ -89,6 +103,50 @@ class RaidCheck Unit const* _caster; }; +// 26169 - Oracle Healing Bonus +class spell_pri_aq_3p_bonus : public SpellScriptLoader +{ + public: + spell_pri_aq_3p_bonus() : SpellScriptLoader("spell_pri_aq_3p_bonus") { } + + class spell_pri_aq_3p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_aq_3p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_ORACULAR_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + if (caster == eventInfo.GetProcTarget()) + return; + + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + + int32 amount = CalculatePct(static_cast<int32>(healInfo->GetHeal()), 10); + caster->CastCustomSpell(SPELL_PRIEST_ORACULAR_HEAL, SPELLVALUE_BASE_POINT0, amount, caster, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pri_aq_3p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pri_aq_3p_bonus_AuraScript(); + } +}; + // -27811 - Blessed Recovery class spell_pri_blessed_recovery : public SpellScriptLoader { @@ -109,16 +167,18 @@ public: void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo()) - { - if (Unit* target = eventInfo.GetActionTarget()) - { - uint32 triggerSpell = sSpellMgr->GetSpellWithRank(SPELL_PRIEST_BLESSED_RECOVERY_R1, aurEff->GetSpellInfo()->GetRank()); - uint32 bp = CalculatePct(int32(dmgInfo->GetDamage()), aurEff->GetAmount()) / 3; - bp += target->GetRemainingPeriodicAmount(target->GetGUID(), triggerSpell, SPELL_AURA_PERIODIC_HEAL); - target->CastCustomSpell(triggerSpell, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff); - } - } + DamageInfo* dmgInfo = eventInfo.GetDamageInfo(); + if (!dmgInfo || !dmgInfo->GetDamage()) + return; + + Unit* target = eventInfo.GetActionTarget(); + uint32 triggerSpell = sSpellMgr->GetSpellWithRank(SPELL_PRIEST_BLESSED_RECOVERY_R1, aurEff->GetSpellInfo()->GetRank()); + SpellInfo const* triggerInfo = sSpellMgr->AssertSpellInfo(triggerSpell); + + int32 bp = CalculatePct(static_cast<int32>(dmgInfo->GetDamage()), aurEff->GetAmount()); + bp /= triggerInfo->GetMaxTicks(); + bp += target->GetRemainingPeriodicAmount(target->GetGUID(), triggerSpell, SPELL_AURA_PERIODIC_HEAL); + target->CastCustomSpell(triggerSpell, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff); } void Register() override @@ -133,6 +193,65 @@ public: } }; +// -64127 - Body and Soul +class spell_pri_body_and_soul : public SpellScriptLoader +{ + public: + spell_pri_body_and_soul() : SpellScriptLoader("spell_pri_body_and_soul") { } + + class spell_pri_body_and_soul_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_body_and_soul_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_BODY_AND_SOUL_POISON_TRIGGER) || + !sSpellMgr->GetSpellInfo(SPELL_PRIEST_ABOLISH_DISEASE)) + return false; + return true; + } + + void HandleProcTriggerSpell(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + // Proc only on Power Word: Shield + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo || !(spellInfo->SpellFamilyFlags[0] & 0x00000001)) + { + PreventDefaultAction(); + return; + } + } + + void HandleProcDummy(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + // Proc only on self casted abolish disease + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; + + Unit* caster = eventInfo.GetActor(); + if (spellInfo->Id != SPELL_PRIEST_ABOLISH_DISEASE || caster != eventInfo.GetProcTarget()) + return; + + if (roll_chance_i(aurEff->GetAmount())) + caster->CastSpell(caster, SPELL_PRIEST_BODY_AND_SOUL_POISON_TRIGGER, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pri_body_and_soul_AuraScript::HandleProcTriggerSpell, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + OnEffectProc += AuraEffectProcFn(spell_pri_body_and_soul_AuraScript::HandleProcDummy, EFFECT_1, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pri_body_and_soul_AuraScript(); + } +}; + // -34861 - Circle of Healing class spell_pri_circle_of_healing : public SpellScriptLoader { @@ -201,7 +320,11 @@ class spell_pri_divine_aegis : public SpellScriptLoader { PreventDefaultAction(); - int32 absorb = CalculatePct(int32(eventInfo.GetHealInfo()->GetHeal()), aurEff->GetAmount()); + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + + int32 absorb = CalculatePct(healInfo->GetHeal(), aurEff->GetAmount()); // Multiple effects stack, so let's try to find this aura. if (AuraEffect const* aegis = eventInfo.GetProcTarget()->GetAuraEffect(SPELL_PRIEST_DIVINE_AEGIS, EFFECT_0)) @@ -209,7 +332,7 @@ class spell_pri_divine_aegis : public SpellScriptLoader absorb = std::min(absorb, eventInfo.GetProcTarget()->getLevel() * 125); - GetTarget()->CastCustomSpell(SPELL_PRIEST_DIVINE_AEGIS, SPELLVALUE_BASE_POINT0, absorb, eventInfo.GetProcTarget(), true, NULL, aurEff); + GetTarget()->CastCustomSpell(SPELL_PRIEST_DIVINE_AEGIS, SPELLVALUE_BASE_POINT0, absorb, eventInfo.GetProcTarget(), true, nullptr, aurEff); } void Register() override @@ -260,6 +383,50 @@ class spell_pri_divine_hymn : public SpellScriptLoader } }; +// 55677 - Glyph of Dispel Magic +class spell_pri_glyph_of_dispel_magic : public SpellScriptLoader +{ + public: + spell_pri_glyph_of_dispel_magic() : SpellScriptLoader("spell_pri_glyph_of_dispel_magic") { } + + class spell_pri_glyph_of_dispel_magic_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_glyph_of_dispel_magic_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_GLYPH_OF_DISPEL_MAGIC_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + // Dispel Magic shares spellfamilyflag with abolish disease + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo || spellInfo->SpellIconID != 74) + return; + + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + int32 amount = static_cast<int32>(target->CountPctFromMaxHealth(aurEff->GetAmount())); + + caster->CastCustomSpell(SPELL_PRIEST_GLYPH_OF_DISPEL_MAGIC_HEAL, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pri_glyph_of_dispel_magic_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pri_glyph_of_dispel_magic_AuraScript(); + } +}; + // 55680 - Glyph of Prayer of Healing class spell_pri_glyph_of_prayer_of_healing : public SpellScriptLoader { @@ -281,9 +448,13 @@ class spell_pri_glyph_of_prayer_of_healing : public SpellScriptLoader { PreventDefaultAction(); + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + SpellInfo const* triggeredSpellInfo = sSpellMgr->AssertSpellInfo(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL); - int32 heal = int32(CalculatePct(int32(eventInfo.GetHealInfo()->GetHeal()), aurEff->GetAmount()) / triggeredSpellInfo->GetMaxTicks()); - GetTarget()->CastCustomSpell(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL, SPELLVALUE_BASE_POINT0, heal, eventInfo.GetProcTarget(), true, NULL, aurEff); + int32 heal = int32(CalculatePct(healInfo->GetHeal(), aurEff->GetAmount()) / triggeredSpellInfo->GetMaxTicks()); + GetTarget()->CastCustomSpell(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL, SPELLVALUE_BASE_POINT0, heal, eventInfo.GetProcTarget(), true, nullptr, aurEff); } void Register() override @@ -398,38 +569,117 @@ class spell_pri_hymn_of_hope : public SpellScriptLoader } }; -// 37594 - Greater Heal Refund -class spell_pri_item_greater_heal_refund : public SpellScriptLoader +// -47569 - Improved Shadowform +class spell_pri_imp_shadowform : public SpellScriptLoader +{ + public: + spell_pri_imp_shadowform() : SpellScriptLoader("spell_pri_imp_shadowform") { } + + class spell_pri_imp_shadowform_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_imp_shadowform_AuraScript); + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + if (roll_chance_i(aurEff->GetAmount())) + eventInfo.GetActor()->RemoveMovementImpairingAuras(); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pri_imp_shadowform_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pri_imp_shadowform_AuraScript(); + } +}; + +// -15337 - Improved Spirit Tap +class spell_pri_improved_spirit_tap : public SpellScriptLoader { public: - spell_pri_item_greater_heal_refund() : SpellScriptLoader("spell_pri_item_greater_heal_refund") { } + spell_pri_improved_spirit_tap() : SpellScriptLoader("spell_pri_improved_spirit_tap") { } - class spell_pri_item_greater_heal_refund_AuraScript : public AuraScript + class spell_pri_improved_spirit_tap_AuraScript : public AuraScript { - PrepareAuraScript(spell_pri_item_greater_heal_refund_AuraScript); + PrepareAuraScript(spell_pri_improved_spirit_tap_AuraScript); bool Validate(SpellInfo const* /*spellInfo*/) override { - if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_ITEM_EFFICIENCY)) + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_SHADOW_WORD_DEATH_R1) || + !sSpellMgr->GetSpellInfo(SPELL_PRIEST_MIND_BLAST_R1)) return false; return true; } - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + bool CheckProc(ProcEventInfo& eventInfo) + { + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + { + if (spellInfo->IsRankOf(sSpellMgr->AssertSpellInfo(SPELL_PRIEST_SHADOW_WORD_DEATH_R1)) || + spellInfo->IsRankOf(sSpellMgr->AssertSpellInfo(SPELL_PRIEST_MIND_BLAST_R1))) + return true; + else if (spellInfo->Id == SPELL_PRIEST_MIND_FLAY_DAMAGE) + return roll_chance_i(50); + } + + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_pri_improved_spirit_tap_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pri_improved_spirit_tap_AuraScript(); + } +}; + +// 40438 - Priest Tier 6 Trinket +class spell_pri_item_t6_trinket : public SpellScriptLoader +{ + public: + spell_pri_item_t6_trinket() : SpellScriptLoader("spell_pri_item_t6_trinket") { } + + class spell_pri_item_t6_trinket_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_item_t6_trinket_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_DIVINE_BLESSING) || + !sSpellMgr->GetSpellInfo(SPELL_PRIEST_DIVINE_WRATH)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - GetTarget()->CastSpell(GetTarget(), SPELL_PRIEST_ITEM_EFFICIENCY, true, NULL, aurEff); + Unit* caster = eventInfo.GetActor(); + if (eventInfo.GetSpellTypeMask() & PROC_SPELL_TYPE_HEAL) + caster->CastSpell((Unit*)nullptr, SPELL_PRIEST_DIVINE_BLESSING, true, nullptr, aurEff); + + if (eventInfo.GetSpellTypeMask() & PROC_SPELL_TYPE_DAMAGE) + caster->CastSpell((Unit*)nullptr, SPELL_PRIEST_DIVINE_WRATH, true, nullptr, aurEff); } void Register() override { - OnEffectProc += AuraEffectProcFn(spell_pri_item_greater_heal_refund_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + OnEffectProc += AuraEffectProcFn(spell_pri_item_t6_trinket_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); } }; AuraScript* GetAuraScript() const override { - return new spell_pri_item_greater_heal_refund_AuraScript(); + return new spell_pri_item_t6_trinket_AuraScript(); } }; @@ -572,6 +822,33 @@ class spell_pri_mind_sear : public SpellScriptLoader } }; +// -47580 - Pain and Suffering (dummy aura) +class spell_pri_pain_and_suffering_dummy : public SpellScriptLoader +{ + public: + spell_pri_pain_and_suffering_dummy() : SpellScriptLoader("spell_pri_pain_and_suffering_dummy") { } + + class spell_pri_pain_and_suffering_dummy_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_pain_and_suffering_dummy_AuraScript); + + bool CheckDummy(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + return false; + } + + void Register() override + { + DoCheckEffectProc += AuraCheckEffectProcFn(spell_pri_pain_and_suffering_dummy_AuraScript::CheckDummy, EFFECT_1, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pri_pain_and_suffering_dummy_AuraScript; + } +}; + // 47948 - Pain and Suffering (Proc) class spell_pri_pain_and_suffering_proc : public SpellScriptLoader { @@ -587,12 +864,14 @@ class spell_pri_pain_and_suffering_proc : public SpellScriptLoader Unit* caster = GetCaster(); // Refresh Shadow Word: Pain on target if (Unit* target = GetHitUnit()) + { if (AuraEffect* aur = target->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_PRIEST, 0x8000, 0, 0, caster->GetGUID())) { aur->SetBonusAmount(caster->SpellDamageBonusDone(target, aur->GetSpellInfo(), 0, DOT)); aur->CalculatePeriodic(caster, false, false); aur->GetBase()->RefreshDuration(); } + } } void Register() override @@ -812,17 +1091,22 @@ class spell_pri_renew : public SpellScriptLoader void HandleApplyEffect(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) { - if (Unit* caster = GetCaster()) + Unit* caster = GetCaster(); + if (!caster) + return; + + // Empowered Renew + if (AuraEffect const* empoweredRenewAurEff = caster->GetDummyAuraEffect(SPELLFAMILY_PRIEST, PRIEST_ICON_ID_EMPOWERED_RENEW_TALENT, EFFECT_1)) { - // Empowered Renew - if (AuraEffect const* empoweredRenewAurEff = caster->GetDummyAuraEffect(SPELLFAMILY_PRIEST, PRIEST_ICON_ID_EMPOWERED_RENEW_TALENT, EFFECT_1)) - { - uint32 heal = caster->SpellHealingBonusDone(GetTarget(), GetSpellInfo(), GetEffect(EFFECT_0)->GetAmount(), DOT); - heal = GetTarget()->SpellHealingBonusTaken(caster, GetSpellInfo(), heal, DOT); + int32 heal = (aurEff->GetAmount() + aurEff->GetBonusAmount()) * aurEff->GetDonePct(); + if (Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod<SPELLMOD_DOT>(GetId(), heal); + heal = GetTarget()->SpellHealingBonusTaken(caster, GetSpellInfo(), heal, DOT); - int32 basepoints0 = empoweredRenewAurEff->GetAmount() * GetEffect(EFFECT_0)->GetTotalTicks() * int32(heal) / 100; - caster->CastCustomSpell(GetTarget(), SPELL_PRIEST_EMPOWERED_RENEW, &basepoints0, NULL, NULL, true, NULL, aurEff); - } + heal *= GetSpellInfo()->GetMaxTicks(); + + int32 basepoints0 = CalculatePct(heal, empoweredRenewAurEff->GetAmount()); + caster->CastCustomSpell(SPELL_PRIEST_EMPOWERED_RENEW, SPELLVALUE_BASE_POINT0, basepoints0, GetTarget(), true, nullptr, aurEff); } } @@ -838,6 +1122,56 @@ class spell_pri_renew : public SpellScriptLoader } }; +// 57989 - Shadowfiend Death +class spell_pri_shadowfiend_death : public SpellScriptLoader +{ + public: + spell_pri_shadowfiend_death() : SpellScriptLoader("spell_pri_shadowfiend_death") { } + + class spell_pri_shadowfiend_death_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_shadowfiend_death_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_GLYPH_OF_SHADOWFIEND_MANA)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return false; + + Unit* shadowfiend = eventInfo.GetActionTarget(); + if (!shadowfiend->GetOwner()) + return false; + + return shadowfiend->HealthBelowPctDamaged(1, damageInfo->GetDamage()); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActionTarget()->GetOwner(); + caster->CastSpell(caster, SPELL_PRIEST_GLYPH_OF_SHADOWFIEND_MANA, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_pri_shadowfiend_death_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_pri_shadowfiend_death_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pri_shadowfiend_death_AuraScript(); + } +}; + // -32379 - Shadow Word Death class spell_pri_shadow_word_death : public SpellScriptLoader { @@ -871,6 +1205,48 @@ class spell_pri_shadow_word_death : public SpellScriptLoader } }; +// 15286 - Vampiric Embrace +class spell_pri_vampiric_embrace : public SpellScriptLoader +{ + public: + spell_pri_vampiric_embrace() : SpellScriptLoader("spell_pri_vampiric_embrace") { } + + class spell_pri_vampiric_embrace_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_vampiric_embrace_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_VAMPIRIC_EMBRACE_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 selfHeal = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + int32 partyHeal = selfHeal / 5; + Unit* caster = eventInfo.GetActor(); + caster->CastCustomSpell((Unit*)nullptr, SPELL_PRIEST_VAMPIRIC_EMBRACE_HEAL, &partyHeal, &selfHeal, nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pri_vampiric_embrace_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pri_vampiric_embrace_AuraScript(); + } +}; + // -34914 - Vampiric Touch class spell_pri_vampiric_touch : public SpellScriptLoader { @@ -883,26 +1259,45 @@ class spell_pri_vampiric_touch : public SpellScriptLoader bool Validate(SpellInfo const* /*spellInfo*/) override { - if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_VAMPIRIC_TOUCH_DISPEL)) + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_VAMPIRIC_TOUCH_DISPEL) || + !sSpellMgr->GetSpellInfo(SPELL_REPLENISHMENT)) return false; return true; } + bool CheckDummy(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + return false; + } + void HandleDispel(DispelInfo* /*dispelInfo*/) { if (Unit* caster = GetCaster()) + { if (Unit* target = GetUnitOwner()) + { if (AuraEffect const* aurEff = GetEffect(EFFECT_1)) { int32 damage = aurEff->GetAmount() * 8; // backfire damage - caster->CastCustomSpell(target, SPELL_PRIEST_VAMPIRIC_TOUCH_DISPEL, &damage, NULL, NULL, true, NULL, aurEff); + caster->CastCustomSpell(SPELL_PRIEST_VAMPIRIC_TOUCH_DISPEL, SPELLVALUE_BASE_POINT0, damage, target, true, nullptr, aurEff); } + } + } + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_REPLENISHMENT, true, nullptr, aurEff); } void Register() override { + DoCheckEffectProc += AuraCheckEffectProcFn(spell_pri_vampiric_touch_AuraScript::CheckDummy, EFFECT_0, SPELL_AURA_DUMMY); + AfterDispel += AuraDispelFn(spell_pri_vampiric_touch_AuraScript::HandleDispel); + OnEffectProc += AuraEffectProcFn(spell_pri_vampiric_touch_AuraScript::HandleProc, EFFECT_2, SPELL_AURA_DUMMY); } }; @@ -912,25 +1307,167 @@ class spell_pri_vampiric_touch : public SpellScriptLoader } }; +// 28809 - Greater Heal +class spell_pri_t3_4p_bonus : public SpellScriptLoader +{ + public: + spell_pri_t3_4p_bonus() : SpellScriptLoader("spell_pri_t3_4p_bonus") { } + + class spell_pri_t3_4p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_t3_4p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_ARMOR_OF_FAITH)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), SPELL_PRIEST_ARMOR_OF_FAITH, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pri_t3_4p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pri_t3_4p_bonus_AuraScript(); + } +}; + +// 37594 - Greater Heal Refund +class spell_pri_t5_heal_2p_bonus : public SpellScriptLoader +{ + public: + spell_pri_t5_heal_2p_bonus() : SpellScriptLoader("spell_pri_t5_heal_2p_bonus") { } + + class spell_pri_t5_heal_2p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_t5_heal_2p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_ITEM_EFFICIENCY)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (HealInfo* healInfo = eventInfo.GetHealInfo()) + if (Unit* healTarget = healInfo->GetTarget()) + if (healInfo->GetEffectiveHeal()) + if (healTarget->GetHealth() >= healTarget->GetMaxHealth()) + return true; + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), SPELL_PRIEST_ITEM_EFFICIENCY, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_pri_t5_heal_2p_bonus_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_pri_t5_heal_2p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pri_t5_heal_2p_bonus_AuraScript(); + } +}; + +// 70770 - Item - Priest T10 Healer 2P Bonus +class spell_pri_t10_heal_2p_bonus : public SpellScriptLoader +{ + public: + spell_pri_t10_heal_2p_bonus() : SpellScriptLoader("spell_pri_t10_heal_2p_bonus") { } + + class spell_pri_t10_heal_2p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_t10_heal_2p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_BLESSED_HEALING)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_PRIEST_BLESSED_HEALING); + int32 amount = CalculatePct(static_cast<int32>(healInfo->GetHeal()), aurEff->GetAmount()); + amount /= spellInfo->GetMaxTicks(); + + // Add remaining ticks to healing done + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + amount += target->GetRemainingPeriodicAmount(caster->GetGUID(), SPELL_PRIEST_BLESSED_HEALING, SPELL_AURA_PERIODIC_HEAL); + + caster->CastCustomSpell(SPELL_PRIEST_BLESSED_HEALING, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pri_t10_heal_2p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pri_t10_heal_2p_bonus_AuraScript(); + } +}; + void AddSC_priest_spell_scripts() { + new spell_pri_aq_3p_bonus(); new spell_pri_blessed_recovery(); + new spell_pri_body_and_soul(); new spell_pri_circle_of_healing(); new spell_pri_divine_aegis(); new spell_pri_divine_hymn(); + new spell_pri_glyph_of_dispel_magic(); new spell_pri_glyph_of_prayer_of_healing(); new spell_pri_guardian_spirit(); new spell_pri_hymn_of_hope(); - new spell_pri_item_greater_heal_refund(); + new spell_pri_imp_shadowform(); + new spell_pri_improved_spirit_tap(); + new spell_pri_item_t6_trinket(); new spell_pri_lightwell_renew(); new spell_pri_mana_burn(); new spell_pri_mana_leech(); new spell_pri_mind_sear(); + new spell_pri_pain_and_suffering_dummy(); new spell_pri_pain_and_suffering_proc(); new spell_pri_penance(); new spell_pri_power_word_shield(); new spell_pri_prayer_of_mending_heal(); new spell_pri_renew(); + new spell_pri_shadowfiend_death(); new spell_pri_shadow_word_death(); + new spell_pri_vampiric_embrace(); new spell_pri_vampiric_touch(); + new spell_pri_t3_4p_bonus(); + new spell_pri_t5_heal_2p_bonus(); + new spell_pri_t10_heal_2p_bonus(); } 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_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp index 40bb3c8ad42..f3d30af1f84 100644 --- a/src/server/scripts/Spells/spell_rogue.cpp +++ b/src/server/scripts/Spells/spell_rogue.cpp @@ -44,7 +44,10 @@ enum RogueSpells SPELL_ROGUE_HONOR_AMONG_THIEVES = 51698, SPELL_ROGUE_HONOR_AMONG_THIEVES_PROC = 52916, SPELL_ROGUE_HONOR_AMONG_THIEVES_2 = 51699, - SPELL_ROGUE_T10_2P_BONUS = 70804 + SPELL_ROGUE_T10_2P_BONUS = 70804, + SPELL_ROGUE_GLYPH_OF_BACKSTAB_TRIGGER = 63975, + SPELL_ROGUE_QUICK_RECOVERY_ENERGY = 31663, + SPELL_ROGUE_CRIPPLING_POISON = 3409 }; // 13877, 33735, (check 51211, 65956) - Blade Flurry @@ -80,10 +83,10 @@ class spell_rog_blade_flurry : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - if (eventInfo.GetDamageInfo()) + if (DamageInfo* damageInfo = eventInfo.GetDamageInfo()) { - int32 damage = eventInfo.GetDamageInfo()->GetDamage(); - GetTarget()->CastCustomSpell(SPELL_ROGUE_BLADE_FLURRY_EXTRA_ATTACK, SPELLVALUE_BASE_POINT0, damage, _procTarget, true, NULL, aurEff); + int32 damage = damageInfo->GetDamage(); + GetTarget()->CastCustomSpell(SPELL_ROGUE_BLADE_FLURRY_EXTRA_ATTACK, SPELLVALUE_BASE_POINT0, damage, _procTarget, true, nullptr, aurEff); } } @@ -173,6 +176,80 @@ class spell_rog_cheat_death : public SpellScriptLoader } }; +// -51664 - Cut to the Chase +class spell_rog_cut_to_the_chase : public SpellScriptLoader +{ + public: + spell_rog_cut_to_the_chase() : SpellScriptLoader("spell_rog_cut_to_the_chase") { } + + class spell_rog_cut_to_the_chase_AuraScript : public AuraScript + { + PrepareAuraScript(spell_rog_cut_to_the_chase_AuraScript); + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + // "refresh your Slice and Dice duration to its 5 combo point maximum" + Unit* caster = eventInfo.GetActor(); + // lookup Slice and Dice + if (AuraEffect const* snd = caster->GetAuraEffect(SPELL_AURA_MOD_MELEE_HASTE, SPELLFAMILY_ROGUE, 0x00040000, 0x00000000, 0x00000000, caster->GetGUID())) + { + // Max 5 cp duration + uint32 countMax = snd->GetSpellInfo()->GetMaxDuration(); + + snd->GetBase()->SetDuration(countMax, true); + snd->GetBase()->SetMaxDuration(snd->GetBase()->GetDuration()); + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_rog_cut_to_the_chase_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_rog_cut_to_the_chase_AuraScript(); + } +}; + +// -51625 - Deadly Brew +class spell_rog_deadly_brew : public SpellScriptLoader +{ + public: + spell_rog_deadly_brew() : SpellScriptLoader("spell_rog_deadly_brew") { } + + class spell_rog_deadly_brew_AuraScript : public AuraScript + { + PrepareAuraScript(spell_rog_deadly_brew_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_CRIPPLING_POISON)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), SPELL_ROGUE_CRIPPLING_POISON, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_rog_deadly_brew_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_rog_deadly_brew_AuraScript(); + } +}; + // -2818 - Deadly Poison class spell_rog_deadly_poison : public SpellScriptLoader { @@ -522,8 +599,49 @@ class spell_rog_prey_on_the_weak : public SpellScriptLoader } }; +// -31244 - Quick Recovery +class spell_rog_quick_recovery : public SpellScriptLoader +{ + public: + spell_rog_quick_recovery() : SpellScriptLoader("spell_rog_quick_recovery") { } + + class spell_rog_quick_recovery_AuraScript : public AuraScript + { + PrepareAuraScript(spell_rog_quick_recovery_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_QUICK_RECOVERY_ENERGY)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; + + Unit* caster = eventInfo.GetActor(); + int32 amount = CalculatePct(spellInfo->CalcPowerCost(caster, spellInfo->GetSchoolMask()), aurEff->GetAmount()); + caster->CastCustomSpell(SPELL_ROGUE_QUICK_RECOVERY_ENERGY, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_rog_quick_recovery_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_rog_quick_recovery_AuraScript(); + } +}; + // -1943 - Rupture -#define RuptureScriptName "spell_rog_rupture" +static char const* const RuptureScriptName = "spell_rog_rupture"; class spell_rog_rupture : public SpellScriptLoader { public: @@ -586,6 +704,41 @@ class spell_rog_rupture : public SpellScriptLoader } }; +// 56800 - Glyph of Backstab (dummy) +class spell_rog_glyph_of_backstab : public SpellScriptLoader +{ + public: + spell_rog_glyph_of_backstab() : SpellScriptLoader("spell_rog_glyph_of_backstab") { } + + class spell_rog_glyph_of_backstab_AuraScript : public AuraScript + { + PrepareAuraScript(spell_rog_glyph_of_backstab_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_GLYPH_OF_BACKSTAB_TRIGGER)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), SPELL_ROGUE_GLYPH_OF_BACKSTAB_TRIGGER, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_rog_glyph_of_backstab_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_rog_glyph_of_backstab_AuraScript(); + } +}; + // 63975 - Glyph of Backstab (triggered - serverside) class spell_rog_glyph_of_backstab_triggered : public SpellScriptLoader { @@ -642,6 +795,37 @@ class spell_rog_glyph_of_backstab_triggered : public SpellScriptLoader } }; +// -13983 - Setup +class spell_rog_setup : public SpellScriptLoader +{ + public: + spell_rog_setup() : SpellScriptLoader("spell_rog_setup") { } + + class spell_rog_setup_AuraScript : public AuraScript + { + PrepareAuraScript(spell_rog_setup_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (Player* target = GetTarget()->ToPlayer()) + if (eventInfo.GetActor() == target->GetSelectedUnit()) + return true; + + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_rog_setup_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_rog_setup_AuraScript(); + } +}; + // 5938 - Shiv class spell_rog_shiv : public SpellScriptLoader { @@ -785,16 +969,21 @@ public: { PrepareAuraScript(spell_rog_honor_among_thieves_AuraScript); + bool Validate(SpellInfo const* spellInfo) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_HONOR_AMONG_THIEVES_2) || + !sSpellMgr->GetSpellInfo(spellInfo->Effects[EFFECT_0].TriggerSpell)) + return false; + return true; + } + bool CheckProc(ProcEventInfo& /*eventInfo*/) { Unit* caster = GetCaster(); - if (!caster) + if (!caster || caster->HasAura(SPELL_ROGUE_HONOR_AMONG_THIEVES_2)) return false; - if (!caster->GetSpellHistory()->HasCooldown(GetSpellInfo()->Effects[EFFECT_0].TriggerSpell)) - return true; - - return false; + return true; } void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) @@ -806,7 +995,7 @@ public: return; Unit* target = GetTarget(); - target->CastSpell(target, GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD), nullptr, aurEff, caster->GetGUID()); + target->CastSpell(target, GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, true, nullptr, aurEff, caster->GetGUID()); } void Register() override @@ -851,36 +1040,9 @@ public: targets.push_back(target); } - void HandleBeforeHit() - { - Unit* target = GetHitUnit(); - if (!target) - return; - - /* - * The applied aura has a duration of 8 seconds - * This prevents new applications while its active - * Removing it on each new proc enables the application from different sources (different grouped players) - * and on new procs after the source cooldown is finished (1 second) - */ - if (target->HasAura(GetSpellInfo()->Id)) - target->RemoveAura(GetSpellInfo()->Id); - } - - void TriggerCooldown() - { - Unit* target = GetHitUnit(); - if (!target) - return; - - target->GetSpellHistory()->AddCooldown(GetSpellInfo()->Id, 0, std::chrono::seconds(1)); - } - void Register() override { OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_rog_honor_among_thieves_proc_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_CASTER_AREA_PARTY); - BeforeHit += SpellHitFn(spell_rog_honor_among_thieves_proc_SpellScript::HandleBeforeHit); - AfterHit += SpellHitFn(spell_rog_honor_among_thieves_proc_SpellScript::TriggerCooldown); } }; @@ -895,14 +1057,17 @@ public: void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - if (Player* player = GetTarget()->ToPlayer()) - if (Unit* spellTarget = ObjectAccessor::GetUnit(*player, player->GetTarget())) - player->CastSpell(spellTarget, SPELL_ROGUE_HONOR_AMONG_THIEVES_2, true); + Unit* caster = GetCaster(); + if (!caster) + return; + + if (Player* player = caster->ToPlayer()) + player->CastSpell((Unit*)nullptr, SPELL_ROGUE_HONOR_AMONG_THIEVES_2, true); } void Register() override { - AfterEffectApply += AuraEffectApplyFn(spell_rog_honor_among_thieves_proc_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + AfterEffectApply += AuraEffectApplyFn(spell_rog_honor_among_thieves_proc_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); } }; @@ -912,55 +1077,101 @@ public: } }; -// 70805 - Rogue T10 2P Bonus -- THIS SHOULD BE REMOVED WITH NEW PROC SYSTEM. -class spell_rog_t10_2p_bonus : public SpellScriptLoader +// -51627 - Turn the Tables +class spell_rog_turn_the_tables : public SpellScriptLoader { -public: - spell_rog_t10_2p_bonus() : SpellScriptLoader("spell_rog_t10_2p_bonus") { } + public: + spell_rog_turn_the_tables() : SpellScriptLoader("spell_rog_turn_the_tables") { } - class spell_rog_t10_2p_bonus_AuraScript : public AuraScript - { - PrepareAuraScript(spell_rog_t10_2p_bonus_AuraScript); + class spell_rog_turn_the_tables_AuraScript : public AuraScript + { + PrepareAuraScript(spell_rog_turn_the_tables_AuraScript); - bool Validate(SpellInfo const* /*spellInfo*/) override + 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(); + + Unit* caster = GetCaster(); + if (!caster) + return; + + Unit* target = GetTarget(); + target->CastSpell((Unit*)nullptr, GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, true, nullptr, aurEff, caster->GetGUID()); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_rog_turn_the_tables_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override { - if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_T10_2P_BONUS)) - return false; - return true; + return new spell_rog_turn_the_tables_AuraScript(); } +}; - bool CheckProc(ProcEventInfo& eventInfo) +// 52910,52914,52915 - Turn the Tables proc +class spell_rog_turn_the_tables_proc : public SpellScriptLoader +{ + public: + spell_rog_turn_the_tables_proc() : SpellScriptLoader("spell_rog_turn_the_tables_proc") { } + + class spell_rog_turn_the_tables_proc_SpellScript : public SpellScript { - return eventInfo.GetActor() == eventInfo.GetActionTarget(); - } + PrepareSpellScript(spell_rog_turn_the_tables_proc_SpellScript); - void Register() override + void FilterTargets(std::list<WorldObject*>& targets) + { + targets.clear(); + + Unit* target = GetOriginalCaster(); + if (!target) + return; + + targets.push_back(target); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_rog_turn_the_tables_proc_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_CASTER_AREA_RAID); + } + }; + + SpellScript* GetSpellScript() const override { - DoCheckProc += AuraCheckProcFn(spell_rog_t10_2p_bonus_AuraScript::CheckProc); + return new spell_rog_turn_the_tables_proc_SpellScript(); } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_rog_t10_2p_bonus_AuraScript(); - } }; void AddSC_rogue_spell_scripts() { new spell_rog_blade_flurry(); new spell_rog_cheat_death(); + new spell_rog_cut_to_the_chase(); + new spell_rog_deadly_brew(); new spell_rog_deadly_poison(); new spell_rog_killing_spree(); new spell_rog_nerves_of_steel(); new spell_rog_preparation(); new spell_rog_prey_on_the_weak(); + new spell_rog_quick_recovery(); new spell_rog_rupture(); + new spell_rog_glyph_of_backstab(); new spell_rog_glyph_of_backstab_triggered(); + new spell_rog_setup(); new spell_rog_shiv(); new spell_rog_tricks_of_the_trade(); new spell_rog_tricks_of_the_trade_proc(); new spell_rog_honor_among_thieves(); new spell_rog_honor_among_thieves_proc(); - new spell_rog_t10_2p_bonus(); + new spell_rog_turn_the_tables(); + new spell_rog_turn_the_tables_proc(); } diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index ad65c7c6ec7..37b08aee7ca 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -31,6 +31,7 @@ enum ShamanSpells { + SPELL_SHAMAN_ANCESTRAL_AWAKENING_DUMMY = 52759, SPELL_SHAMAN_ANCESTRAL_AWAKENING_PROC = 52752, SPELL_SHAMAN_BIND_SIGHT = 6277, SPELL_SHAMAN_CLEANSING_TOTEM_EFFECT = 52025, @@ -58,13 +59,79 @@ enum ShamanSpells SPELL_SHAMAN_TOTEM_EARTHBIND_TOTEM = 6474, SPELL_SHAMAN_TOTEM_EARTHEN_POWER = 59566, SPELL_SHAMAN_TOTEM_HEALING_STREAM_HEAL = 52042, - SPELL_SHAMAN_TOTEMIC_MASTERY = 38437 + SPELL_SHAMAN_TOTEMIC_MASTERY = 38437, + SPELL_SHAMAN_TIDAL_FORCE_CRIT = 55166, + SPELL_SHAMAN_TOTEMIC_POWER_MP5 = 28824, + SPELL_SHAMAN_TOTEMIC_POWER_SPELL_POWER = 28825, + SPELL_SHAMAN_TOTEMIC_POWER_ATTACK_POWER = 28826, + SPELL_SHAMAN_TOTEMIC_POWER_ARMOR = 28827, + SPELL_SHAMAN_WINDFURY_WEAPON_R1 = 8232, + SPELL_SHAMAN_WINDFURY_ATTACK_MH = 25504, + SPELL_SHAMAN_WINDFURY_ATTACK_OH = 33750, + SPELL_SHAMAN_ENERGY_SURGE = 40465, + SPELL_SHAMAN_POWER_SURGE = 40466, + SPELL_SHAMAN_GLYPH_OF_HEALING_WAVE_HEAL = 55533, + SPELL_SHAMAN_SPIRIT_HUNT_HEAL = 58879, + SPELL_SHAMAN_ELECTRIFIED = 64930, + SPELL_SHAMAN_LAVA_BURST_BONUS_DAMAGE = 71824, + SPELL_SHAMAN_CHAINED_HEAL = 70809, + SPELL_SHAMAN_TOTEM_OF_WRATH_SPELL_POWER = 63283, + SPELL_SHAMAN_FREEZE = 63685, + SPELL_SHAMAN_FLAMETONGUE_ATTACK = 10444, + SPELL_SHAMAN_LIGHTNING_BOLT_OVERLOAD_R1 = 45284, + SPELL_SHAMAN_CHAIN_LIGHTNING_OVERLOAD_R1 = 45297, + 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_BLESSING_OF_THE_ETERNALS_R1 = 51554 }; enum ShamanSpellIcons { SHAMAN_ICON_ID_RESTORATIVE_TOTEMS = 338, - SHAMAN_ICON_ID_SHAMAN_LAVA_FLOW = 3087 + SHAMAN_ICON_ID_SHAMAN_LAVA_FLOW = 3087, + SHAMAN_ICON_ID_TOTEM_OF_WRATH = 2019 +}; + +// -51556 - Ancestral Awakening +class spell_sha_ancestral_awakening : public SpellScriptLoader +{ + public: + spell_sha_ancestral_awakening() : SpellScriptLoader("spell_sha_ancestral_awakening") { } + + class spell_sha_ancestral_awakening_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_ancestral_awakening_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ANCESTRAL_AWAKENING_DUMMY)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + + int32 amount = CalculatePct(static_cast<int32>(healInfo->GetHeal()), aurEff->GetAmount()); + eventInfo.GetActor()->CastCustomSpell(SPELL_SHAMAN_ANCESTRAL_AWAKENING_DUMMY, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_ancestral_awakening_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_ancestral_awakening_AuraScript(); + } }; // 52759 - Ancestral Awakening (Proc) @@ -167,6 +234,37 @@ class spell_sha_astral_shift : public SpellScriptLoader } }; +// -51474 - Astral Shift aura +class spell_sha_astral_shift_aura : public SpellScriptLoader +{ + public: + spell_sha_astral_shift_aura() : SpellScriptLoader("spell_sha_astral_shift_aura") { } + + class spell_sha_astral_shift_aura_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_astral_shift_aura_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + if (spellInfo->GetAllEffectsMechanicMask() & ((1 << MECHANIC_SILENCE) | (1 << MECHANIC_STUN) | (1 << MECHANIC_FEAR))) + return true; + + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_sha_astral_shift_aura_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_astral_shift_aura_AuraScript(); + } +}; + // 2825 - Bloodlust class spell_sha_bloodlust : public SpellScriptLoader { @@ -311,57 +409,26 @@ class spell_sha_earth_shield : public SpellScriptLoader { if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_EARTH_SHIELD_HEAL)) return false; - if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_GLYPH_OF_EARTH_SHIELD)) - return false; return true; } - void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool & /*canBeRecalculated*/) + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) { if (Unit* caster = GetCaster()) - { amount = caster->SpellHealingBonusDone(GetUnitOwner(), GetSpellInfo(), amount, HEAL); - amount = GetUnitOwner()->SpellHealingBonusTaken(caster, GetSpellInfo(), amount, HEAL); - - //! WORKAROUND - // If target is affected by healing reduction, modifier is guaranteed to be negative - // value (e.g. -50). To revert the effect, multiply amount with reciprocal of relative value: - // (100 / ((-1) * modifier)) * 100 = (-1) * 100 * 100 / modifier = -10000 / modifier - if (int32 modifier = GetUnitOwner()->GetMaxNegativeAuraModifier(SPELL_AURA_MOD_HEALING_PCT)) - ApplyPct(amount, -10000.0f / float(modifier)); - - // Glyph of Earth Shield - //! WORKAROUND - //! this glyph is a proc - if (AuraEffect* glyph = caster->GetAuraEffect(SPELL_SHAMAN_GLYPH_OF_EARTH_SHIELD, EFFECT_0)) - AddPct(amount, glyph->GetAmount()); - } - } - - bool CheckProc(ProcEventInfo& /*eventInfo*/) - { - //! HACK due to currenct proc system implementation - if (Player* player = GetTarget()->ToPlayer()) - if (player->GetSpellHistory()->HasCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL)) - return false; - return true; + // SpellHealingBonusTaken will be called on Heal } void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) { PreventDefaultAction(); - GetTarget()->CastCustomSpell(SPELL_SHAMAN_EARTH_SHIELD_HEAL, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), GetTarget(), true, NULL, aurEff, GetCasterGUID()); - - /// @hack: due to currenct proc system implementation - if (Player* player = GetTarget()->ToPlayer()) - player->GetSpellHistory()->AddCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL, 0, std::chrono::seconds(3)); + GetTarget()->CastCustomSpell(SPELL_SHAMAN_EARTH_SHIELD_HEAL, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), GetTarget(), true, nullptr, aurEff, GetCasterGUID()); } void Register() override { DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_sha_earth_shield_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_DUMMY); - DoCheckProc += AuraCheckProcFn(spell_sha_earth_shield_AuraScript::CheckProc); OnEffectProc += AuraEffectProcFn(spell_sha_earth_shield_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); } }; @@ -471,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 { @@ -575,6 +682,281 @@ class spell_sha_flame_shock : public SpellScriptLoader } }; +// -10400 - Flametongue Weapon (Passive) +class spell_sha_flametongue_weapon : public SpellScriptLoader +{ + public: + spell_sha_flametongue_weapon() : SpellScriptLoader("spell_sha_flametongue_weapon") { } + + class spell_sha_flametongue_weapon_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_flametongue_weapon_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_FLAMETONGUE_ATTACK)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + Player* player = eventInfo.GetActor()->ToPlayer(); + if (!player) + return false; + + Item* item = player->GetItemByGuid(GetAura()->GetCastItemGUID()); + if (!item || !item->IsEquipped()) + return false; + + WeaponAttackType attType = static_cast<WeaponAttackType>(player->GetAttackBySlot(item->GetSlot())); + if (attType != BASE_ATTACK && attType != OFF_ATTACK) + return false; + + if (((attType == BASE_ATTACK) && !(eventInfo.GetTypeMask() & PROC_FLAG_DONE_MAINHAND_ATTACK)) || + ((attType == OFF_ATTACK) && !(eventInfo.GetTypeMask() & PROC_FLAG_DONE_OFFHAND_ATTACK))) + return false; + + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Player* player = eventInfo.GetActor()->ToPlayer(); + Unit* target = eventInfo.GetProcTarget(); + WeaponAttackType attType = BASE_ATTACK; + if (eventInfo.GetTypeMask() & PROC_FLAG_DONE_OFFHAND_ATTACK) + attType = OFF_ATTACK; + + Item* item = ASSERT_NOTNULL(player->GetWeaponForAttack(attType)); + + float const basePoints = GetSpellInfo()->Effects[aurEff->GetEffIndex()].CalcValue(); + + // Flametongue max damage is normalized based on a 4.0 speed weapon + // Tooltip says max damage = BasePoints / 25, so BasePoints / 25 / 4 to get base damage per 1.0s AS + float fireDamage = basePoints / 100.0f; + float const attackSpeed = player->GetAttackTime(attType) / 1000.f; + fireDamage *= attackSpeed; + + // clip value between (BasePoints / 77) and (BasePoints / 25) as the tooltip indicates + RoundToInterval(fireDamage, basePoints / 77.0f, basePoints / 25.0f); + + // Calculate Spell Power scaling + float spellPowerBonus = player->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_FIRE) + target->SpellBaseDamageBonusTaken(SPELL_SCHOOL_MASK_FIRE); + float const spCoeff = 0.03811f; + spellPowerBonus *= spCoeff * attackSpeed; + + // All done, now proc damage + int32 amount = static_cast<int32>(fireDamage + spellPowerBonus); + player->CastCustomSpell(SPELL_SHAMAN_FLAMETONGUE_ATTACK, SPELLVALUE_BASE_POINT0, amount, target, true, item, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_sha_flametongue_weapon_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_sha_flametongue_weapon_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_flametongue_weapon_AuraScript(); + } +}; + +// -63373 - Frozen Power +class spell_sha_frozen_power : public SpellScriptLoader +{ + public: + spell_sha_frozen_power() : SpellScriptLoader("spell_sha_frozen_power") { } + + class spell_sha_frozen_power_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_frozen_power_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_FREEZE)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + if (!roll_chance_i(aurEff->GetAmount())) + return; + + Unit* caster = eventInfo.GetActor(); + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_FREEZE); + float minDistance(spellInfo->Effects[EFFECT_0].CalcValue(caster)); + + Unit* target = eventInfo.GetProcTarget(); + if (caster->GetDistance(target) < minDistance) + return; + + caster->CastSpell(target, SPELL_SHAMAN_FREEZE, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_frozen_power_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_frozen_power_AuraScript(); + } +}; + +// 63279 - Glyph of Earth Shield +class spell_sha_glyph_of_earth_shield : public SpellScriptLoader +{ + public: + spell_sha_glyph_of_earth_shield() : SpellScriptLoader("spell_sha_glyph_of_earth_shield") { } + + class spell_sha_glyph_of_earth_shield_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_glyph_of_earth_shield_AuraScript); + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + SpellInfo const* earthShield = eventInfo.GetSpellInfo(); + if (!earthShield) + return; + + AuraEffect* earthShieldEffect = eventInfo.GetProcTarget()->GetAuraEffect(earthShield->Id, EFFECT_0, eventInfo.GetActor()->GetGUID()); + if (!earthShieldEffect) + return; + + int32 amount = earthShieldEffect->GetAmount(); + AddPct(amount, aurEff->GetAmount()); + earthShieldEffect->SetAmount(amount); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_glyph_of_earth_shield_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_glyph_of_earth_shield_AuraScript(); + } +}; + +// 55440 - Glyph of Healing Wave +class spell_sha_glyph_of_healing_wave : public SpellScriptLoader +{ + public: + spell_sha_glyph_of_healing_wave() : SpellScriptLoader("spell_sha_glyph_of_healing_wave") { } + + class spell_sha_glyph_of_healing_wave_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_glyph_of_healing_wave_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_GLYPH_OF_HEALING_WAVE_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + if (caster == eventInfo.GetProcTarget()) + return; + + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + + int32 amount = CalculatePct(static_cast<int32>(healInfo->GetHeal()), aurEff->GetAmount()); + caster->CastCustomSpell(SPELL_SHAMAN_GLYPH_OF_HEALING_WAVE_HEAL, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_glyph_of_healing_wave_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_glyph_of_healing_wave_AuraScript(); + } +}; + +// 63280 - Glyph of Totem of Wrath +class spell_sha_glyph_of_totem_of_wrath : public SpellScriptLoader +{ + public: + spell_sha_glyph_of_totem_of_wrath() : SpellScriptLoader("spell_sha_glyph_of_totem_of_wrath") { } + + class spell_sha_glyph_of_totem_of_wrath_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_glyph_of_totem_of_wrath_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_TOTEM_OF_WRATH_SPELL_POWER)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + // Totem of Wrath shares family flags with other totems + // filter by spellIcon instead + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo || spellInfo->SpellIconID != SHAMAN_ICON_ID_TOTEM_OF_WRATH) + return false; + + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + + // Fire totem summon slot + Creature* totem = ObjectAccessor::GetCreature(*caster, caster->m_SummonSlot[1]); + if (!totem) + return; + + SpellInfo const* totemSpell = sSpellMgr->GetSpellInfo(totem->m_spells[0]); + if (!totemSpell) + return; + + int32 bp0 = CalculatePct(totemSpell->Effects[EFFECT_0].CalcValue(caster), aurEff->GetAmount()); + int32 bp1 = CalculatePct(totemSpell->Effects[EFFECT_1].CalcValue(caster), aurEff->GetAmount()); + caster->CastCustomSpell((Unit*)nullptr, SPELL_SHAMAN_TOTEM_OF_WRATH_SPELL_POWER, &bp0, &bp1, nullptr, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_sha_glyph_of_totem_of_wrath_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_sha_glyph_of_totem_of_wrath_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_glyph_of_totem_of_wrath_AuraScript(); + } +}; + // 52041, 52046, 52047, 52048, 52049, 52050, 58759, 58760, 58761 - Healing Stream Totem class spell_sha_healing_stream_totem : public SpellScriptLoader { @@ -597,6 +979,7 @@ class spell_sha_healing_stream_totem : public SpellScriptLoader int32 damage = GetEffectValue(); SpellInfo const* triggeringSpell = GetTriggeringSpell(); if (Unit* target = GetHitUnit()) + { if (Unit* caster = GetCaster()) { if (Unit* owner = caster->GetOwner()) @@ -614,8 +997,10 @@ class spell_sha_healing_stream_totem : public SpellScriptLoader damage = int32(target->SpellHealingBonusTaken(owner, triggeringSpell, damage, HEAL)); } - caster->CastCustomSpell(target, SPELL_SHAMAN_TOTEM_HEALING_STREAM_HEAL, &damage, 0, 0, true, 0, 0, GetOriginalCaster()->GetGUID()); + + caster->CastCustomSpell(SPELL_SHAMAN_TOTEM_HEALING_STREAM_HEAL, SPELLVALUE_BASE_POINT0, damage, target, true, nullptr, nullptr, GetOriginalCaster()->GetGUID()); } + } } void Register() override @@ -660,9 +1045,8 @@ class spell_sha_heroism : public SpellScriptLoader void Register() override { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_heroism_SpellScript::RemoveInvalidTargets, EFFECT_0, TARGET_UNIT_CASTER_AREA_RAID); - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_heroism_SpellScript::RemoveInvalidTargets, EFFECT_1, TARGET_UNIT_CASTER_AREA_RAID); - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_heroism_SpellScript::RemoveInvalidTargets, EFFECT_2, TARGET_UNIT_CASTER_AREA_RAID); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_heroism_SpellScript::RemoveInvalidTargets, EFFECT_ALL, TARGET_UNIT_CASTER_AREA_RAID); + AfterHit += SpellHitFn(spell_sha_heroism_SpellScript::ApplyDebuff); } }; @@ -673,6 +1057,125 @@ class spell_sha_heroism : public SpellScriptLoader } }; +// -16180 - Improved Water Shield +class spell_sha_imp_water_shield : public SpellScriptLoader +{ + public: + spell_sha_imp_water_shield() : SpellScriptLoader("spell_sha_imp_water_shield") { } + + class spell_sha_imp_water_shield_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_imp_water_shield_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return false; + + // If we're here, we've already passed initial aura roll + // So just chance based on 100% + + // Default chance for Healing Wave and Riptide + int32 chance = 100; + // Lesser Healing Wave - 0.6 of default + if (spellInfo->SpellFamilyFlags[0] & 0x00000080) + chance = 60; + // Chain heal - 0.3 of default + else if (spellInfo->SpellFamilyFlags[0] & 0x00000100) + chance = 30; + + if (!roll_chance_i(chance)) + return false; + + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + // Get Water Shield + AuraEffect const* waterShield = caster->GetAuraEffect(SPELL_AURA_PROC_TRIGGER_SPELL, SPELLFAMILY_SHAMAN, 0x00000000, 0x00000020, 0x00000000, caster->GetGUID()); + if (!waterShield) + return; + + uint32 spellId = waterShield->GetSpellInfo()->Effects[waterShield->GetEffIndex()].TriggerSpell; + caster->CastSpell((Unit*)nullptr, spellId, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_sha_imp_water_shield_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_sha_imp_water_shield_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_imp_water_shield_AuraScript(); + } +}; + +// -30675 - Lightning Overload +class spell_sha_lightning_overload : public SpellScriptLoader +{ + public: + spell_sha_lightning_overload() : SpellScriptLoader("spell_sha_lightning_overload") { } + + class spell_sha_lightning_overload_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_lightning_overload_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_LIGHTNING_BOLT_OVERLOAD_R1) || + !sSpellMgr->GetSpellInfo(SPELL_SHAMAN_CHAIN_LIGHTNING_OVERLOAD_R1)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; + + uint32 spellId; + + // Lightning Bolt + if (spellInfo->SpellFamilyFlags[0] & 0x00000001) + spellId = sSpellMgr->GetSpellWithRank(SPELL_SHAMAN_LIGHTNING_BOLT_OVERLOAD_R1, spellInfo->GetRank()); + // Chain Lightning + else + { + // Chain lightning has [LightOverload_Proc_Chance] / [Max_Number_of_Targets] chance to proc of each individual target hit. + // A maxed LO would have a 33% / 3 = 11% chance to proc of each target. + // LO chance was already "accounted" at the proc chance roll, now need to divide the chance by [Max_Number_of_Targets] + float chance = 100.0f / spellInfo->Effects[EFFECT_0].ChainTarget; + if (!roll_chance_f(chance)) + return; + + spellId = sSpellMgr->GetSpellWithRank(SPELL_SHAMAN_CHAIN_LIGHTNING_OVERLOAD_R1, spellInfo->GetRank()); + } + + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_lightning_overload_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_lightning_overload_AuraScript(); + } +}; + // 23551 - Lightning Shield T2 Bonus class spell_sha_item_lightning_shield : public SpellScriptLoader { @@ -693,7 +1196,7 @@ class spell_sha_item_lightning_shield : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - GetTarget()->CastSpell(eventInfo.GetProcTarget(), SPELL_SHAMAN_ITEM_LIGHTNING_SHIELD, true, NULL, aurEff); + GetTarget()->CastSpell(eventInfo.GetProcTarget(), SPELL_SHAMAN_ITEM_LIGHTNING_SHIELD, true, nullptr, aurEff); } void Register() override @@ -728,7 +1231,7 @@ class spell_sha_item_lightning_shield_trigger : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) { PreventDefaultAction(); - GetTarget()->CastSpell(GetTarget(), SPELL_SHAMAN_ITEM_LIGHTNING_SHIELD_DAMAGE, true, NULL, aurEff); + GetTarget()->CastSpell(GetTarget(), SPELL_SHAMAN_ITEM_LIGHTNING_SHIELD_DAMAGE, true, nullptr, aurEff); } void Register() override @@ -760,23 +1263,21 @@ class spell_sha_item_mana_surge : public SpellScriptLoader return true; } - bool CheckProc(ProcEventInfo& eventInfo) - { - return eventInfo.GetDamageInfo()->GetSpellInfo() != nullptr; - } - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - int32 mana = eventInfo.GetDamageInfo()->GetSpellInfo()->CalcPowerCost(GetTarget(), eventInfo.GetSchoolMask()); + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; + + int32 mana = spellInfo->CalcPowerCost(GetTarget(), eventInfo.GetSchoolMask()); int32 damage = CalculatePct(mana, 35); - GetTarget()->CastCustomSpell(SPELL_SHAMAN_ITEM_MANA_SURGE, SPELLVALUE_BASE_POINT0, damage, GetTarget(), true, NULL, aurEff); + GetTarget()->CastCustomSpell(SPELL_SHAMAN_ITEM_MANA_SURGE, SPELLVALUE_BASE_POINT0, damage, GetTarget(), true, nullptr, aurEff); } 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); } }; @@ -787,6 +1288,71 @@ class spell_sha_item_mana_surge : public SpellScriptLoader } }; +// 40463 - Shaman Tier 6 Trinket +class spell_sha_item_t6_trinket : public SpellScriptLoader +{ + public: + spell_sha_item_t6_trinket() : SpellScriptLoader("spell_sha_item_t6_trinket") { } + + class spell_sha_item_t6_trinket_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_item_t6_trinket_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ENERGY_SURGE) || + !sSpellMgr->GetSpellInfo(SPELL_SHAMAN_POWER_SURGE)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; + + uint32 spellId; + int32 chance; + + // Lesser Healing Wave + if (spellInfo->SpellFamilyFlags[0] & 0x00000080) + { + spellId = SPELL_SHAMAN_ENERGY_SURGE; + chance = 10; + } + // Lightning Bolt + else if (spellInfo->SpellFamilyFlags[0] & 0x00000001) + { + spellId = SPELL_SHAMAN_ENERGY_SURGE; + chance = 15; + } + // Stormstrike + else if (spellInfo->SpellFamilyFlags[1] & 0x00000010) + { + spellId = SPELL_SHAMAN_POWER_SURGE; + chance = 50; + } + else + return; + + if (roll_chance_i(chance)) + eventInfo.GetActor()->CastSpell((Unit*)nullptr, spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_item_t6_trinket_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_item_t6_trinket_AuraScript(); + } +}; + // 70811 - Item - Shaman T10 Elemental 2P Bonus class spell_sha_item_t10_elemental_2p_bonus : public SpellScriptLoader { @@ -844,11 +1410,12 @@ class spell_sha_lava_lash : public SpellScriptLoader { int32 damage = GetEffectValue(); int32 hitDamage = GetHitDamage(); - if (caster->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) + if (Item* offhand = caster->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) { // Damage is increased by 25% if your off-hand weapon is enchanted with Flametongue. - if (caster->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_SHAMAN, 0x200000, 0, 0)) - AddPct(hitDamage, damage); + if (AuraEffect const* aurEff = caster->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_SHAMAN, 0x200000, 0, 0)) + if (aurEff->GetBase()->GetCastItemGUID() == offhand->GetGUID()) + AddPct(hitDamage, damage); SetHitDamage(hitDamage); } } @@ -912,6 +1479,49 @@ public: } }; +// 53817 - Maelstrom Weapon +class spell_sha_maelstrom_weapon : public SpellScriptLoader +{ + public: + spell_sha_maelstrom_weapon() : SpellScriptLoader("spell_sha_maelstrom_weapon") { } + + class spell_sha_maelstrom_weapon_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_maelstrom_weapon_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_MAELSTROM_POWER) || + !sSpellMgr->GetSpellInfo(SPELL_SHAMAN_T10_ENHANCEMENT_4P_BONUS)) + return false; + return true; + } + + void HandleBonus(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetStackAmount() < GetSpellInfo()->StackAmount) + return; + + Unit* caster = GetUnitOwner(); + AuraEffect const* aurEff = caster->GetAuraEffect(SPELL_SHAMAN_T10_ENHANCEMENT_4P_BONUS, EFFECT_0); + if (!aurEff || !roll_chance_i(aurEff->GetAmount())) + return; + + caster->CastSpell((Unit*)nullptr, SPELL_SHAMAN_MAELSTROM_POWER, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_sha_maelstrom_weapon_AuraScript::HandleBonus, EFFECT_0, SPELL_AURA_ADD_PCT_MODIFIER, AURA_EFFECT_HANDLE_CHANGE_AMOUNT); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_maelstrom_weapon_AuraScript(); + } +}; + // 52031, 52033, 52034, 52035, 52036, 58778, 58779, 58780 - Mana Spring Totem class spell_sha_mana_spring_totem : public SpellScriptLoader { @@ -983,7 +1593,7 @@ class spell_sha_mana_tide_totem : public SpellScriptLoader effValue += dummy->GetAmount(); // Regenerate 6% of Total Mana Every 3 secs int32 effBasePoints0 = int32(CalculatePct(unitTarget->GetMaxPower(POWER_MANA), effValue)); - caster->CastCustomSpell(unitTarget, SPELL_SHAMAN_MANA_TIDE_TOTEM, &effBasePoints0, NULL, NULL, true, NULL, NULL, GetOriginalCaster()->GetGUID()); + caster->CastCustomSpell(SPELL_SHAMAN_MANA_TIDE_TOTEM, SPELLVALUE_BASE_POINT0, effBasePoints0, unitTarget, true, nullptr, nullptr, GetOriginalCaster()->GetGUID()); } } } @@ -1021,13 +1631,16 @@ public: void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + int32 healthpct = aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue(); // %s2 - the 30% threshold for health if (Unit* target = eventInfo.GetActionTarget()) { - if (target->HealthBelowPctDamaged(healthpct, eventInfo.GetDamageInfo()->GetDamage())) + if (target->HealthBelowPctDamaged(healthpct, damageInfo->GetDamage())) { - uint32 bp = CalculatePct(target->GetMaxHealth(), aurEff->GetAmount()); target->CastCustomSpell(SPELL_SHAMAN_NATURE_GUARDIAN, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff); @@ -1096,6 +1709,170 @@ class spell_sha_sentry_totem : public SpellScriptLoader } }; +// 30823 - Shamanistic Rage +class spell_sha_shamanistic_rage : public SpellScriptLoader +{ + public: + spell_sha_shamanistic_rage() : SpellScriptLoader("spell_sha_shamanistic_rage") { } + + class spell_sha_shamanistic_rage_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_shamanistic_rage_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_SHAMANISTIC_RAGE_PROC)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + + Unit* target = GetTarget(); + int32 amount = CalculatePct(static_cast<int32>(target->GetTotalAttackPowerValue(BASE_ATTACK)), aurEff->GetAmount()); + target->CastCustomSpell(SPELL_SHAMAN_SHAMANISTIC_RAGE_PROC, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_shamanistic_rage_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_shamanistic_rage_AuraScript(); + } +}; + +// 58877 - Spirit Hunt +class spell_sha_spirit_hunt : public SpellScriptLoader +{ + public: + spell_sha_spirit_hunt() : SpellScriptLoader("spell_sha_spirit_hunt") { } + + class spell_sha_spirit_hunt_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_spirit_hunt_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_SPIRIT_HUNT_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + Unit* caster = eventInfo.GetActor(); + Unit* target = caster->GetOwner(); + if (!target) + return; + + int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + caster->CastCustomSpell(SPELL_SHAMAN_SPIRIT_HUNT_HEAL, SPELLVALUE_BASE_POINT0, amount, caster, true, nullptr, aurEff); + caster->CastCustomSpell(SPELL_SHAMAN_SPIRIT_HUNT_HEAL, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_spirit_hunt_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_spirit_hunt_AuraScript(); + } +}; + +// -51525 - Static Shock +class spell_sha_static_shock : public SpellScriptLoader +{ + public: + spell_sha_static_shock() : SpellScriptLoader("spell_sha_static_shock") { } + + class spell_sha_static_shock_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_static_shock_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_LIGHTNING_SHIELD_DAMAGE_R1)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + + // Get Lightning Shield + AuraEffect const* lightningShield = caster->GetAuraEffect(SPELL_AURA_PROC_TRIGGER_SPELL, SPELLFAMILY_SHAMAN, 0x00000400, 0x00000000, 0x00000000, caster->GetGUID()); + if (!lightningShield) + return; + + uint32 spellId = sSpellMgr->GetSpellWithRank(SPELL_SHAMAN_LIGHTNING_SHIELD_DAMAGE_R1, lightningShield->GetSpellInfo()->GetRank()); + eventInfo.GetActor()->CastSpell(eventInfo.GetProcTarget(), spellId, true, nullptr, aurEff); + lightningShield->GetBase()->DropCharge(); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_static_shock_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_static_shock_AuraScript(); + } +}; + +// 55198 - Tidal Force +class spell_sha_tidal_force_dummy : public SpellScriptLoader +{ + public: + spell_sha_tidal_force_dummy() : SpellScriptLoader("spell_sha_tidal_force_dummy") { } + + class spell_sha_tidal_force_dummy_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_tidal_force_dummy_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_TIDAL_FORCE_CRIT)) + return false; + return true; + } + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->RemoveAuraFromStack(SPELL_SHAMAN_TIDAL_FORCE_CRIT); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_tidal_force_dummy_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_tidal_force_dummy_AuraScript(); + } +}; + // -51490 - Thunderstorm class spell_sha_thunderstorm : public SpellScriptLoader { @@ -1142,14 +1919,14 @@ public: return true; } - void HandleDummy(AuraEffect const* /*aurEff*/) + void HandleDummy(AuraEffect const* aurEff) { Unit* target = GetTarget(); for (uint8 i = SUMMON_SLOT_TOTEM; i < MAX_TOTEM_SLOT; ++i) if (!target->m_SummonSlot[i]) return; - target->CastSpell(target, SPELL_SHAMAN_TOTEMIC_MASTERY, true); + target->CastSpell(target, SPELL_SHAMAN_TOTEMIC_MASTERY, true, nullptr, aurEff); PreventDefaultAction(); } @@ -1165,30 +1942,406 @@ public: } }; +// 28823 - Totemic Power +class spell_sha_t3_6p_bonus : public SpellScriptLoader +{ + public: + spell_sha_t3_6p_bonus() : SpellScriptLoader("spell_sha_t3_6p_bonus") { } + + class spell_sha_t3_6p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_t3_6p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_TOTEMIC_POWER_ARMOR) || + !sSpellMgr->GetSpellInfo(SPELL_SHAMAN_TOTEMIC_POWER_ATTACK_POWER) || + !sSpellMgr->GetSpellInfo(SPELL_SHAMAN_TOTEMIC_POWER_SPELL_POWER) || + !sSpellMgr->GetSpellInfo(SPELL_SHAMAN_TOTEMIC_POWER_MP5)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + uint32 spellId; + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + + switch (target->getClass()) + { + case CLASS_PALADIN: + case CLASS_PRIEST: + case CLASS_SHAMAN: + case CLASS_DRUID: + spellId = SPELL_SHAMAN_TOTEMIC_POWER_MP5; + break; + case CLASS_MAGE: + case CLASS_WARLOCK: + spellId = SPELL_SHAMAN_TOTEMIC_POWER_SPELL_POWER; + break; + case CLASS_HUNTER: + case CLASS_ROGUE: + spellId = SPELL_SHAMAN_TOTEMIC_POWER_ATTACK_POWER; + break; + case CLASS_WARRIOR: + spellId = SPELL_SHAMAN_TOTEMIC_POWER_ARMOR; + break; + default: + return; + } + + caster->CastSpell(target, spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_t3_6p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_t3_6p_bonus_AuraScript(); + } +}; + +// 64928 - Item - Shaman T8 Elemental 4P Bonus +class spell_sha_t8_elemental_4p_bonus : public SpellScriptLoader +{ + public: + spell_sha_t8_elemental_4p_bonus() : SpellScriptLoader("spell_sha_t8_elemental_4p_bonus") { } + + class spell_sha_t8_elemental_4p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_t8_elemental_4p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ELECTRIFIED)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_ELECTRIFIED); + int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + amount /= spellInfo->GetMaxTicks(); + + // Add remaining ticks to damage done + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + amount += target->GetRemainingPeriodicAmount(caster->GetGUID(), SPELL_SHAMAN_ELECTRIFIED, SPELL_AURA_PERIODIC_DAMAGE); + + caster->CastCustomSpell(SPELL_SHAMAN_ELECTRIFIED, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_t8_elemental_4p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_t8_elemental_4p_bonus_AuraScript(); + } +}; + +// 67228 - Item - Shaman T9 Elemental 4P Bonus (Lava Burst) +class spell_sha_t9_elemental_4p_bonus : public SpellScriptLoader +{ + public: + spell_sha_t9_elemental_4p_bonus() : SpellScriptLoader("spell_sha_t9_elemental_4p_bonus") { } + + class spell_sha_t9_elemental_4p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_t9_elemental_4p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_LAVA_BURST_BONUS_DAMAGE)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_LAVA_BURST_BONUS_DAMAGE); + int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + amount /= spellInfo->GetMaxTicks(); + + // Add remaining ticks to damage done + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + amount += target->GetRemainingPeriodicAmount(caster->GetGUID(), SPELL_SHAMAN_LAVA_BURST_BONUS_DAMAGE, SPELL_AURA_PERIODIC_DAMAGE); + + caster->CastCustomSpell(SPELL_SHAMAN_LAVA_BURST_BONUS_DAMAGE, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_t9_elemental_4p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_t9_elemental_4p_bonus_AuraScript(); + } +}; + +// 70817 - Item - Shaman T10 Elemental 4P Bonus +class spell_sha_t10_elemental_4p_bonus : public SpellScriptLoader +{ + public: + spell_sha_t10_elemental_4p_bonus() : SpellScriptLoader("spell_sha_t10_elemental_4p_bonus") { } + + class spell_sha_t10_elemental_4p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_t10_elemental_4p_bonus_AuraScript); + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + + // try to find spell Flame Shock on the target + AuraEffect* flameShock = target->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_SHAMAN, 0x10000000, 0x00000000, 0x00000000, caster->GetGUID()); + if (!flameShock) + return; + + Aura* flameShockAura = flameShock->GetBase(); + + int32 maxDuration = flameShockAura->GetMaxDuration(); + int32 newDuration = flameShockAura->GetDuration() + aurEff->GetAmount() * IN_MILLISECONDS; + + flameShockAura->SetDuration(newDuration); + // is it blizzlike to change max duration for FS? + if (newDuration > maxDuration) + flameShockAura->SetMaxDuration(newDuration); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_t10_elemental_4p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_t10_elemental_4p_bonus_AuraScript(); + } +}; + +// 70808 - Item - Shaman T10 Restoration 4P Bonus +class spell_sha_t10_restoration_4p_bonus : public SpellScriptLoader +{ + public: + spell_sha_t10_restoration_4p_bonus() : SpellScriptLoader("spell_sha_t10_restoration_4p_bonus") { } + + class spell_sha_t10_restoration_4p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_t10_restoration_4p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_CHAINED_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; + + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_CHAINED_HEAL); + int32 amount = CalculatePct(static_cast<int32>(healInfo->GetHeal()), aurEff->GetAmount()); + amount /= spellInfo->GetMaxTicks(); + + // Add remaining ticks to healing done + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + amount += target->GetRemainingPeriodicAmount(caster->GetGUID(), SPELL_SHAMAN_CHAINED_HEAL, SPELL_AURA_PERIODIC_HEAL); + + caster->CastCustomSpell(SPELL_SHAMAN_CHAINED_HEAL, SPELLVALUE_BASE_POINT0, amount, target, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_t10_restoration_4p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_t10_restoration_4p_bonus_AuraScript(); + } +}; + +// 33757 - Windfury Weapon (Passive) +class spell_sha_windfury_weapon : public SpellScriptLoader +{ + public: + spell_sha_windfury_weapon() : SpellScriptLoader("spell_sha_windfury_weapon") { } + + class spell_sha_windfury_weapon_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_windfury_weapon_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_WINDFURY_WEAPON_R1) || + !sSpellMgr->GetSpellInfo(SPELL_SHAMAN_WINDFURY_ATTACK_MH) || + !sSpellMgr->GetSpellInfo(SPELL_SHAMAN_WINDFURY_ATTACK_OH)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + Player* player = eventInfo.GetActor()->ToPlayer(); + if (!player) + return false; + + Item* item = player->GetItemByGuid(GetAura()->GetCastItemGUID()); + if (!item || !item->IsEquipped()) + return false; + + WeaponAttackType attType = static_cast<WeaponAttackType>(player->GetAttackBySlot(item->GetSlot())); + if (attType != BASE_ATTACK && attType != OFF_ATTACK) + return false; + + if (((attType == BASE_ATTACK) && !(eventInfo.GetTypeMask() & PROC_FLAG_DONE_MAINHAND_ATTACK)) || + ((attType == OFF_ATTACK) && !(eventInfo.GetTypeMask() & PROC_FLAG_DONE_OFFHAND_ATTACK))) + return false; + + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Player* player = eventInfo.GetActor()->ToPlayer(); + + uint32 spellId = 0; + WeaponAttackType attType = BASE_ATTACK; + if (eventInfo.GetTypeMask() & PROC_FLAG_DONE_MAINHAND_ATTACK) + spellId = SPELL_SHAMAN_WINDFURY_ATTACK_MH; + + if (eventInfo.GetTypeMask() & PROC_FLAG_DONE_OFFHAND_ATTACK) + { + spellId = SPELL_SHAMAN_WINDFURY_ATTACK_OH; + attType = OFF_ATTACK; + } + + Item* item = ASSERT_NOTNULL(player->GetWeaponForAttack(attType)); + + int32 enchantId = static_cast<int32>(item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT)); + int32 extraAttackPower = 0; + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_WINDFURY_WEAPON_R1); + while (spellInfo) + { + if (spellInfo->Effects[EFFECT_0].MiscValue == enchantId) + { + extraAttackPower = spellInfo->Effects[EFFECT_1].CalcValue(player); + break; + } + spellInfo = spellInfo->GetNextRankSpell(); + } + + if (!extraAttackPower) + return; + + // Value gained from additional AP + int32 amount = static_cast<int32>(extraAttackPower / 14.f * player->GetAttackTime(attType) / 1000.f); + + // Attack twice + for (uint8 i = 0; i < 2; ++i) + player->CastCustomSpell(spellId, SPELLVALUE_BASE_POINT0, amount, eventInfo.GetProcTarget(), true, item, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_sha_windfury_weapon_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_sha_windfury_weapon_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_windfury_weapon_AuraScript(); + } +}; + void AddSC_shaman_spell_scripts() { + new spell_sha_ancestral_awakening(); new spell_sha_ancestral_awakening_proc(); new spell_sha_astral_shift(); + new spell_sha_astral_shift_aura(); new spell_sha_bloodlust(); new spell_sha_chain_heal(); new spell_sha_cleansing_totem_pulse(); 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(); + new spell_sha_frozen_power(); + new spell_sha_glyph_of_earth_shield(); + new spell_sha_glyph_of_healing_wave(); + new spell_sha_glyph_of_totem_of_wrath(); new spell_sha_healing_stream_totem(); new spell_sha_heroism(); + new spell_sha_imp_water_shield(); + new spell_sha_lightning_overload(); new spell_sha_item_lightning_shield(); new spell_sha_item_lightning_shield_trigger(); new spell_sha_item_mana_surge(); + new spell_sha_item_t6_trinket(); new spell_sha_item_t10_elemental_2p_bonus(); new spell_sha_lava_lash(); new spell_sha_lightning_shield(); + new spell_sha_maelstrom_weapon(); new spell_sha_mana_spring_totem(); new spell_sha_mana_tide_totem(); new spell_sha_nature_guardian(); new spell_sha_sentry_totem(); + new spell_sha_shamanistic_rage(); + new spell_sha_spirit_hunt(); + new spell_sha_static_shock(); + new spell_sha_tidal_force_dummy(); new spell_sha_thunderstorm(); new spell_sha_totemic_mastery(); + new spell_sha_t3_6p_bonus(); + new spell_sha_t8_elemental_4p_bonus(); + new spell_sha_t9_elemental_4p_bonus(); + new spell_sha_t10_elemental_4p_bonus(); + new spell_sha_t10_restoration_4p_bonus(); + new spell_sha_windfury_weapon(); } diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index 5e0074bf9f7..1a8252ad7ec 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -37,6 +37,7 @@ enum WarlockSpells SPELL_WARLOCK_DEMONIC_EMPOWERMENT_FELGUARD = 54508, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_FELHUNTER = 54509, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_IMP = 54444, + SPELL_WARLOCK_DEMONIC_PACT_PROC = 48090, SPELL_WARLOCK_FEL_SYNERGY_HEAL = 54181, SPELL_WARLOCK_GLYPH_OF_SHADOWFLAME = 63311, SPELL_WARLOCK_GLYPH_OF_SIPHON_LIFE = 63106, @@ -58,13 +59,30 @@ enum WarlockSpells SPELL_WARLOCK_NETHER_PROTECTION_NATURE = 54375, SPELL_WARLOCK_SOULSHATTER = 32835, SPELL_WARLOCK_SIPHON_LIFE_HEAL = 63106, - SPELL_WARLOCK_UNSTABLE_AFFLICTION_DISPEL = 31117 + SPELL_WARLOCK_UNSTABLE_AFFLICTION_DISPEL = 31117, + SPELL_WARLOCK_GLYPH_OF_LIFE_TAP_TRIGGERED = 63321, + SPELL_WARLOCK_SEED_OF_CORRUPTION_DAMAGE_R1 = 27285, + SPELL_WARLOCK_SEED_OF_CORRUPTION_GENERIC = 32865, + SPELL_WARLOCK_SHADOW_TRANCE = 17941, + SPELL_WARLOCK_SOUL_LEECH_HEAL = 30294, + SPELL_WARLOCK_IMP_SOUL_LEECH_R1 = 54117, + SPELL_WARLOCK_SOUL_LEECH_PET_MANA_1 = 54607, + SPELL_WARLOCK_SOUL_LEECH_PET_MANA_2 = 59118, + SPELL_WARLOCK_SOUL_LEECH_CASTER_MANA_1 = 54300, + SPELL_WARLOCK_SOUL_LEECH_CASTER_MANA_2 = 59117, + SPELL_REPLENISHMENT = 57669, + 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 }; enum WarlockSpellIcons { WARLOCK_ICON_ID_IMPROVED_LIFE_TAP = 208, - WARLOCK_ICON_ID_MANA_FEED = 1982 + WARLOCK_ICON_ID_MANA_FEED = 1982, + WARLOCK_ICON_ID_DEMONIC_PACT = 3220 }; // -710 - Banish @@ -250,6 +268,36 @@ class spell_warl_curse_of_doom : public SpellScriptLoader } }; +class spell_warl_decimation : public SpellScriptLoader +{ + public: + spell_warl_decimation() : SpellScriptLoader("spell_warl_decimation") { } + + class spell_warl_decimation_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_decimation_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + if (eventInfo.GetActionTarget()->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, spellInfo, eventInfo.GetActor())) + return true; + + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_warl_decimation_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warl_decimation_AuraScript(); + } +}; + // 48018 - Demonic Circle: Summon class spell_warl_demonic_circle_summon : public SpellScriptLoader { @@ -402,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 { @@ -416,6 +508,7 @@ class spell_warl_everlasting_affliction : public SpellScriptLoader { Unit* caster = GetCaster(); if (Unit* target = GetHitUnit()) + { // Refresh corruption on target if (AuraEffect* aur = target->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_WARLOCK, 0x2, 0, 0, caster->GetGUID())) { @@ -423,6 +516,7 @@ class spell_warl_everlasting_affliction : public SpellScriptLoader aur->CalculatePeriodic(caster, false, false); aur->GetBase()->RefreshDuration(true); } + } } void Register() override @@ -456,15 +550,19 @@ class spell_warl_fel_synergy : public SpellScriptLoader bool CheckProc(ProcEventInfo& eventInfo) { - return GetTarget()->GetGuardianPet() && eventInfo.GetDamageInfo()->GetDamage(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return false; + + return GetTarget()->GetGuardianPet() != nullptr; } void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - int32 heal = CalculatePct(int32(eventInfo.GetDamageInfo()->GetDamage()), aurEff->GetAmount()); - GetTarget()->CastCustomSpell(SPELL_WARLOCK_FEL_SYNERGY_HEAL, SPELLVALUE_BASE_POINT0, heal, (Unit*)NULL, true, NULL, aurEff); // TARGET_UNIT_PET + int32 heal = CalculatePct(static_cast<int32>(eventInfo.GetDamageInfo()->GetDamage()), aurEff->GetAmount()); + GetTarget()->CastCustomSpell(SPELL_WARLOCK_FEL_SYNERGY_HEAL, SPELLVALUE_BASE_POINT0, heal, (Unit*)nullptr, true, nullptr, aurEff); // TARGET_UNIT_PET } void Register() override @@ -480,6 +578,79 @@ class spell_warl_fel_synergy : public SpellScriptLoader } }; +// -18094 - Nightfall +// 56218 - Glyph of Corruption +class spell_warl_glyph_of_corruption_nightfall : public SpellScriptLoader +{ + public: + spell_warl_glyph_of_corruption_nightfall() : SpellScriptLoader("spell_warl_glyph_of_corruption_nightfall") { } + + class spell_warl_glyph_of_corruption_nightfall_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_glyph_of_corruption_nightfall_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_SHADOW_TRANCE)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + caster->CastSpell(caster, SPELL_WARLOCK_SHADOW_TRANCE, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_warl_glyph_of_corruption_nightfall_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warl_glyph_of_corruption_nightfall_AuraScript(); + } +}; + +// 63320 - Glyph of Life Tap +class spell_warl_glyph_of_life_tap : public SpellScriptLoader +{ +public: + spell_warl_glyph_of_life_tap() : SpellScriptLoader("spell_warl_glyph_of_life_tap") { } + + class spell_warl_glyph_of_life_tap_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_glyph_of_life_tap_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_GLYPH_OF_LIFE_TAP_TRIGGERED)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + caster->CastSpell(caster, SPELL_WARLOCK_GLYPH_OF_LIFE_TAP_TRIGGERED, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_warl_glyph_of_life_tap_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warl_glyph_of_life_tap_AuraScript(); + } +}; + // 63310 - Glyph of Shadowflame class spell_warl_glyph_of_shadowflame : public SpellScriptLoader { @@ -714,9 +885,9 @@ public: bool CheckProc(ProcEventInfo& eventInfo) { - if (eventInfo.GetDamageInfo()) + if (DamageInfo* damageInfo = eventInfo.GetDamageInfo()) { - switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask())) + switch (GetFirstSchoolInMask(damageInfo->GetSchoolMask())) { case SPELL_SCHOOL_HOLY: case SPELL_SCHOOL_FIRE: @@ -779,6 +950,55 @@ public: } }; +// 54909, 53646 - Demonic Pact +class spell_warl_demonic_pact : public SpellScriptLoader +{ + public: + spell_warl_demonic_pact() : SpellScriptLoader("spell_warl_demonic_pact") { } + + class spell_warl_demonic_pact_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_demonic_pact_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMONIC_PACT_PROC)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetActor() && eventInfo.GetActor()->IsPet(); + } + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + if (Unit* owner = eventInfo.GetActor()->GetOwner()) + { + if (AuraEffect* aurEff = owner->GetDummyAuraEffect(SPELLFAMILY_WARLOCK, WARLOCK_ICON_ID_DEMONIC_PACT, EFFECT_0)) + { + int32 bp0 = static_cast<int32>((aurEff->GetAmount() * owner->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_MAGIC) + 100.0f) / 100.0f); + owner->CastCustomSpell(SPELL_WARLOCK_DEMONIC_PACT_PROC, SPELLVALUE_BASE_POINT0, bp0, (Unit*)nullptr, true, nullptr, aurEff); + } + } + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_warl_demonic_pact_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_warl_demonic_pact_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warl_demonic_pact_AuraScript(); + } +}; + // 18541 - Ritual of Doom Effect class spell_warl_ritual_of_doom_effect : public SpellScriptLoader { @@ -807,6 +1027,47 @@ class spell_warl_ritual_of_doom_effect : public SpellScriptLoader } }; +// 6358 - Seduction +class spell_warl_seduction : public SpellScriptLoader +{ + public: + spell_warl_seduction() : SpellScriptLoader("spell_warl_seduction") { } + + class spell_warl_seduction_SpellScript : public SpellScript + { + PrepareSpellScript(spell_warl_seduction_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_GLYPH_OF_SUCCUBUS)) + return false; + return true; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* owner = GetCaster()->GetOwner(); + if (!owner || !owner->HasAura(SPELL_WARLOCK_GLYPH_OF_SUCCUBUS)) + return; + + Unit* target = GetHitUnit(); + target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE, ObjectGuid::Empty, target->GetAura(32409)); // SW:D shall not be removed. + target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); + target->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH); + } + + void Register() override + { + OnEffectLaunchTarget += SpellEffectFn(spell_warl_seduction_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_warl_seduction_SpellScript(); + } +}; + // -27285 - Seed of Corruption class spell_warl_seed_of_corruption : public SpellScriptLoader { @@ -835,6 +1096,118 @@ class spell_warl_seed_of_corruption : public SpellScriptLoader } }; +// -27243 - Seed of Corruption +class spell_warl_seed_of_corruption_dummy : public SpellScriptLoader +{ + public: + spell_warl_seed_of_corruption_dummy() : SpellScriptLoader("spell_warl_seed_of_corruption_dummy") { } + + class spell_warl_seed_of_corruption_dummy_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_seed_of_corruption_dummy_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_SEED_OF_CORRUPTION_DAMAGE_R1)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 amount = aurEff->GetAmount() - damageInfo->GetDamage(); + if (amount > 0) + { + const_cast<AuraEffect*>(aurEff)->SetAmount(amount); + if (!GetTarget()->HealthBelowPctDamaged(1, damageInfo->GetDamage())) + return; + } + + Remove(); + + Unit* caster = GetCaster(); + if (!caster) + return; + + uint32 spellId = sSpellMgr->GetSpellWithRank(SPELL_WARLOCK_SEED_OF_CORRUPTION_DAMAGE_R1, GetSpellInfo()->GetRank()); + caster->CastSpell(eventInfo.GetActionTarget(), spellId, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_warl_seed_of_corruption_dummy_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warl_seed_of_corruption_dummy_AuraScript(); + } +}; + +// 32863 - Seed of Corruption +// 36123 - Seed of Corruption +// 38252 - Seed of Corruption +// 39367 - Seed of Corruption +// 44141 - Seed of Corruption +// 70388 - Seed of Corruption +// Monster spells, triggered only on amount drop (not on death) +class spell_warl_seed_of_corruption_generic : public SpellScriptLoader +{ + public: + spell_warl_seed_of_corruption_generic() : SpellScriptLoader("spell_warl_seed_of_corruption_generic") { } + + class spell_warl_seed_of_corruption_generic_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_seed_of_corruption_generic_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_SEED_OF_CORRUPTION_GENERIC)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 amount = aurEff->GetAmount() - damageInfo->GetDamage(); + if (amount > 0) + { + const_cast<AuraEffect*>(aurEff)->SetAmount(amount); + return; + } + + Remove(); + + Unit* caster = GetCaster(); + if (!caster) + return; + + caster->CastSpell(eventInfo.GetActionTarget(), SPELL_WARLOCK_SEED_OF_CORRUPTION_GENERIC, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_warl_seed_of_corruption_generic_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warl_seed_of_corruption_generic_AuraScript(); + } +}; + // -7235 - Shadow Ward class spell_warl_shadow_ward : public SpellScriptLoader { @@ -893,19 +1266,23 @@ class spell_warl_siphon_life : public SpellScriptLoader bool CheckProc(ProcEventInfo& eventInfo) { - return eventInfo.GetDamageInfo()->GetDamage() && GetTarget()->IsAlive(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return false; + + return GetTarget()->IsAlive(); } void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount())); + int32 amount = CalculatePct(static_cast<int32>(eventInfo.GetDamageInfo()->GetDamage()), aurEff->GetAmount()); // Glyph of Siphon Life if (AuraEffect const* glyph = GetTarget()->GetAuraEffect(SPELL_WARLOCK_GLYPH_OF_SIPHON_LIFE, EFFECT_0)) AddPct(amount, glyph->GetAmount()); - GetTarget()->CastCustomSpell(SPELL_WARLOCK_SIPHON_LIFE_HEAL, SPELLVALUE_BASE_POINT0, amount, GetTarget(), true, NULL, aurEff); + GetTarget()->CastCustomSpell(SPELL_WARLOCK_SIPHON_LIFE_HEAL, SPELLVALUE_BASE_POINT0, amount, GetTarget(), true, nullptr, aurEff); } void Register() override @@ -921,6 +1298,71 @@ class spell_warl_siphon_life : public SpellScriptLoader } }; +// -30293 - Soul Leech +class spell_warl_soul_leech : public SpellScriptLoader +{ + public: + spell_warl_soul_leech() : SpellScriptLoader("spell_warl_soul_leech") { } + + class spell_warl_soul_leech_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_soul_leech_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_SOUL_LEECH_HEAL) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_IMP_SOUL_LEECH_R1) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_SOUL_LEECH_PET_MANA_1) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_SOUL_LEECH_PET_MANA_2) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_SOUL_LEECH_CASTER_MANA_1) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_SOUL_LEECH_CASTER_MANA_2) || + !sSpellMgr->GetSpellInfo(SPELL_REPLENISHMENT)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + static uint32 const casterMana[2] = { SPELL_WARLOCK_SOUL_LEECH_CASTER_MANA_1, SPELL_WARLOCK_SOUL_LEECH_CASTER_MANA_2 }; + static uint32 const petMana[2] = { SPELL_WARLOCK_SOUL_LEECH_PET_MANA_1, SPELL_WARLOCK_SOUL_LEECH_PET_MANA_2 }; + + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + Unit* caster = eventInfo.GetActor(); + int32 bp = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + caster->CastCustomSpell(SPELL_WARLOCK_SOUL_LEECH_HEAL, SPELLVALUE_BASE_POINT0, bp, caster, true); + + // Improved Soul Leech code below + AuraEffect const* impSoulLeech = GetTarget()->GetAuraEffectOfRankedSpell(SPELL_WARLOCK_IMP_SOUL_LEECH_R1, EFFECT_1, aurEff->GetCasterGUID()); + if (!impSoulLeech) + return; + + uint8 impSoulLeechRank = impSoulLeech->GetSpellInfo()->GetRank(); + uint32 selfSpellId = casterMana[impSoulLeechRank - 1]; + uint32 petSpellId = petMana[impSoulLeechRank - 1]; + + caster->CastSpell((Unit*)nullptr, selfSpellId, true, nullptr, aurEff); + caster->CastSpell((Unit*)nullptr, petSpellId, true, nullptr, aurEff); + + if (roll_chance_i(impSoulLeech->GetAmount())) + caster->CastSpell((Unit*)nullptr, SPELL_REPLENISHMENT, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_warl_soul_leech_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warl_soul_leech_AuraScript(); + } +}; + // 29858 - Soulshatter class spell_warl_soulshatter : public SpellScriptLoader { @@ -960,6 +1402,45 @@ class spell_warl_soulshatter : public SpellScriptLoader } }; +// 37377 - Shadowflame +// 39437 - Shadowflame Hellfire and RoF +template <uint32 TriggerSpellId> +class spell_warl_t4_2p_bonus : public SpellScriptLoader +{ + public: + spell_warl_t4_2p_bonus(char const* ScriptName) : SpellScriptLoader(ScriptName) { } + + template <uint32 Trigger> + class spell_warl_t4_2p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_t4_2p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(Trigger)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + caster->CastSpell(caster, Trigger, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_warl_t4_2p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warl_t4_2p_bonus_AuraScript<TriggerSpellId>(); + } +}; + // -30108 - Unstable Affliction class spell_warl_unstable_affliction : public SpellScriptLoader { @@ -1005,20 +1486,31 @@ void AddSC_warlock_spell_scripts() new spell_warl_banish(); new spell_warl_create_healthstone(); new spell_warl_curse_of_doom(); + new spell_warl_decimation(); new spell_warl_demonic_circle_summon(); 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(); new spell_warl_glyph_of_shadowflame(); new spell_warl_haunt(); new spell_warl_health_funnel(); + new spell_warl_glyph_of_corruption_nightfall(); new spell_warl_life_tap(); new spell_warl_nether_protection(); new spell_warl_ritual_of_doom_effect(); + new spell_warl_seduction(); new spell_warl_seed_of_corruption(); + new spell_warl_seed_of_corruption_dummy(); + new spell_warl_seed_of_corruption_generic(); new spell_warl_shadow_ward(); new spell_warl_siphon_life(); + new spell_warl_soul_leech(); new spell_warl_soulshatter(); + new spell_warl_t4_2p_bonus<SPELL_WARLOCK_FLAMESHADOW>("spell_warl_t4_2p_bonus_shadow"); + new spell_warl_t4_2p_bonus<SPELL_WARLOCK_SHADOWFLAME>("spell_warl_t4_2p_bonus_fire"); new spell_warl_unstable_affliction(); } diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp index ea9ccc956e5..81fb4ec2457 100644 --- a/src/server/scripts/Spells/spell_warrior.cpp +++ b/src/server/scripts/Spells/spell_warrior.cpp @@ -32,6 +32,7 @@ enum WarriorSpells SPELL_WARRIOR_BLADESTORM_PERIODIC_WHIRLWIND = 50622, SPELL_WARRIOR_BLOODTHIRST = 23885, SPELL_WARRIOR_BLOODTHIRST_DAMAGE = 23881, + SPELL_WARRIOR_BLOODSURGE_R1 = 29723, SPELL_WARRIOR_CHARGE = 34846, SPELL_WARRIOR_DAMAGE_SHIELD_DAMAGE = 59653, SPELL_WARRIOR_DEEP_WOUNDS_RANK_1 = 12162, @@ -39,6 +40,8 @@ enum WarriorSpells SPELL_WARRIOR_DEEP_WOUNDS_RANK_3 = 12868, SPELL_WARRIOR_DEEP_WOUNDS_PERIODIC = 12721, SPELL_WARRIOR_EXECUTE = 20647, + SPELL_WARRIOR_EXECUTE_GCD_REDUCED = 71069, + SPELL_WARRIOR_EXTRA_CHARGE = 70849, SPELL_WARRIOR_GLYPH_OF_EXECUTION = 58367, SPELL_WARRIOR_GLYPH_OF_VIGILANCE = 63326, SPELL_WARRIOR_JUGGERNAUT_CRIT_BONUS_BUFF = 65156, @@ -46,6 +49,8 @@ enum WarriorSpells SPELL_WARRIOR_LAST_STAND_TRIGGERED = 12976, SPELL_WARRIOR_RETALIATION_DAMAGE = 22858, SPELL_WARRIOR_SLAM = 50783, + SPELL_WARRIOR_SLAM_GCD_REDUCED = 71072, + SPELL_WARRIOR_SUDDEN_DEATH_R1 = 46913, SPELL_WARRIOR_SUNDER_ARMOR = 58567, SPELL_WARRIOR_SWEEPING_STRIKES_EXTRA_ATTACK_1 = 12723, SPELL_WARRIOR_SWEEPING_STRIKES_EXTRA_ATTACK_2 = 26654, @@ -55,7 +60,13 @@ enum WarriorSpells SPELL_WARRIOR_UNRELENTING_ASSAULT_TRIGGER_1 = 64849, SPELL_WARRIOR_UNRELENTING_ASSAULT_TRIGGER_2 = 64850, SPELL_WARRIOR_VIGILANCE_PROC = 50725, - SPELL_WARRIOR_VIGILANCE_REDIRECT_THREAT = 59665 + SPELL_WARRIOR_VIGILANCE_REDIRECT_THREAT = 59665, + SPELL_WARRIOR_IMPROVED_SPELL_REFLECTION_TRIGGER = 59725, + SPELL_WARRIOR_SECOND_WIND_TRIGGER_1 = 29841, + SPELL_WARRIOR_SECOND_WIND_TRIGGER_2 = 29842, + SPELL_WARRIOR_GLYPH_OF_BLOCKING = 58374, + SPELL_WARRIOR_STOICISM = 70845, + SPELL_WARRIOR_T10_MELEE_4P_BONUS = 70847 }; enum WarriorSpellIcons @@ -69,6 +80,7 @@ enum MiscSpells SPELL_PALADIN_GREATER_BLESSING_OF_SANCTUARY = 25899, SPELL_PRIEST_RENEWED_HOPE = 63944, SPELL_GEN_DAMAGE_REDUCTION_AURA = 68066, + SPELL_CATEGORY_SHIELD_SLAM = 1209 }; // 23881 - Bloodthirst @@ -258,7 +270,10 @@ class spell_warr_deep_wounds : public SpellScriptLoader bool Validate(SpellInfo const* /*spellInfo*/) override { - if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_RANK_1) || !sSpellMgr->GetSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_RANK_2) || !sSpellMgr->GetSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_RANK_3)) + if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_RANK_1) || + !sSpellMgr->GetSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_RANK_2) || + !sSpellMgr->GetSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_RANK_3) || + !sSpellMgr->GetSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_PERIODIC)) return false; return true; } @@ -272,15 +287,11 @@ class spell_warr_deep_wounds : public SpellScriptLoader ApplyPct(damage, 16 * GetSpellInfo()->GetRank()); SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_PERIODIC); - uint32 ticks = uint32(spellInfo->GetDuration()) / spellInfo->Effects[EFFECT_0].Amplitude; + damage /= spellInfo->GetMaxTicks(); // Add remaining ticks to damage done - if (AuraEffect const* aurEff = target->GetAuraEffect(SPELL_WARRIOR_DEEP_WOUNDS_PERIODIC, EFFECT_0, caster->GetGUID())) - damage += (aurEff->GetAmount() + aurEff->GetBonusAmount()) * aurEff->GetDonePct() * int32(ticks - aurEff->GetTickNumber()); - - damage /= int32(ticks); - - caster->CastCustomSpell(target, SPELL_WARRIOR_DEEP_WOUNDS_PERIODIC, &damage, NULL, NULL, true); + damage += target->GetRemainingPeriodicAmount(caster->GetGUID(), SPELL_WARRIOR_DEEP_WOUNDS_PERIODIC, SPELL_AURA_PERIODIC_DAMAGE); + caster->CastCustomSpell(SPELL_WARRIOR_DEEP_WOUNDS_PERIODIC, SPELLVALUE_BASE_POINT0, damage, target, true); } } @@ -296,6 +307,60 @@ class spell_warr_deep_wounds : public SpellScriptLoader } }; +// -12834 - Deep Wounds Aura +class spell_warr_deep_wounds_aura : public SpellScriptLoader +{ + public: + spell_warr_deep_wounds_aura() : SpellScriptLoader("spell_warr_deep_wounds_aura") { } + + class spell_warr_deep_wounds_aura_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warr_deep_wounds_aura_AuraScript); + + bool Validate(SpellInfo const* spellInfo) override + { + if (!sSpellMgr->GetSpellInfo(spellInfo->Effects[EFFECT_0].TriggerSpell)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo) + return false; + + return eventInfo.GetActor()->GetTypeId() == TYPEID_PLAYER; + } + + void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* actor = eventInfo.GetActor(); + float damage = 0.f; + + if (eventInfo.GetDamageInfo()->GetAttackType() == OFF_ATTACK) + damage = (actor->GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE) + actor->GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE)) / 2.f; + else + damage = (actor->GetFloatValue(UNIT_FIELD_MINDAMAGE) + actor->GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2.f; + + actor->CastCustomSpell(GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, SPELLVALUE_BASE_POINT0, int32(damage), eventInfo.GetProcTarget(), true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_warr_deep_wounds_aura_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_warr_deep_wounds_aura_AuraScript::OnProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warr_deep_wounds_aura_AuraScript(); + } +}; + // -5308 - Execute class spell_warr_execute : public SpellScriptLoader { @@ -352,6 +417,94 @@ class spell_warr_execute : public SpellScriptLoader } }; +// -29723 - Bloodsurge +// -46913 - Sudden Death +class spell_warr_extra_proc : public SpellScriptLoader +{ + public: + spell_warr_extra_proc() : SpellScriptLoader("spell_warr_extra_proc") { } + + class spell_warr_extra_proc_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warr_extra_proc_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_T10_MELEE_4P_BONUS) || + !sSpellMgr->GetSpellInfo(SPELL_WARRIOR_EXTRA_CHARGE) || + !sSpellMgr->GetSpellInfo(SPELL_WARRIOR_SLAM_GCD_REDUCED) || + !sSpellMgr->GetSpellInfo(SPELL_WARRIOR_EXECUTE_GCD_REDUCED)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + Unit* target = GetTarget(); + AuraEffect const* bonusAurEff = target->GetAuraEffect(SPELL_WARRIOR_T10_MELEE_4P_BONUS, EFFECT_0); + if (!bonusAurEff) + return; + + if (!roll_chance_i(bonusAurEff->GetAmount())) + return; + + target->CastSpell((Unit*)nullptr, SPELL_WARRIOR_EXTRA_CHARGE, true, nullptr, aurEff); + + SpellInfo const* auraInfo = aurEff->GetSpellInfo(); + if (auraInfo->IsRankOf(sSpellMgr->AssertSpellInfo(SPELL_WARRIOR_BLOODSURGE_R1))) + target->CastSpell((Unit*)nullptr, SPELL_WARRIOR_SLAM_GCD_REDUCED, true, nullptr, aurEff); + else if (auraInfo->IsRankOf(sSpellMgr->AssertSpellInfo(SPELL_WARRIOR_SUDDEN_DEATH_R1))) + target->CastSpell((Unit*)nullptr, SPELL_WARRIOR_EXECUTE_GCD_REDUCED, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_warr_extra_proc_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warr_extra_proc_AuraScript(); + } +}; + +// 58375 - Glyph of Blocking +class spell_warr_glyph_of_blocking : public SpellScriptLoader +{ + public: + spell_warr_glyph_of_blocking() : SpellScriptLoader("spell_warr_glyph_of_blocking") { } + + class spell_warr_glyph_of_blocking_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warr_glyph_of_blocking_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_GLYPH_OF_BLOCKING)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + caster->CastSpell(caster, SPELL_WARRIOR_GLYPH_OF_BLOCKING, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_warr_glyph_of_blocking_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warr_glyph_of_blocking_AuraScript(); + } +}; + // 58387 - Glyph of Sunder Armor class spell_warr_glyph_of_sunder_armor : public SpellScriptLoader { @@ -388,31 +541,39 @@ class spell_warr_glyph_of_sunder_armor : public SpellScriptLoader } }; -// 59725 - Improved Spell Reflection +// -59088 - Improved Spell Reflection class spell_warr_improved_spell_reflection : public SpellScriptLoader { public: spell_warr_improved_spell_reflection() : SpellScriptLoader("spell_warr_improved_spell_reflection") { } - class spell_warr_improved_spell_reflection_SpellScript : public SpellScript + class spell_warr_improved_spell_reflection_AuraScript : public AuraScript { - PrepareSpellScript(spell_warr_improved_spell_reflection_SpellScript); + PrepareAuraScript(spell_warr_improved_spell_reflection_AuraScript); - void FilterTargets(std::list<WorldObject*>& unitList) + bool Validate(SpellInfo const* /*spellInfo*/) override { - if (GetCaster()) - unitList.remove(GetCaster()); + if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_IMPROVED_SPELL_REFLECTION_TRIGGER)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + caster->CastCustomSpell(SPELL_WARRIOR_IMPROVED_SPELL_REFLECTION_TRIGGER, SPELLVALUE_MAX_TARGETS, aurEff->GetAmount(), caster, true, nullptr, aurEff); } void Register() override { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_warr_improved_spell_reflection_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_CASTER_AREA_PARTY); + OnEffectProc += AuraEffectProcFn(spell_warr_improved_spell_reflection_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); } }; - SpellScript* GetSpellScript() const override + AuraScript* GetAuraScript() const override { - return new spell_warr_improved_spell_reflection_SpellScript(); + return new spell_warr_improved_spell_reflection_AuraScript(); } }; @@ -444,6 +605,44 @@ class spell_warr_intimidating_shout : public SpellScriptLoader } }; +// 70844 - Item - Warrior T10 Protection 4P Bonus +class spell_warr_item_t10_prot_4p_bonus : public SpellScriptLoader +{ + public: + spell_warr_item_t10_prot_4p_bonus() : SpellScriptLoader("spell_warr_item_t10_prot_4p_bonus") { } + + class spell_warr_item_t10_prot_4p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warr_item_t10_prot_4p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_STOICISM)) + return false; + return true; + } + + void HandleProc(ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* target = eventInfo.GetActionTarget(); + int32 bp0 = CalculatePct(target->GetMaxHealth(), GetSpellInfo()->Effects[EFFECT_1].CalcValue()); + target->CastCustomSpell(SPELL_WARRIOR_STOICISM, SPELLVALUE_BASE_POINT0, bp0, (Unit*)nullptr, true); + } + + void Register() override + { + OnProc += AuraProcFn(spell_warr_item_t10_prot_4p_bonus_AuraScript::HandleProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warr_item_t10_prot_4p_bonus_AuraScript(); + } +}; + // 12975 - Last Stand class spell_warr_last_stand : public SpellScriptLoader { @@ -606,6 +805,56 @@ class spell_warr_retaliation : public SpellScriptLoader } }; +// -29834 - Second Wind +class spell_warr_second_wind : public SpellScriptLoader +{ + public: + spell_warr_second_wind() : SpellScriptLoader("spell_warr_second_wind") { } + + class spell_warr_second_wind_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warr_second_wind_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_SECOND_WIND_TRIGGER_1) || + !sSpellMgr->GetSpellInfo(SPELL_WARRIOR_SECOND_WIND_TRIGGER_2)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return false; + + return (spellInfo->GetAllEffectsMechanicMask() & ((1 << MECHANIC_ROOT) | (1 << MECHANIC_STUN))) != 0; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + static uint32 const triggeredSpells[2] = { SPELL_WARRIOR_SECOND_WIND_TRIGGER_1, SPELL_WARRIOR_SECOND_WIND_TRIGGER_2 }; + + PreventDefaultAction(); + Unit* caster = eventInfo.GetActionTarget(); + uint32 spellId = triggeredSpells[GetSpellInfo()->GetRank() - 1]; + caster->CastSpell(caster, spellId, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_warr_second_wind_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_warr_second_wind_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warr_second_wind_AuraScript(); + } +}; + // 64380, 65941 - Shattering Throw class spell_warr_shattering_throw : public SpellScriptLoader { @@ -705,18 +954,18 @@ class spell_warr_sweeping_strikes : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - if (eventInfo.GetDamageInfo()) + if (DamageInfo* damageInfo = eventInfo.GetDamageInfo()) { - SpellInfo const* spellInfo = eventInfo.GetDamageInfo()->GetSpellInfo(); + SpellInfo const* spellInfo = damageInfo->GetSpellInfo(); if (spellInfo && (spellInfo->Id == SPELL_WARRIOR_BLADESTORM_PERIODIC_WHIRLWIND || (spellInfo->Id == SPELL_WARRIOR_EXECUTE && !_procTarget->HasAuraState(AURA_STATE_HEALTHLESS_20_PERCENT)))) { // If triggered by Execute (while target is not under 20% hp) or Bladestorm deals normalized weapon damage - GetTarget()->CastSpell(_procTarget, SPELL_WARRIOR_SWEEPING_STRIKES_EXTRA_ATTACK_2, true, NULL, aurEff); + GetTarget()->CastSpell(_procTarget, SPELL_WARRIOR_SWEEPING_STRIKES_EXTRA_ATTACK_2, true, nullptr, aurEff); } else { - int32 damage = eventInfo.GetDamageInfo()->GetDamage(); - GetTarget()->CastCustomSpell(SPELL_WARRIOR_SWEEPING_STRIKES_EXTRA_ATTACK_1, SPELLVALUE_BASE_POINT0, damage, _procTarget, true, NULL, aurEff); + int32 damage = damageInfo->GetDamage(); + GetTarget()->CastCustomSpell(SPELL_WARRIOR_SWEEPING_STRIKES_EXTRA_ATTACK_1, SPELLVALUE_BASE_POINT0, damage, _procTarget, true, nullptr, aurEff); } } } @@ -737,6 +986,101 @@ class spell_warr_sweeping_strikes : public SpellScriptLoader } }; +// -46951 - Sword and Board +class spell_warr_sword_and_board : public SpellScriptLoader +{ + public: + spell_warr_sword_and_board() : SpellScriptLoader("spell_warr_sword_and_board") { } + + class spell_warr_sword_and_board_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warr_sword_and_board_AuraScript); + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + // Remove cooldown on Shield Slam + GetTarget()->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool + { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); + return spellInfo && spellInfo->GetCategory() == SPELL_CATEGORY_SHIELD_SLAM; + }, true); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_warr_sword_and_board_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warr_sword_and_board_AuraScript(); + } +}; + +// 28845 - Cheat Death +class spell_warr_t3_prot_8p_bonus : public SpellScriptLoader +{ + public: + spell_warr_t3_prot_8p_bonus() : SpellScriptLoader("spell_warr_t3_prot_8p_bonus") { } + + class spell_warr_t3_prot_8p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warr_t3_prot_8p_bonus_AuraScript); + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetActionTarget()->HealthBelowPct(20)) + return true; + + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (damageInfo && damageInfo->GetDamage()) + if (GetTarget()->HealthBelowPctDamaged(20, damageInfo->GetDamage())) + return true; + + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_warr_t3_prot_8p_bonus_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warr_t3_prot_8p_bonus_AuraScript(); + } +}; + +// 32216 - Victorious +class spell_warr_victorious : public SpellScriptLoader +{ + public: + spell_warr_victorious() : SpellScriptLoader("spell_warr_victorious") { } + + class spell_warr_victorious_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warr_victorious_AuraScript); + + void HandleDummy(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + // Prevent console log + PreventDefaultAction(); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_warr_victorious_AuraScript::HandleDummy, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warr_victorious_AuraScript(); + } +}; + // 50720 - Vigilance class spell_warr_vigilance : public SpellScriptLoader { @@ -871,17 +1215,25 @@ void AddSC_warrior_spell_scripts() new spell_warr_concussion_blow(); new spell_warr_damage_shield(); new spell_warr_deep_wounds(); + new spell_warr_deep_wounds_aura(); new spell_warr_execute(); + new spell_warr_extra_proc(); + new spell_warr_glyph_of_blocking(); new spell_warr_glyph_of_sunder_armor(); new spell_warr_improved_spell_reflection(); new spell_warr_intimidating_shout(); + new spell_warr_item_t10_prot_4p_bonus(); new spell_warr_last_stand(); new spell_warr_overpower(); new spell_warr_rend(); new spell_warr_retaliation(); + new spell_warr_second_wind(); new spell_warr_shattering_throw(); new spell_warr_slam(); new spell_warr_sweeping_strikes(); + new spell_warr_sword_and_board(); + new spell_warr_t3_prot_8p_bonus(); + new spell_warr_victorious(); new spell_warr_vigilance(); new spell_warr_vigilance_trigger(); } diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp index 514a9b7eead..91166294355 100644 --- a/src/server/scripts/World/go_scripts.cpp +++ b/src/server/scripts/World/go_scripts.cpp @@ -51,6 +51,7 @@ EndContentData */ #include "Spell.h" #include "Player.h" #include "WorldSession.h" +#include "GameEventMgr.h" /*###### ## go_cat_figurine @@ -1262,6 +1263,312 @@ class go_toy_train_set : public GameObjectScript } }; +/*#### +## go_brewfest_music +####*/ + +enum BrewfestMusic +{ + EVENT_BREWFESTDWARF01 = 11810, // 1.35 min + EVENT_BREWFESTDWARF02 = 11812, // 1.55 min + EVENT_BREWFESTDWARF03 = 11813, // 0.23 min + EVENT_BREWFESTGOBLIN01 = 11811, // 1.08 min + EVENT_BREWFESTGOBLIN02 = 11814, // 1.33 min + EVENT_BREWFESTGOBLIN03 = 11815 // 0.28 min +}; + +// These are in seconds +enum BrewfestMusicTime +{ + EVENT_BREWFESTDWARF01_TIME = 95000, + EVENT_BREWFESTDWARF02_TIME = 155000, + EVENT_BREWFESTDWARF03_TIME = 23000, + EVENT_BREWFESTGOBLIN01_TIME = 68000, + EVENT_BREWFESTGOBLIN02_TIME = 93000, + EVENT_BREWFESTGOBLIN03_TIME = 28000 +}; + +enum BrewfestMusicAreas +{ + SILVERMOON = 3430, // Horde + UNDERCITY = 1497, + ORGRIMMAR_1 = 1296, + ORGRIMMAR_2 = 14, + THUNDERBLUFF = 1638, + IRONFORGE_1 = 809, // Alliance + IRONFORGE_2 = 1, + STORMWIND = 12, + EXODAR = 3557, + DARNASSUS = 1657, + SHATTRATH = 3703 // General +}; + +enum BrewfestMusicEvents +{ + EVENT_BM_SELECT_MUSIC = 1, + EVENT_BM_START_MUSIC = 2 +}; + +class go_brewfest_music : public GameObjectScript +{ +public: + go_brewfest_music() : GameObjectScript("go_brewfest_music") { } + + struct go_brewfest_musicAI : public GameObjectAI + { + uint32 rnd = 0; + uint32 musicTime = 1000; + + go_brewfest_musicAI(GameObject* go) : GameObjectAI(go) + { + _events.ScheduleEvent(EVENT_BM_SELECT_MUSIC, 1000); + _events.ScheduleEvent(EVENT_BM_START_MUSIC, 2000); + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BM_SELECT_MUSIC: + if (!IsHolidayActive(HOLIDAY_BREWFEST)) // Check if Brewfest is active + break; + rnd = urand(0, 2); // Select random music sample + _events.ScheduleEvent(EVENT_BM_SELECT_MUSIC, musicTime); // Select new song music after play time is over + break; + case EVENT_BM_START_MUSIC: + if (!IsHolidayActive(HOLIDAY_BREWFEST)) // Check if Brewfest is active + break; + // Check if gob is correct area, play music, set time of music + if (go->GetAreaId() == SILVERMOON || go->GetAreaId() == UNDERCITY || go->GetAreaId() == ORGRIMMAR_1 || go->GetAreaId() == ORGRIMMAR_2 || go->GetAreaId() == THUNDERBLUFF || go->GetAreaId() == SHATTRATH) + { + if (rnd == 0) + { + go->PlayDirectMusic(EVENT_BREWFESTGOBLIN01); + musicTime = EVENT_BREWFESTGOBLIN01_TIME; + } + else if (rnd == 1) + { + go->PlayDirectMusic(EVENT_BREWFESTGOBLIN02); + musicTime = EVENT_BREWFESTGOBLIN02_TIME; + } + else + { + go->PlayDirectMusic(EVENT_BREWFESTGOBLIN03); + musicTime = EVENT_BREWFESTGOBLIN03_TIME; + } + } + if (go->GetAreaId() == IRONFORGE_1 || go->GetAreaId() == IRONFORGE_2 || go->GetAreaId() == STORMWIND || go->GetAreaId() == EXODAR || go->GetAreaId() == DARNASSUS || go->GetAreaId() == SHATTRATH) + { + if (rnd == 0) + { + go->PlayDirectMusic(EVENT_BREWFESTDWARF01); + musicTime = EVENT_BREWFESTDWARF01_TIME; + } + else if (rnd == 1) + { + go->PlayDirectMusic(EVENT_BREWFESTDWARF02); + musicTime = EVENT_BREWFESTDWARF02_TIME; + } + else + { + go->PlayDirectMusic(EVENT_BREWFESTDWARF03); + musicTime = EVENT_BREWFESTDWARF03_TIME; + } + } + _events.ScheduleEvent(EVENT_BM_START_MUSIC, 5000); // Every 5 second's SMSG_PLAY_MUSIC packet (PlayDirectMusic) is pushed to the client + break; + default: + break; + } + } + } + private: + EventMap _events; + }; + + GameObjectAI* GetAI(GameObject* go) const override + { + return new go_brewfest_musicAI(go); + } +}; + +/*#### +## go_midsummer_music +####*/ + +enum MidsummerMusic +{ + EVENTMIDSUMMERFIREFESTIVAL_A = 12319, // 1.08 min + EVENTMIDSUMMERFIREFESTIVAL_H = 12325, // 1.12 min +}; + +enum MidsummerMusicEvents +{ + EVENT_MM_START_MUSIC = 1 +}; + +class go_midsummer_music : public GameObjectScript +{ +public: + go_midsummer_music() : GameObjectScript("go_midsummer_music") { } + + struct go_midsummer_musicAI : public GameObjectAI + { + go_midsummer_musicAI(GameObject* go) : GameObjectAI(go) + { + _events.ScheduleEvent(EVENT_MM_START_MUSIC, 1000); + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_MM_START_MUSIC: + { + 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; + } + default: + break; + } + } + } + private: + EventMap _events; + }; + + GameObjectAI* GetAI(GameObject* go) const override + { + return new go_midsummer_musicAI(go); + } +}; + +/*#### +## go_darkmoon_faire_music +####*/ + +enum DarkmoonFaireMusic +{ + MUSIC_DARKMOON_FAIRE_MUSIC = 8440 +}; + +enum DarkmoonFaireMusicEvents +{ + EVENT_DFM_START_MUSIC = 1 +}; + +class go_darkmoon_faire_music : public GameObjectScript +{ +public: + go_darkmoon_faire_music() : GameObjectScript("go_darkmoon_faire_music") { } + + struct go_darkmoon_faire_musicAI : public GameObjectAI + { + go_darkmoon_faire_musicAI(GameObject* go) : GameObjectAI(go) + { + _events.ScheduleEvent(EVENT_DFM_START_MUSIC, 1000); + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_DFM_START_MUSIC: + if (!IsHolidayActive(HOLIDAY_DARKMOON_FAIRE_ELWYNN) || !IsHolidayActive(HOLIDAY_DARKMOON_FAIRE_THUNDER) || !IsHolidayActive(HOLIDAY_DARKMOON_FAIRE_SHATTRATH)) + break; + go->PlayDirectMusic(MUSIC_DARKMOON_FAIRE_MUSIC); + _events.ScheduleEvent(EVENT_DFM_START_MUSIC, 5000); // Every 5 second's SMSG_PLAY_MUSIC packet (PlayDirectMusic) is pushed to the client (sniffed value) + break; + default: + break; + } + } + } + private: + EventMap _events; + }; + + GameObjectAI* GetAI(GameObject* go) const override + { + return new go_darkmoon_faire_musicAI(go); + } +}; + +/*#### +## go_pirate_day_music +####*/ + +enum PirateDayMusic +{ + MUSIC_PIRATE_DAY_MUSIC = 12845 +}; + +enum PirateDayMusicEvents +{ + EVENT_PDM_START_MUSIC = 1 +}; + +class go_pirate_day_music : public GameObjectScript +{ +public: + go_pirate_day_music() : GameObjectScript("go_pirate_day_music") { } + + struct go_pirate_day_musicAI : public GameObjectAI + { + go_pirate_day_musicAI(GameObject* go) : GameObjectAI(go) + { + _events.ScheduleEvent(EVENT_PDM_START_MUSIC, 1000); + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_PDM_START_MUSIC: + if (!IsHolidayActive(HOLIDAY_PIRATES_DAY)) + break; + go->PlayDirectMusic(MUSIC_PIRATE_DAY_MUSIC); + _events.ScheduleEvent(EVENT_PDM_START_MUSIC, 5000); // Every 5 second's SMSG_PLAY_MUSIC packet (PlayDirectMusic) is pushed to the client (sniffed value) + break; + default: + break; + } + } + } + private: + EventMap _events; + }; + + GameObjectAI* GetAI(GameObject* go) const override + { + return new go_pirate_day_musicAI(go); + } +}; + void AddSC_go_scripts() { new go_cat_figurine(); @@ -1299,4 +1606,8 @@ void AddSC_go_scripts() new go_midsummer_bonfire(); new go_midsummer_ribbon_pole(); new go_toy_train_set(); + new go_brewfest_music(); + new go_midsummer_music(); + new go_darkmoon_faire_music(); + new go_pirate_day_music(); } diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index a598f017eb6..8dd606f8b66 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -243,47 +243,6 @@ public: } }; -/*###### -## npc_lunaclaw_spirit -######*/ - -enum LunaclawSpirit -{ - QUEST_BODY_HEART_A = 6001, - QUEST_BODY_HEART_H = 6002, - - TEXT_ID_DEFAULT = 4714, - TEXT_ID_PROGRESS = 4715 -}; - -#define GOSSIP_ITEM_GRANT "You have thought well, spirit. I ask you to grant me the strength of your body and the strength of your heart." - -class npc_lunaclaw_spirit : public CreatureScript -{ -public: - npc_lunaclaw_spirit() : CreatureScript("npc_lunaclaw_spirit") { } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (player->GetQuestStatus(QUEST_BODY_HEART_A) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(QUEST_BODY_HEART_H) == QUEST_STATUS_INCOMPLETE) - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_GRANT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - - SendGossipMenuFor(player, TEXT_ID_DEFAULT, creature->GetGUID()); - return true; - } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF + 1) - { - SendGossipMenuFor(player, TEXT_ID_PROGRESS, creature->GetGUID()); - player->AreaExploredOrEventHappens(player->GetTeam() == ALLIANCE ? QUEST_BODY_HEART_A : QUEST_BODY_HEART_H); - } - return true; - } -}; - /*######## # npc_chicken_cluck #########*/ @@ -2644,7 +2603,6 @@ public: void AddSC_npcs_special() { new npc_air_force_bots(); - new npc_lunaclaw_spirit(); new npc_chicken_cluck(); new npc_dancing_flames(); new npc_torch_tossing_target_bunny_controller(); diff --git a/src/server/shared/Dynamic/LinkedList.h b/src/server/shared/Dynamic/LinkedList.h index 94f67cac909..1c99144db0e 100644 --- a/src/server/shared/Dynamic/LinkedList.h +++ b/src/server/shared/Dynamic/LinkedList.h @@ -32,18 +32,18 @@ class LinkedListElement LinkedListElement* iNext; LinkedListElement* iPrev; + public: - LinkedListElement() : iNext(NULL), iPrev(NULL) { } - virtual ~LinkedListElement() { delink(); } + LinkedListElement() : iNext(nullptr), iPrev(nullptr) { } - bool hasNext() const { return(iNext && iNext->iNext != NULL); } - bool hasPrev() const { return(iPrev && iPrev->iPrev != NULL); } - bool isInList() const { return(iNext != NULL && iPrev != NULL); } + bool hasNext() const { return (iNext && iNext->iNext != nullptr); } + bool hasPrev() const { return (iPrev && iPrev->iPrev != nullptr); } + bool isInList() const { return (iNext != nullptr && iPrev != nullptr); } - LinkedListElement * next() { return hasNext() ? iNext : NULL; } - LinkedListElement const* next() const { return hasNext() ? iNext : NULL; } - LinkedListElement * prev() { return hasPrev() ? iPrev : NULL; } - LinkedListElement const* prev() const { return hasPrev() ? iPrev : NULL; } + LinkedListElement * next() { return hasNext() ? iNext : nullptr; } + LinkedListElement const* next() const { return hasNext() ? iNext : nullptr; } + LinkedListElement * prev() { return hasPrev() ? iPrev : nullptr; } + LinkedListElement const* prev() const { return hasPrev() ? iPrev : nullptr; } LinkedListElement * nocheck_next() { return iNext; } LinkedListElement const* nocheck_next() const { return iNext; } @@ -52,10 +52,13 @@ class LinkedListElement void delink() { - if (isInList()) - { - iNext->iPrev = iPrev; iPrev->iNext = iNext; iNext = NULL; iPrev = NULL; - } + if (!isInList()) + return; + + iNext->iPrev = iPrev; + iPrev->iNext = iNext; + iNext = nullptr; + iPrev = nullptr; } void insertBefore(LinkedListElement* pElem) @@ -75,8 +78,14 @@ class LinkedListElement } private: - LinkedListElement(LinkedListElement const&); - LinkedListElement& operator=(LinkedListElement const&); + LinkedListElement(LinkedListElement const&) = delete; + LinkedListElement& operator=(LinkedListElement const&) = delete; + + protected: + ~LinkedListElement() + { + delink(); + } }; //============================================ @@ -97,15 +106,13 @@ class LinkedListHead iLast.iPrev = &iFirst; } - virtual ~LinkedListHead() { } - bool isEmpty() const { return(!iFirst.iNext->isInList()); } - LinkedListElement * getFirst() { return(isEmpty() ? NULL : iFirst.iNext); } - LinkedListElement const* getFirst() const { return(isEmpty() ? NULL : iFirst.iNext); } + LinkedListElement * getFirst() { return (isEmpty() ? nullptr : iFirst.iNext); } + LinkedListElement const* getFirst() const { return (isEmpty() ? nullptr : iFirst.iNext); } - LinkedListElement * getLast() { return(isEmpty() ? NULL : iLast.iPrev); } - LinkedListElement const* getLast() const { return(isEmpty() ? NULL : iLast.iPrev); } + LinkedListElement * getLast() { return(isEmpty() ? nullptr : iLast.iPrev); } + LinkedListElement const* getLast() const { return(isEmpty() ? nullptr : iLast.iPrev); } void insertFirst(LinkedListElement* pElem) { @@ -248,8 +255,11 @@ class LinkedListHead typedef Iterator<LinkedListElement> iterator; private: - LinkedListHead(LinkedListHead const&); - LinkedListHead& operator=(LinkedListHead const&); + LinkedListHead(LinkedListHead const&) = delete; + LinkedListHead& operator=(LinkedListHead const&) = delete; + + protected: + ~LinkedListHead() { } }; //============================================ diff --git a/src/server/shared/Dynamic/LinkedReference/RefManager.h b/src/server/shared/Dynamic/LinkedReference/RefManager.h index 9dbab4f338e..3c716e3c6b7 100644 --- a/src/server/shared/Dynamic/LinkedReference/RefManager.h +++ b/src/server/shared/Dynamic/LinkedReference/RefManager.h @@ -23,31 +23,29 @@ #include "Dynamic/LinkedList.h" #include "Dynamic/LinkedReference/Reference.h" -template <class TO, class FROM> class RefManager : public LinkedListHead +template <class TO, class FROM> +class RefManager : public LinkedListHead { public: - typedef LinkedListHead::Iterator< Reference<TO, FROM> > iterator; + typedef LinkedListHead::Iterator<Reference<TO, FROM>> iterator; RefManager() { } - virtual ~RefManager() { clearReferences(); } - Reference<TO, FROM>* getFirst() { return ((Reference<TO, FROM>*) LinkedListHead::getFirst()); } - Reference<TO, FROM> const* getFirst() const { return ((Reference<TO, FROM> const*) LinkedListHead::getFirst()); } - Reference<TO, FROM>* getLast() { return ((Reference<TO, FROM>*) LinkedListHead::getLast()); } - Reference<TO, FROM> const* getLast() const { return ((Reference<TO, FROM> const*) LinkedListHead::getLast()); } + Reference<TO, FROM>* getFirst() { return static_cast<Reference<TO, FROM>*>(LinkedListHead::getFirst()); } + + Reference<TO, FROM> const* getFirst() const { return static_cast<Reference<TO, FROM> const*>(LinkedListHead::getFirst()); } iterator begin() { return iterator(getFirst()); } - iterator end() { return iterator(NULL); } - iterator rbegin() { return iterator(getLast()); } - iterator rend() { return iterator(NULL); } + iterator end() { return iterator(nullptr); } + + virtual ~RefManager() + { + clearReferences(); + } void clearReferences() { - LinkedListElement* ref; - while ((ref = getFirst()) != NULL) - { - ((Reference<TO, FROM>*) ref)->invalidate(); - ref->delink(); // the delink might be already done by invalidate(), but doing it here again does not hurt and insures an empty list - } + while (Reference<TO, FROM>* ref = getFirst()) + ref->invalidate(); } }; diff --git a/src/server/shared/Dynamic/LinkedReference/Reference.h b/src/server/shared/Dynamic/LinkedReference/Reference.h index 4a473b0f2ac..0dcf91fb052 100644 --- a/src/server/shared/Dynamic/LinkedReference/Reference.h +++ b/src/server/shared/Dynamic/LinkedReference/Reference.h @@ -29,6 +29,7 @@ template <class TO, class FROM> class Reference : public LinkedListElement private: TO* iRefTo; FROM* iRefFrom; + protected: // Tell our refTo (target) object that we have a link virtual void targetObjectBuildLink() = 0; @@ -90,14 +91,14 @@ template <class TO, class FROM> class Reference : public LinkedListElement Reference<TO, FROM> * nocheck_prev() { return((Reference<TO, FROM> *) LinkedListElement::nocheck_prev()); } Reference<TO, FROM> const* nocheck_prev() const { return((Reference<TO, FROM> const*) LinkedListElement::nocheck_prev()); } - TO* operator ->() const { return iRefTo; } + TO* operator->() const { return iRefTo; } TO* getTarget() const { return iRefTo; } FROM* GetSource() const { return iRefFrom; } private: - Reference(Reference const&); - Reference& operator=(Reference const&); + Reference(Reference const&) = delete; + Reference& operator=(Reference const&) = delete; }; //===================================================== diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 0513f2b0ab4..1d173057e6f 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1018,6 +1018,15 @@ Quests.IgnoreAutoAccept = 0 Quests.IgnoreAutoComplete = 0 # +# Quests.DailyResetTime +# Description: Hour of the day when daily quest reset occurs. +# Range: 0-23 +# Default: 3 - (3:00 AM, Blizzlike) +# + +Quests.DailyResetTime = 3 + +# # Guild.EventLogRecordsCount # Description: Number of log entries for guild events that are stored per guild. Old entries # will be overwritten if the number of log entries exceed the configured value. @@ -1682,10 +1691,10 @@ Creature.MovingStopTimeForPlayer = 180000 # Description: Chat protection from creating fake messages using a lot spaces or other # invisible symbols. Not applied to the addon language, but may break old # addons that use normal languages for sending data to other clients. -# Default: 0 - (Disabled) -# 1 - (Enabled) +# Default: 1 - (Enabled, Blizzlike) +# 0 - (Disabled) -ChatFakeMessagePreventing = 0 +ChatFakeMessagePreventing = 1 # # ChatStrictLinkChecking.Severity @@ -3061,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 |
