aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreeston <treeston.mmoc@gmail.com>2018-08-27 19:08:17 +0200
committerShauren <shauren.trinity@gmail.com>2021-10-23 02:14:04 +0200
commit392a644dc8562ea54b9c185a2776fceb1006e2cd (patch)
treeeec4d04885e582c39f0b88ea1cf0d87b4bb1015a
parent395f58d651c7c370522ead6ba9a3c684d91e3a66 (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)
-rw-r--r--src/server/game/Combat/ThreatManager.cpp280
-rw-r--r--src/server/game/Combat/ThreatManager.h65
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp5
-rw-r--r--src/server/game/Entities/Player/Player.cpp3
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp22
-rw-r--r--src/server/game/Entities/Unit/Unit.h1
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp15
-rw-r--r--src/server/game/Spells/SpellEffects.cpp2
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp3
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp5
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp2
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);