diff options
author | Treeston <treeston.mmoc@gmail.com> | 2018-08-27 19:08:17 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2021-10-23 02:14:04 +0200 |
commit | 392a644dc8562ea54b9c185a2776fceb1006e2cd (patch) | |
tree | eec4d04885e582c39f0b88ea1cf0d87b4bb1015a | |
parent | 395f58d651c7c370522ead6ba9a3c684d91e3a66 (diff) |
Core/Threat: Threat system adjustments:
* Online states are now re-evaluated before victim update instead of continuously. Closes #22226. Tagging #21501.
* Victim update now happens every 1s as opposed to every server tick unless current target goes away.
* Suppressed threat is no longer re-established until the victim gains additional threat (by hitting the target, for instance).
* Assistance threat is now split between non-controlled units threatened by target, as opposed to all units threatened by target.
(cherry picked from commit 5cea572a9ad524c6f28ff8519bee61d1ff4357d0)
11 files changed, 218 insertions, 185 deletions
diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp index 8ac3828b8cb..6d5273ac91b 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -52,20 +52,23 @@ void ThreatReference::ScaleThreat(float factor) HeapNotifyDecreased(); } -void ThreatReference::UpdateOnlineState() +void ThreatReference::UpdateOffline() { - OnlineState onlineState = SelectOnlineState(); - if (onlineState == _online) + bool const shouldBeOffline = ShouldBeOffline(); + if (shouldBeOffline == IsOffline()) return; - bool increase = (onlineState > _online); - _online = onlineState; - if (increase) - HeapNotifyIncreased(); - else - HeapNotifyDecreased(); - if (!IsAvailable()) - _owner->GetThreatManager().SendRemoveToClients(_victim); + if (shouldBeOffline) + { + _online = ONLINE_STATE_OFFLINE; + HeapNotifyDecreased(); + _mgr.SendRemoveToClients(_victim); + } + else + { + _online = ShouldBeSuppressed() ? ONLINE_STATE_SUPPRESSED : ONLINE_STATE_ONLINE; + HeapNotifyIncreased(); + } } /*static*/ bool ThreatReference::FlagsAllowFighting(Unit const* a, Unit const* b) @@ -85,25 +88,24 @@ void ThreatReference::UpdateOnlineState() return true; } -ThreatReference::OnlineState ThreatReference::SelectOnlineState() +bool ThreatReference::ShouldBeOffline() const { - // first, check all offline conditions - if (!_owner->CanSeeOrDetect(_victim)) // not in map/phase, or stealth/invis - return ONLINE_STATE_OFFLINE; - if (_victim->HasUnitState(UNIT_STATE_DIED)) // feign death - return ONLINE_STATE_OFFLINE; + if (!_owner->CanSeeOrDetect(_victim)) + return true; + if (!_owner->_IsTargetAcceptable(_victim) || !_owner->CanCreatureAttack(_victim)) + return true; if (!FlagsAllowFighting(_owner, _victim) || !FlagsAllowFighting(_victim, _owner)) - return ONLINE_STATE_OFFLINE; - if (_owner->IsAIEnabled() && !_owner->GetAI()->CanAIAttack(_victim)) - return ONLINE_STATE_OFFLINE; - // next, check suppression (immunity to chosen melee attack school) + return true; + return false; +} + +bool ThreatReference::ShouldBeSuppressed() const +{ if (_victim->IsImmunedToDamage(_owner->GetMeleeDamageSchoolMask())) - return ONLINE_STATE_SUPPRESSED; - // or any form of CC that will break on damage - disorient, polymorph, blind etc + return true; if (_victim->HasBreakableByDamageCrowdControlAura()) - return ONLINE_STATE_SUPPRESSED; - // no suppression - we're online - return ONLINE_STATE_ONLINE; + return true; + return false; } void ThreatReference::UpdateTauntState(TauntState state) @@ -123,9 +125,14 @@ void ThreatReference::UpdateTauntState(TauntState state) HeapNotifyIncreased(); } -void ThreatReference::ClearThreat(bool sendRemove) +void ThreatReference::ClearThreat() +{ + _mgr.ClearThreat(this); +} + +void ThreatReference::UnregisterAndFree() { - _owner->GetThreatManager().PurgeThreatListRef(_victim->GetGUID(), sendRemove); + _owner->GetThreatManager().PurgeThreatListRef(_victim->GetGUID()); _victim->GetThreatManager().PurgeThreatenedByMeRef(_owner->GetGUID()); delete this; } @@ -148,7 +155,7 @@ void ThreatReference::ClearThreat(bool sendRemove) return true; } -ThreatManager::ThreatManager(Unit* owner) : _owner(owner), _ownerCanHaveThreatList(false), _ownerEngaged(false), _updateClientTimer(CLIENT_THREAT_UPDATE_INTERVAL), _currentVictimRef(nullptr), _fixateRef(nullptr) +ThreatManager::ThreatManager(Unit* owner) : _owner(owner), _ownerCanHaveThreatList(false), _ownerEngaged(false), _updateTimer(THREAT_UPDATE_INTERVAL), _currentVictimRef(nullptr), _fixateRef(nullptr) { for (int8 i = 0; i < MAX_SPELL_SCHOOL; ++i) _singleSchoolModifiers[i] = 1.0f; @@ -170,18 +177,25 @@ void ThreatManager::Update(uint32 tdiff) { if (!CanHaveThreatList() || !IsEngaged()) return; - if (_updateClientTimer <= tdiff) + if (_updateTimer <= tdiff) { - _updateClientTimer = CLIENT_THREAT_UPDATE_INTERVAL; - SendThreatListToClients(); + UpdateVictim(); + _updateTimer = THREAT_UPDATE_INTERVAL; } else - _updateClientTimer -= tdiff; + _updateTimer -= tdiff; +} + +Unit* ThreatManager::GetCurrentVictim() +{ + if (!_currentVictimRef || _currentVictimRef->ShouldBeOffline()) + UpdateVictim(); + return const_cast<ThreatManager const*>(this)->GetCurrentVictim(); } Unit* ThreatManager::GetCurrentVictim() const { - if (_currentVictimRef) + if (_currentVictimRef && !_currentVictimRef->ShouldBeOffline()) return _currentVictimRef->GetVictim(); return nullptr; } @@ -194,22 +208,6 @@ Unit* ThreatManager::GetAnyTarget() const return nullptr; } -Unit* ThreatManager::SelectVictim() -{ - if (_sortedThreatList.empty()) - return nullptr; - - ThreatReference const* newVictimRef = ReselectVictim(); - if (newVictimRef != _currentVictimRef) - { - if (newVictimRef) - SendNewVictimToClients(newVictimRef); - - _currentVictimRef = newVictimRef; - } - return newVictimRef ? newVictimRef->GetVictim() : nullptr; -} - bool ThreatManager::IsThreatListEmpty(bool includeOffline) const { if (includeOffline) @@ -265,14 +263,11 @@ bool ThreatManager::IsThreateningTo(ObjectGuid const& who, bool includeOffline) } bool ThreatManager::IsThreateningTo(Unit const* who, bool includeOffline) const { return IsThreateningTo(who->GetGUID(), includeOffline); } -void ThreatManager::UpdateOnlineStates(bool meThreateningOthers, bool othersThreateningMe) +void ThreatManager::EvaluateSuppressed() { - if (othersThreateningMe) - for (auto const& pair : _myThreatListEntries) - pair.second->UpdateOnlineState(); - if (meThreateningOthers) - for (auto const& pair : _threatenedByMe) - pair.second->UpdateOnlineState(); + for (auto const& pair : _threatenedByMe) + if (pair.second->IsOnline() && pair.second->ShouldBeSuppressed()) + pair.second->_online = ThreatReference::ONLINE_STATE_SUPPRESSED; } static void SaveCreatureHomePositionIfNeed(Creature* c) @@ -367,7 +362,15 @@ void ThreatManager::AddThreat(Unit* target, float amount, SpellInfo const* spell auto it = _myThreatListEntries.find(target->GetGUID()); if (it != _myThreatListEntries.end()) { - it->second->AddThreat(amount); + ThreatReference* const ref = it->second; + + // causing threat causes SUPPRESSED threat states to stop being suppressed + if (ref->GetOnlineState() == ThreatReference::ONLINE_STATE_SUPPRESSED) + if (!ref->ShouldBeSuppressed()) + ref->_online = ThreatReference::ONLINE_STATE_ONLINE; + + if (ref->IsOnline()) + ref->AddThreat(amount); return; } @@ -377,10 +380,11 @@ void ThreatManager::AddThreat(Unit* target, float amount, SpellInfo const* spell target->GetThreatManager().PutThreatenedByMeRef(_owner->GetGUID(), ref); if (!_ownerEngaged) { + Creature* cOwner = ASSERT_NOTNULL(_owner->ToCreature()); // if we got here the owner can have a threat list, and must be a creature! _ownerEngaged = true; - Creature* cOwner = _owner->ToCreature(); - ASSERT(cOwner); // if we got here the owner can have a threat list, and must be a creature! + UpdateVictim(); + SaveCreatureHomePositionIfNeed(cOwner); if (CreatureAI* ownerAI = cOwner->AI()) ownerAI->JustEngagedWith(target); @@ -437,14 +441,22 @@ void ThreatManager::TauntUpdate() void ThreatManager::ResetAllThreat() { for (auto const& pair : _myThreatListEntries) - pair.second->SetThreat(0.0f); + pair.second->ScaleThreat(0.0f); } void ThreatManager::ClearThreat(Unit* target) { auto it = _myThreatListEntries.find(target->GetGUID()); if (it != _myThreatListEntries.end()) - it->second->ClearThreat(); + ClearThreat(it->second); +} + +void ThreatManager::ClearThreat(ThreatReference* ref) +{ + SendRemoveToClients(ref->_victim); + ref->UnregisterAndFree(); + if (!_currentVictimRef) + UpdateVictim(); } void ThreatManager::ClearAllThreat() @@ -455,7 +467,7 @@ void ThreatManager::ClearAllThreat() SendClearAllThreatToClients(); do - _myThreatListEntries.begin()->second->ClearThreat(false); + _myThreatListEntries.begin()->second->UnregisterAndFree(); while (!_myThreatListEntries.empty()); } @@ -481,8 +493,24 @@ Unit* ThreatManager::GetFixateTarget() const return nullptr; } +void ThreatManager::UpdateVictim() +{ + ThreatReference const* const newVictim = ReselectVictim(); + bool const newHighest = (newVictim != _currentVictimRef); + + _currentVictimRef = newVictim; + SendThreatListToClients(newVictim && newHighest); + +} + ThreatReference const* ThreatManager::ReselectVictim() { + if (_sortedThreatList.empty()) + return nullptr; + + for (auto const& pair : _myThreatListEntries) + pair.second->UpdateOffline(); + // fixated target is always preferred if (_fixateRef && _fixateRef->IsAvailable()) return _fixateRef; @@ -526,7 +554,7 @@ ThreatReference const* ThreatManager::ReselectVictim() ++it; } // we should have found the old victim at some point in the loop above, so execution should never get to this point - ASSERT(false && "Current victim not found in sorted threat list even though it has a reference - manager desync!"); + ASSERT(false, "Current victim not found in sorted threat list even though it has a reference - manager desync!"); return nullptr; } @@ -596,46 +624,41 @@ ThreatReference const* ThreatManager::ReselectVictim() return threat; } -void ThreatManager::SendClearAllThreatToClients() const -{ - WorldPackets::Combat::ThreatClear threatClear; - threatClear.UnitGUID = _owner->GetGUID(); - _owner->SendMessageToSet(threatClear.Write(), false); -} - -void ThreatManager::SendThreatListToClients() const -{ - WorldPackets::Combat::ThreatUpdate threatUpdate; - threatUpdate.UnitGUID = _owner->GetGUID(); - threatUpdate.ThreatList.reserve(_sortedThreatList.size()); - for (ThreatReference const* ref : _sortedThreatList) - { - if (!ref->IsAvailable()) // @todo check if suppressed threat should get sent for bubble/iceblock/hop etc - continue; - - WorldPackets::Combat::ThreatInfo threatInfo; - threatInfo.UnitGUID = ref->GetVictim()->GetGUID(); - threatInfo.Threat = int64(ref->GetThreat() * 100); - threatUpdate.ThreatList.push_back(threatInfo); - } - _owner->SendMessageToSet(threatUpdate.Write(), false); -} - void ThreatManager::ForwardThreatForAssistingMe(Unit* assistant, float baseAmount, SpellInfo const* spell, bool ignoreModifiers) { if (spell && spell->HasAttribute(SPELL_ATTR1_NO_THREAT)) // shortcut, none of the calls would do anything return; if (_threatenedByMe.empty()) return; - float const perTarget = baseAmount / _threatenedByMe.size(); // Threat is divided evenly among all targets (LibThreat sourced) + + std::vector<Creature*> canBeThreatened, cannotBeThreatened; for (auto const& pair : _threatenedByMe) - pair.second->GetOwner()->GetThreatManager().AddThreat(assistant, perTarget, spell, ignoreModifiers); + { + Creature* owner = pair.second->GetOwner(); + if (!owner->HasBreakableByDamageCrowdControlAura()) + canBeThreatened.push_back(owner); + else + cannotBeThreatened.push_back(owner); + } + + if (!canBeThreatened.empty()) // targets under CC cannot gain assist threat - split evenly among the rest + { + float const perTarget = baseAmount / canBeThreatened.size(); + for (Creature* threatened : canBeThreatened) + threatened->GetThreatManager().AddThreat(assistant, perTarget, spell, ignoreModifiers); + } + + for (Creature* threatened : cannotBeThreatened) + threatened->GetThreatManager().AddThreat(assistant, 0.0f, spell, true); } void ThreatManager::RemoveMeFromThreatLists() { while (!_threatenedByMe.empty()) - _threatenedByMe.begin()->second->ClearThreat(); + { + auto& ref = _threatenedByMe.begin()->second; + ref->_mgr.ClearThreat(_owner); + } } void ThreatManager::UpdateMyTempModifiers() @@ -644,11 +667,19 @@ void ThreatManager::UpdateMyTempModifiers() for (AuraEffect const* eff : _owner->GetAuraEffectsByType(SPELL_AURA_MOD_TOTAL_THREAT)) mod += eff->GetAmount(); - for (auto const& pair : _threatenedByMe) + if (_threatenedByMe.empty()) + return; + + auto it = _threatenedByMe.begin(); + bool const isIncrease = (it->second->_tempModifier < mod); + do { - pair.second->_tempModifier = mod; - pair.second->HeapNotifyChanged(); - } + it->second->_tempModifier = mod; + if (isIncrease) + it->second->HeapNotifyIncreased(); + else + it->second->HeapNotifyDecreased(); + } while ((++it) != _threatenedByMe.end()); } void ThreatManager::UpdateMySpellSchoolModifiers() @@ -686,6 +717,13 @@ void ThreatManager::UnregisterRedirectThreat(uint32 spellId, ObjectGuid const& v UpdateRedirectInfo(); } +void ThreatManager::SendClearAllThreatToClients() const +{ + WorldPackets::Combat::ThreatClear threatClear; + threatClear.UnitGUID = _owner->GetGUID(); + _owner->SendMessageToSet(threatClear.Write(), false); +} + void ThreatManager::SendRemoveToClients(Unit const* victim) const { WorldPackets::Combat::ThreatRemove threatRemove; @@ -694,23 +732,36 @@ void ThreatManager::SendRemoveToClients(Unit const* victim) const _owner->SendMessageToSet(threatRemove.Write(), false); } -void ThreatManager::SendNewVictimToClients(ThreatReference const* victimRef) const +void ThreatManager::SendThreatListToClients(bool newHighest) const { - WorldPackets::Combat::HighestThreatUpdate highestThreatUpdate; - highestThreatUpdate.UnitGUID = _owner->GetGUID(); - highestThreatUpdate.HighestThreatGUID = victimRef->_victim->GetGUID(); - highestThreatUpdate.ThreatList.reserve(_sortedThreatList.size()); - for (ThreatReference const* ref : _sortedThreatList) + auto fillSharedPacketDataAndSend = [&](auto& packet) { - if (!ref->IsAvailable()) - continue; + packet.UnitGUID = _owner->GetGUID(); + packet.ThreatList.reserve(_sortedThreatList.size()); + for (ThreatReference const* ref : _sortedThreatList) + { + if (!ref->IsAvailable()) + continue; - WorldPackets::Combat::ThreatInfo threatInfo; - threatInfo.UnitGUID = ref->GetVictim()->GetGUID(); - threatInfo.Threat = int64(ref->GetThreat() * 100); - highestThreatUpdate.ThreatList.push_back(threatInfo); + WorldPackets::Combat::ThreatInfo threatInfo; + threatInfo.UnitGUID = ref->GetVictim()->GetGUID(); + threatInfo.Threat = int64(ref->GetThreat() * 100); + packet.ThreatList.push_back(threatInfo); + } + _owner->SendMessageToSet(packet.Write(), false); + }; + + if (newHighest) + { + WorldPackets::Combat::HighestThreatUpdate highestThreatUpdate; + highestThreatUpdate.HighestThreatGUID = _currentVictimRef->GetVictim()->GetGUID(); + fillSharedPacketDataAndSend(highestThreatUpdate); + } + else + { + WorldPackets::Combat::ThreatUpdate threatUpdate; + fillSharedPacketDataAndSend(threatUpdate); } - _owner->SendMessageToSet(highestThreatUpdate.Write(), false); } void ThreatManager::PutThreatListRef(ObjectGuid const& guid, ThreatReference* ref) @@ -721,22 +772,19 @@ void ThreatManager::PutThreatListRef(ObjectGuid const& guid, ThreatReference* re ref->_handle = _sortedThreatList.push(ref); } -void ThreatManager::PurgeThreatListRef(ObjectGuid const& guid, bool sendRemove) +void ThreatManager::PurgeThreatListRef(ObjectGuid const& guid) { auto it = _myThreatListEntries.find(guid); if (it == _myThreatListEntries.end()) return; ThreatReference* ref = it->second; _myThreatListEntries.erase(it); + _sortedThreatList.erase(ref->_handle); - if (_currentVictimRef == ref) - _currentVictimRef = nullptr; if (_fixateRef == ref) _fixateRef = nullptr; - - _sortedThreatList.erase(ref->_handle); - if (sendRemove && ref->IsAvailable()) - SendRemoveToClients(ref->_victim); + if (_currentVictimRef == ref) + _currentVictimRef = nullptr; } void ThreatManager::PutThreatenedByMeRef(ObjectGuid const& guid, ThreatReference* ref) diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h index 439245ab680..fc49bb26b6e 100644 --- a/src/server/game/Combat/ThreatManager.h +++ b/src/server/game/Combat/ThreatManager.h @@ -27,6 +27,7 @@ #include <unordered_map> #include <vector> +class Creature; class Unit; class SpellInfo; @@ -43,7 +44,7 @@ class SpellInfo; * - There is an active combat reference between owner and victim. * * * * ThreatManager also keeps track of whether its owner is engaged (a boolean flag). * - * - If a (non-offline) threat list entry is added to a not-yet-engaged ThreatManager, it calls JustEngagedWith on its owner's AI. * + * - If a (non-offline) threat list entry is added to a not-yet-engaged ThreatManager, it calls JustEngagedWith on its owner's AI. * * - The engaged state is cleared in ClearAllThreat (which is invoked on evade). * * - This flag can be accessed through the IsEngaged method. For creatures that can have a threat list, this is equal to Unit::IsEngaged. * * * @@ -57,16 +58,16 @@ class SpellInfo; * Selection uses the following properties on ThreatReference, in order: * * - Online state (one of ONLINE, SUPPRESSED, OFFLINE): * * - ONLINE: Normal threat state, target is valid and attackable * - * - SUPPRESSED: Target is attackable, but fully immuned. This is used for targets under HoP, Divine Shield, Ice Block etc. * + * - SUPPRESSED: Target is attackable, but inopportune. This is used for targets under immunity effects and damage-breaking CC. * * Targets with SUPPRESSED threat can still be valid targets, but any target with ONLINE threat will be preferred. * * - OFFLINE: The target is, for whatever reason, not valid at this time (for example, IMMUNE_TO_X flags or game master state). * - * These targets can never be selected by SelectVictim, which will return nullptr if all targets are OFFLINE (typically causing evade). * + * These targets can never be selected, and GetCurrentVictim will return nullptr if all targets are OFFLINE (typically causing evade). * * - Related methods: GetOnlineState, IsOnline, IsAvailable, IsOffline * * - Taunt state (one of TAUNT, NONE, DETAUNT), the names speak for themselves * * - Related methods: GetTauntState, IsTaunting, IsDetaunted * * - Actual threat value (GetThreat) * * * - * The current (= last selected) victim can be accessed using GetCurrentVictim. SelectVictim selects a (potentially new) victim. * + * The current (= last selected) victim can be accessed using GetCurrentVictim. * * Beyond that, ThreatManager has a variety of helpers and notifiers, which are documented inline below. * * * * SPECIAL NOTE: Please be aware that any iterator may be invalidated if you modify a ThreatReference. The heap holds const pointers for a reason, but * @@ -87,7 +88,7 @@ class TC_GAME_API ThreatManager public: typedef boost::heap::fibonacci_heap<ThreatReference const*, boost::heap::compare<CompareThreatLessThan>> threat_list_heap; class ThreatListIterator; - static const uint32 CLIENT_THREAT_UPDATE_INTERVAL = 1000u; + static const uint32 THREAT_UPDATE_INTERVAL = 1000u; static bool CanHaveThreatList(Unit const* who); @@ -105,12 +106,11 @@ class TC_GAME_API ThreatManager // can our owner have a threat list? // identical to ThreatManager::CanHaveThreatList(GetOwner()) bool CanHaveThreatList() const { return _ownerCanHaveThreatList; } - // returns the victim selected by the last SelectVictim call - this can be nullptr + // returns the current victim - this can be nullptr if owner's threat list is empty, or has only offline targets + Unit* GetCurrentVictim(); Unit* GetCurrentVictim() const; // returns an arbitrary non-offline victim from owner's threat list if one exists, nullptr otherwise Unit* GetAnyTarget() const; - // selects a (potentially new) victim from the threat list and returns it - this can be nullptr - Unit* SelectVictim(); bool IsEngaged() const { return _ownerEngaged; } // are there any entries in owner's threat list? @@ -140,8 +140,8 @@ class TC_GAME_API ThreatManager bool IsThreateningTo(Unit const* who, bool includeOffline = false) const; auto const& GetThreatenedByMeList() const { return _threatenedByMe; } - // Notify the ThreatManager that a condition changed that may impact refs' online state so it can re-evaluate - void UpdateOnlineStates(bool meThreateningOthers = true, bool othersThreateningMe = true); + // Notify the ThreatManager that its owner may now be suppressed on others' threat lists (immunity or damage-breakable CC being applied) + void EvaluateSuppressed(); ///== AFFECT MY THREAT LIST == void AddThreat(Unit* target, float amount, SpellInfo const* spell = nullptr, bool ignoreModifiers = false, bool ignoreRedirects = false); void ScaleThreat(Unit* target, float factor); @@ -157,6 +157,7 @@ class TC_GAME_API ThreatManager void ResetAllThreat(); // Removes specified target from the threat list void ClearThreat(Unit* target); + void ClearThreat(ThreatReference* ref); // Removes all targets from the threat list (will cause evade in UpdateVictim if called) void ClearAllThreat(); @@ -166,9 +167,6 @@ class TC_GAME_API ThreatManager void ClearFixate() { FixateTarget(nullptr); } Unit* GetFixateTarget() const; - // sends SMSG_THREAT_UPDATE to all nearby clients (used by client to forward threat list info to addons) - void SendThreatListToClients() const; - ///== AFFECT OTHERS' THREAT LISTS == // what it says on the tin - call AddThreat on everything that's threatened by us with the specified params void ForwardThreatForAssistingMe(Unit* assistant, float baseAmount, SpellInfo const* spell = nullptr, bool ignoreModifiers = false); @@ -199,17 +197,20 @@ class TC_GAME_API ThreatManager // send opcodes (all for my own threat list) void SendClearAllThreatToClients() const; void SendRemoveToClients(Unit const* victim) const; - void SendNewVictimToClients(ThreatReference const* victimRef) const; + void SendThreatListToClients(bool newHighest) const; ///== MY THREAT LIST == void PutThreatListRef(ObjectGuid const& guid, ThreatReference* ref); - void PurgeThreatListRef(ObjectGuid const& guid, bool sendRemove); + void PurgeThreatListRef(ObjectGuid const& guid); - uint32 _updateClientTimer; + uint32 _updateTimer; threat_list_heap _sortedThreatList; std::unordered_map<ObjectGuid, ThreatReference*> _myThreatListEntries; - ThreatReference const* _currentVictimRef; + + // picks a new victim - called from ::Update periodically + void UpdateVictim(); ThreatReference const* ReselectVictim(); + ThreatReference const* _currentVictimRef; ThreatReference const* _fixateRef; ///== OTHERS' THREAT LISTS == @@ -254,7 +255,7 @@ class TC_GAME_API ThreatReference enum TauntState : uint32 { TAUNT_STATE_DETAUNT = 0, TAUNT_STATE_NONE = 1, TAUNT_STATE_TAUNT = 2 }; enum OnlineState { ONLINE_STATE_ONLINE = 2, ONLINE_STATE_SUPPRESSED = 1, ONLINE_STATE_OFFLINE = 0 }; - Unit* GetOwner() const { return _owner; } + Creature* GetOwner() const { return _owner; } Unit* GetVictim() const { return _victim; } float GetThreat() const { return std::max<float>(_baseAmount + (float)_tempModifier, 0.0f); } OnlineState GetOnlineState() const { return _online; } @@ -265,28 +266,34 @@ class TC_GAME_API ThreatReference bool IsTaunting() const { return _taunted >= TAUNT_STATE_TAUNT; } bool IsDetaunted() const { return _taunted == TAUNT_STATE_DETAUNT; } - void SetThreat(float amount) { _baseAmount = amount; HeapNotifyChanged(); } void AddThreat(float amount); void ScaleThreat(float factor); void ModifyThreatByPercent(int32 percent) { if (percent) ScaleThreat(0.01f*float(100 + percent)); } - void UpdateOnlineState(); + void UpdateOffline(); - void ClearThreat(bool sendRemove = true); // dealloc's this + void ClearThreat(); // dealloc's this private: - ThreatReference(ThreatManager* mgr, Unit* victim, float amount) : _owner(mgr->_owner), _mgr(mgr), _victim(victim), _baseAmount(amount), _tempModifier(0), _online(SelectOnlineState()), _taunted(TAUNT_STATE_NONE) { } static bool FlagsAllowFighting(Unit const* a, Unit const* b); - OnlineState SelectOnlineState(); + + ThreatReference(ThreatManager* mgr, Unit* victim, float amount) : + _owner(reinterpret_cast<Creature*>(mgr->_owner)), _mgr(*mgr), _victim(victim), + _online(ShouldBeOffline() ? ONLINE_STATE_OFFLINE : ShouldBeSuppressed() ? ONLINE_STATE_SUPPRESSED : ONLINE_STATE_ONLINE), + _baseAmount(IsOnline() ? amount : 0.0f), _tempModifier(0), _taunted(TAUNT_STATE_NONE) { } + + void UnregisterAndFree(); + + bool ShouldBeOffline() const; + bool ShouldBeSuppressed() const; void UpdateTauntState(TauntState state = TAUNT_STATE_NONE); - Unit* const _owner; - ThreatManager* const _mgr; - void HeapNotifyIncreased() { _mgr->_sortedThreatList.increase(_handle); } - void HeapNotifyDecreased() { _mgr->_sortedThreatList.decrease(_handle); } - void HeapNotifyChanged() { _mgr->_sortedThreatList.update(_handle); } + Creature* const _owner; + ThreatManager& _mgr; + void HeapNotifyIncreased() { _mgr._sortedThreatList.increase(_handle); } + void HeapNotifyDecreased() { _mgr._sortedThreatList.decrease(_handle); } Unit* const _victim; + OnlineState _online; float _baseAmount; int32 _tempModifier; // Temporary effects (auras with SPELL_AURA_MOD_TOTAL_THREAT) - set from victim's threatmanager in ThreatManager::UpdateMyTempModifiers - OnlineState _online; TauntState _taunted; ThreatManager::threat_list_heap::handle_type _handle; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index f202646e7ea..e4895dd5952 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -676,7 +676,7 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, LoadCreaturesAddon(); LoadTemplateImmunities(); - GetThreatManager().UpdateOnlineStates(true, true); + GetThreatManager().EvaluateSuppressed(); return true; } @@ -807,7 +807,6 @@ void Creature::Update(uint32 diff) if (diff >= m_boundaryCheckTime) { AI()->CheckInRoom(); - GetThreatManager().UpdateOnlineStates(false, true); m_boundaryCheckTime = 2500; } else m_boundaryCheckTime -= diff; @@ -1188,7 +1187,7 @@ Unit* Creature::SelectVictim() Unit* target = nullptr; if (CanHaveThreatList()) - target = GetThreatManager().SelectVictim(); + target = GetThreatManager().GetCurrentVictim(); else if (!HasReactState(REACT_PASSIVE)) { // We're a player pet, probably diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index bec840089fc..4bf396c353c 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2165,10 +2165,7 @@ void Player::SetGameMaster(bool on) RemoveUnitFlag2(UNIT_FLAG2_ALLOW_CHEAT_SPELLS); if (Pet* pet = GetPet()) - { pet->SetFaction(GetFaction()); - pet->GetThreatManager().UpdateOnlineStates(); - } // restore FFA PvP Server state if (sWorld->IsFFAPvPRealm()) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index fe94238f9ed..bd512988fb0 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -7713,16 +7713,11 @@ void Unit::SetImmuneToAll(bool apply, bool keepCombat) { AddUnitFlag(UnitFlags(UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC)); ValidateAttackersAndOwnTarget(); - if (keepCombat) - m_threatManager.UpdateOnlineStates(true, true); - else + if (!keepCombat) m_combatManager.EndAllCombat(); } else - { RemoveUnitFlag(UnitFlags(UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC)); - m_threatManager.UpdateOnlineStates(true, true); - } } void Unit::SetImmuneToPC(bool apply, bool keepCombat) @@ -7731,9 +7726,7 @@ void Unit::SetImmuneToPC(bool apply, bool keepCombat) { AddUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); ValidateAttackersAndOwnTarget(); - if (keepCombat) - m_threatManager.UpdateOnlineStates(true, true); - else + if (!keepCombat) { std::list<CombatReference*> toEnd; for (auto const& pair : m_combatManager.GetPvECombatRefs()) @@ -7747,10 +7740,7 @@ void Unit::SetImmuneToPC(bool apply, bool keepCombat) } } else - { RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); - m_threatManager.UpdateOnlineStates(true, true); - } } void Unit::SetImmuneToNPC(bool apply, bool keepCombat) @@ -7759,9 +7749,7 @@ void Unit::SetImmuneToNPC(bool apply, bool keepCombat) { AddUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC); ValidateAttackersAndOwnTarget(); - if (keepCombat) - m_threatManager.UpdateOnlineStates(true, true); - else + if (!keepCombat) { std::list<CombatReference*> toEnd; for (auto const& pair : m_combatManager.GetPvECombatRefs()) @@ -7775,10 +7763,7 @@ void Unit::SetImmuneToNPC(bool apply, bool keepCombat) } } else - { RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC); - m_threatManager.UpdateOnlineStates(true, true); - } } bool Unit::IsThreatened() const @@ -11527,7 +11512,6 @@ void Unit::UpdateObjectVisibility(bool forced) AddToNotify(NOTIFY_VISIBILITY_CHANGED); else { - m_threatManager.UpdateOnlineStates(true, true); WorldObject::UpdateObjectVisibility(true); // call MoveInLineOfSight for nearby creatures Trinity::AIRelocationNotifier notifier(*this); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 3035bc2fa2b..e6bd72cf6e7 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1127,7 +1127,6 @@ class TC_GAME_API Unit : public WorldObject ThreatManager const& GetThreatManager() const { return m_threatManager; } void SendClearTarget(); - void SendThreatListUpdate() { m_threatManager.SendThreatListToClients(); } bool HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, flag128 familyFlags) const; bool virtual HasSpell(uint32 /*spellID*/) const { return false; } diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index a9532ff0823..e2001c167dd 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -2043,7 +2043,7 @@ void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode, } } - target->GetThreatManager().UpdateOnlineStates(true, false); + target->GetThreatManager().EvaluateSuppressed(); } void AuraEffect::HandleAuraModScale(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -2151,7 +2151,6 @@ void AuraEffect::HandleFeignDeath(AuraApplication const* aurApp, uint8 mode, boo if (Creature* creature = target->ToCreature()) creature->InitializeReactState(); } - target->GetThreatManager().UpdateOnlineStates(true, false); } void AuraEffect::HandleModUnattackable(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -2799,7 +2798,7 @@ void AuraEffect::HandleModConfuse(AuraApplication const* aurApp, uint8 mode, boo Unit* target = aurApp->GetTarget(); target->SetControlled(apply, UNIT_STATE_CONFUSED); - target->GetThreatManager().UpdateOnlineStates(true, false); + target->GetThreatManager().EvaluateSuppressed(); } void AuraEffect::HandleModFear(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -2810,7 +2809,7 @@ void AuraEffect::HandleModFear(AuraApplication const* aurApp, uint8 mode, bool a Unit* target = aurApp->GetTarget(); target->SetControlled(apply, UNIT_STATE_FLEEING); - target->GetThreatManager().UpdateOnlineStates(true, false); + target->GetThreatManager().EvaluateSuppressed(); } void AuraEffect::HandleAuraModStun(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -2821,7 +2820,7 @@ void AuraEffect::HandleAuraModStun(AuraApplication const* aurApp, uint8 mode, bo Unit* target = aurApp->GetTarget(); target->SetControlled(apply, UNIT_STATE_STUNNED); - target->GetThreatManager().UpdateOnlineStates(true, false); + target->GetThreatManager().EvaluateSuppressed(); } void AuraEffect::HandleAuraModRoot(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -2832,7 +2831,7 @@ void AuraEffect::HandleAuraModRoot(AuraApplication const* aurApp, uint8 mode, bo Unit* target = aurApp->GetTarget(); target->SetControlled(apply, UNIT_STATE_ROOT); - target->GetThreatManager().UpdateOnlineStates(true, false); + target->GetThreatManager().EvaluateSuppressed(); } void AuraEffect::HandlePreventFleeing(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3204,7 +3203,7 @@ void AuraEffect::HandleAuraModSchoolImmunity(AuraApplication const* aurApp, uint target->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::StealthOrInvis); } - target->GetThreatManager().UpdateOnlineStates(true, false); + target->GetThreatManager().EvaluateSuppressed(); } void AuraEffect::HandleAuraModDmgImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3215,7 +3214,7 @@ void AuraEffect::HandleAuraModDmgImmunity(AuraApplication const* aurApp, uint8 m Unit* target = aurApp->GetTarget(); m_spellInfo->ApplyAllSpellImmunitiesTo(target, GetSpellEffectInfo(), apply); - target->GetThreatManager().UpdateOnlineStates(true, false); + target->GetThreatManager().EvaluateSuppressed(); } void AuraEffect::HandleAuraModDispelImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index a7008eefae2..59090943a0b 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -3493,7 +3493,7 @@ void Spell::EffectSanctuary() { // in dungeons (or for nonplayers), reset this unit on all enemies' threat lists for (auto const& pair : unitTarget->GetThreatManager().GetThreatenedByMeList()) - pair.second->SetThreat(0.0f); + pair.second->ScaleThreat(0.0f); } // makes spells cast before this time fizzle diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp index 4b7373cdf55..43f0220df27 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp @@ -295,9 +295,6 @@ public: void UpdateAI(uint32 diff) override { - if (!_lastPlayerCombatState && me->IsEngaged()) - me->GetThreatManager().UpdateOnlineStates(false, true); - if (!UpdateVictim()) return; 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 14d1c985e8c..ad6203dd864 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp @@ -610,7 +610,10 @@ struct boss_faction_championsAI : public BossAI { for (ThreatReference* ref : me->GetThreatManager().GetModifiableThreatList()) if (Player* victim = ref->GetVictim()->ToPlayer()) - ref->SetThreat(1000000.0f * CalculateThreat(me->GetDistance2d(victim), victim->GetArmor(), victim->GetHealth())); + { + ref->ScaleThreat(0.0f); + ref->AddThreat(1000000.0f * CalculateThreat(me->GetDistance2d(victim), victim->GetArmor(), victim->GetHealth())); + } } void UpdatePower() diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp index 2e4a561dc56..b9aafbedd2d 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp @@ -600,7 +600,7 @@ class boss_algalon_the_observer : public CreatureScript //! for creatures that start combat in REACT_PASSIVE and UNIT_FLAG_NOT_SELECTABLE //! causing them to immediately evade if (!me->GetThreatManager().IsThreatListEmpty()) - AttackStart(me->GetThreatManager().SelectVictim()); + AttackStart(me->GetThreatManager().GetCurrentVictim()); for (uint32 i = 0; i < LIVING_CONSTELLATION_COUNT; ++i) if (Creature* summon = DoSummon(NPC_LIVING_CONSTELLATION, ConstellationPos[i], 0, TEMPSUMMON_DEAD_DESPAWN)) summon->SetReactState(REACT_PASSIVE); |