diff --git a/sql/base/auth_database.sql b/sql/base/auth_database.sql
index cc57dc7df22..ab69c9b8bb7 100644
--- a/sql/base/auth_database.sql
+++ b/sql/base/auth_database.sql
@@ -962,9 +962,8 @@ INSERT INTO `rbac_linked_permissions` VALUES
(196,843),
(196,866),
(196,867),
-(196,872),
-(196,873),
-(197,232),
+(196,870),(196,872),
+(196,873),(197,232),
(197,236),
(197,237),
(197,273),
@@ -1847,6 +1846,7 @@ INSERT INTO `rbac_permissions` VALUES
(865,'Command: npc showloot'),
(866,'Command: list spawnpoints'),
(867,'Command: reload quest_greeting_locale'),
+(870,'Command: debug threatinfo');
(872,'Command: server debug'),
(873,'Command: reload creature_movement_override');
/*!40000 ALTER TABLE `rbac_permissions` ENABLE KEYS */;
diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h
index f4ac832dfad..2a600a922f5 100644
--- a/src/server/game/Combat/ThreatManager.h
+++ b/src/server/game/Combat/ThreatManager.h
@@ -15,13 +15,11 @@
* with this program. If not, see .
*/
-#ifndef TRINITY_THREATMANAGER_H
-#define TRINITY_THREATMANAGER_H
-
#include "Common.h"
#include "IteratorPair.h"
#include "ObjectGuid.h"
#include "SharedDefines.h"
+#include
#include
#include
#include
@@ -82,8 +80,9 @@ struct CompareThreatLessThan
// Please check Game/Combat/ThreatManager.h for documentation on how this class works!
class TC_GAME_API ThreatManager
{
-public:
- typedef boost::heap::fibonacci_heap> threat_list_heap;
+ public:
+ typedef boost::heap::fibonacci_heap> threat_list_heap;
+ static const uint32 CLIENT_THREAT_UPDATE_INTERVAL = 1000u;
static bool CanHaveThreatList(Unit const* who);
@@ -99,180 +98,178 @@ public:
// never nullptr
Unit* GetOwner() const { return _owner; }
// can our owner have a threat list?
- // never nullptr
- Unit* GetOwner() const { return _owner; }
- // 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
- 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();
+ // identical to ThreatManager::CanHaveThreatList(GetOwner())
+ bool CanHaveThreatList() const { return _ownerCanHaveThreatList; }
+ // returns the victim selected by the last SelectVictim call - this can be nullptr
+ 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?
- bool IsThreatListEmpty(bool includeOffline = false) const;
- // is there a threat list entry on owner's threat list with victim == who?
- bool IsThreatenedBy(ObjectGuid const& who, bool includeOffline = false) const;
- // is there a threat list entry on owner's threat list with victim == who?
- bool IsThreatenedBy(Unit const* who, bool includeOffline = false) const;
- // returns ThreatReference amount if a ref exists, 0.0f otherwise
- float GetThreat(Unit const* who, bool includeOffline = false) const;
- size_t GetThreatListSize() const { return _sortedThreatList.size(); }
- // fastest of the three threat list getters - gets the threat list in "arbitrary" order
- Trinity::IteratorPair GetUnsortedThreatList() const { return { _sortedThreatList.begin(), _sortedThreatList.end() }; }
- // slightly slower than GetUnsorted, but, well...sorted - only use it if you need the sorted property, of course
- // note: current tank is NOT guaranteed to be the first entry in this list - check GetCurrentVictim separately if you want that!
- Trinity::IteratorPair GetSortedThreatList() const { return { _sortedThreatList.ordered_begin(), _sortedThreatList.ordered_end() }; }
- // slowest of the three threat list getters (by far), but lets you modify the threat references
- std::vector GetModifiableThreatList() const;
+ bool IsEngaged() const { return _ownerEngaged; }
+ // are there any entries in owner's threat list?
+ bool IsThreatListEmpty(bool includeOffline = false) const;
+ // is there a threat list entry on owner's threat list with victim == who?
+ bool IsThreatenedBy(ObjectGuid const& who, bool includeOffline = false) const;
+ // is there a threat list entry on owner's threat list with victim == who?
+ bool IsThreatenedBy(Unit const* who, bool includeOffline = false) const;
+ // returns ThreatReference amount if a ref exists, 0.0f otherwise
+ float GetThreat(Unit const* who, bool includeOffline = false) const;
+ size_t GetThreatListSize() const { return _sortedThreatList.size(); }
+ // fastest of the three threat list getters - gets the threat list in "arbitrary" order
+ Trinity::IteratorPair GetUnsortedThreatList() const { return { _sortedThreatList.begin(), _sortedThreatList.end() }; }
+ // slightly slower than GetUnsorted, but, well...sorted - only use it if you need the sorted property, of course
+ // note: current tank is NOT guaranteed to be the first entry in this list - check GetCurrentVictim separately if you want that!
+ Trinity::IteratorPair GetSortedThreatList() const { return { _sortedThreatList.ordered_begin(), _sortedThreatList.ordered_end() }; }
+ // slowest of the three threat list getters (by far), but lets you modify the threat references
+ std::vector GetModifiableThreatList() const;
- // does any unit have a threat list entry with victim == this.owner?
- bool IsThreateningAnyone(bool includeOffline = false) const;
- // is there a threat list entry on who's threat list for this.owner?
- bool IsThreateningTo(ObjectGuid const& who, bool includeOffline = false) const;
- // is there a threat list entry on who's threat list for this.owner?
- bool IsThreateningTo(Unit const* who, bool includeOffline = false) const;
- auto const& GetThreatenedByMeList() const { return _threatenedByMe; }
+ // does any unit have a threat list entry with victim == this.owner?
+ bool IsThreateningAnyone(bool includeOffline = false) const;
+ // is there a threat list entry on who's threat list for this.owner?
+ bool IsThreateningTo(ObjectGuid const& who, bool includeOffline = false) const;
+ // is there a threat list entry on who's threat list for this.owner?
+ 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);
- ///== 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);
- // Modify target's threat by +percent%
- void ModifyThreatByPercent(Unit* target, int32 percent) { if (percent) ScaleThreat(target, 0.01f * float(100 + percent)); }
- // Resets the specified unit's threat to zero
- void ResetThreat(Unit* target) { ScaleThreat(target, 0.0f); }
- // Sets the specified unit's threat to be equal to the highest entry on the threat list
- void MatchUnitThreatToHighestThreat(Unit* target);
- // Notify the ThreatManager that we have a new taunt aura (or a taunt aura expired)
- void TauntUpdate();
- // Sets all threat refs in owner's threat list to have zero threat
- void ResetAllThreat();
- // Removes specified target from the threat list
- void ClearThreat(Unit* target);
- // Removes all targets from the threat list (will cause evade in UpdateVictim if called)
- void ClearAllThreat();
+ // 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);
+ ///== 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);
+ // Modify target's threat by +percent%
+ void ModifyThreatByPercent(Unit* target, int32 percent) { if (percent) ScaleThreat(target, 0.01f*float(100 + percent)); }
+ // Resets the specified unit's threat to zero
+ void ResetThreat(Unit* target) { ScaleThreat(target, 0.0f); }
+ // Sets the specified unit's threat to be equal to the highest entry on the threat list
+ void MatchUnitThreatToHighestThreat(Unit* target);
+ // Notify the ThreatManager that we have a new taunt aura (or a taunt aura expired)
+ void TauntUpdate();
+ // Sets all threat refs in owner's threat list to have zero threat
+ void ResetAllThreat();
+ // Removes specified target from the threat list
+ void ClearThreat(Unit* target);
+ // Removes all targets from the threat list (will cause evade in UpdateVictim if called)
+ void ClearAllThreat();
- // sends SMSG_THREAT_UPDATE to all nearby clients (used by client to forward threat list info to addons)
- void SendThreatListToClients() 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);
- // delete all ThreatReferences with victim == owner
- void RemoveMeFromThreatLists();
- // re-calculates the temporary threat modifier from auras on myself
- void UpdateMyTempModifiers();
- // re-calculate SPELL_AURA_MOD_THREAT modifiers
- void UpdateMySpellSchoolModifiers();
+ ///== 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);
+ // delete all ThreatReferences with victim == owner
+ void RemoveMeFromThreatLists();
+ // re-calculates the temporary threat modifier from auras on myself
+ void UpdateMyTempModifiers();
+ // re-calculate SPELL_AURA_MOD_THREAT modifiers
+ void UpdateMySpellSchoolModifiers();
- ///== REDIRECT SYSTEM ==
- // Register a redirection effect that redirects pct% of threat generated by owner to victim
- void RegisterRedirectThreat(uint32 spellId, ObjectGuid const& victim, uint32 pct);
- // Unregister a redirection effort for all victims
- void UnregisterRedirectThreat(uint32 spellId);
- // Unregister a redirection effect for a specific victim
- void UnregisterRedirectThreat(uint32 spellId, ObjectGuid const& victim);
+ ///== REDIRECT SYSTEM ==
+ // Register a redirection effect that redirects pct% of threat generated by owner to victim
+ void RegisterRedirectThreat(uint32 spellId, ObjectGuid const& victim, uint32 pct);
+ // Unregister a redirection effort for all victims
+ void UnregisterRedirectThreat(uint32 spellId);
+ // Unregister a redirection effect for a specific victim
+ void UnregisterRedirectThreat(uint32 spellId, ObjectGuid const& victim);
-private:
- Unit* const _owner;
- bool _ownerCanHaveThreatList;
- bool _ownerEngaged;
+ private:
+ Unit* const _owner;
+ bool _ownerCanHaveThreatList;
+ bool _ownerEngaged;
- static const CompareThreatLessThan CompareThreat;
- static bool CompareReferencesLT(ThreatReference const* a, ThreatReference const* b, float aWeight);
- static float CalculateModifiedThreat(float threat, Unit const* victim, SpellInfo const* spell);
+ static const CompareThreatLessThan CompareThreat;
+ static bool CompareReferencesLT(ThreatReference const* a, ThreatReference const* b, float aWeight);
+ static float CalculateModifiedThreat(float threat, Unit const* victim, SpellInfo const* spell);
- // send opcodes (all for my own threat list)
- void SendClearAllThreatToClients() const;
- void SendRemoveToClients(Unit const* victim) const;
- void SendNewVictimToClients(ThreatReference const* victimRef) const;
+ // send opcodes (all for my own threat list)
+ void SendClearAllThreatToClients() const;
+ void SendRemoveToClients(Unit const* victim) const;
+ void SendNewVictimToClients(ThreatReference const* victimRef) const;
- ///== MY THREAT LIST ==
- void PutThreatListRef(ObjectGuid const& guid, ThreatReference* ref);
- void PurgeThreatListRef(ObjectGuid const& guid, bool sendRemove);
+ ///== MY THREAT LIST ==
+ void PutThreatListRef(ObjectGuid const& guid, ThreatReference* ref);
+ void PurgeThreatListRef(ObjectGuid const& guid, bool sendRemove);
- uint32 _updateClientTimer;
- threat_list_heap _sortedThreatList;
- std::unordered_map _myThreatListEntries;
- ThreatReference const* _currentVictimRef;
- ThreatReference const* ReselectVictim();
+ uint32 _updateClientTimer;
+ threat_list_heap _sortedThreatList;
+ std::unordered_map _myThreatListEntries;
+ ThreatReference const* _currentVictimRef;
+ ThreatReference const* ReselectVictim();
- ///== OTHERS' THREAT LISTS ==
- void PutThreatenedByMeRef(ObjectGuid const& guid, ThreatReference* ref);
- void PurgeThreatenedByMeRef(ObjectGuid const& guid);
- std::unordered_map _threatenedByMe; // these refs are entries for myself on other units' threat lists
- float _singleSchoolModifiers[MAX_SPELL_SCHOOL]; // most spells are single school - we pre-calculate these and store them
- mutable std::unordered_map::type, float> _multiSchoolModifiers; // these are calculated on demand
+ ///== OTHERS' THREAT LISTS ==
+ void PutThreatenedByMeRef(ObjectGuid const& guid, ThreatReference* ref);
+ void PurgeThreatenedByMeRef(ObjectGuid const& guid);
+ std::unordered_map _threatenedByMe; // these refs are entries for myself on other units' threat lists
+ std::array _singleSchoolModifiers; // most spells are single school - we pre-calculate these and store them
+ mutable std::unordered_map::type, float> _multiSchoolModifiers; // these are calculated on demand
- // redirect system (is kind of dumb, but that's because none of the redirection spells actually have any aura effect associated with them, so spellscript needs to deal with it)
- void UpdateRedirectInfo();
- std::vector> _redirectInfo; // current redirection targets and percentages (updated from registry in ThreatManager::UpdateRedirectInfo)
- std::unordered_map> _redirectRegistry; // spellid -> (victim -> pct); all redirection effects on us (removal individually managed by spell scripts because blizzard is dumb)
+ // redirect system (is kind of dumb, but that's because none of the redirection spells actually have any aura effect associated with them, so spellscript needs to deal with it)
+ void UpdateRedirectInfo();
+ std::vector> _redirectInfo; // current redirection targets and percentages (updated from registry in ThreatManager::UpdateRedirectInfo)
+ std::unordered_map> _redirectRegistry; // spellid -> (victim -> pct); all redirection effects on us (removal individually managed by spell scripts because blizzard is dumb)
-public:
- ThreatManager(ThreatManager const&) = delete;
- ThreatManager& operator=(ThreatManager const&) = delete;
+ public:
+ ThreatManager(ThreatManager const&) = delete;
+ ThreatManager& operator=(ThreatManager const&) = delete;
friend class ThreatReference;
friend struct CompareThreatLessThan;
+ friend class debug_commandscript;
};
// Please check Game/Combat/ThreatManager.h for documentation on how this class works!
class TC_GAME_API ThreatReference
{
-public:
- enum TauntState { TAUNT_STATE_DETAUNT = -1, TAUNT_STATE_NONE = 0, TAUNT_STATE_TAUNT = 1 };
- enum OnlineState { ONLINE_STATE_ONLINE = 2, ONLINE_STATE_SUPPRESSED = 1, ONLINE_STATE_OFFLINE = 0 };
+ public:
+ enum TauntState { TAUNT_STATE_DETAUNT = -1, TAUNT_STATE_NONE = 0, TAUNT_STATE_TAUNT = 1 };
+ enum OnlineState { ONLINE_STATE_ONLINE = 2, ONLINE_STATE_SUPPRESSED = 1, ONLINE_STATE_OFFLINE = 0 };
- Unit* GetOwner() const { return _owner; }
- Unit* GetVictim() const { return _victim; }
- float GetThreat() const { return std::max(_baseAmount + (float)_tempModifier, 0.0f); }
- OnlineState GetOnlineState() const { return _online; }
- bool IsOnline() const { return (_online >= ONLINE_STATE_ONLINE); }
- bool IsAvailable() const { return (_online > ONLINE_STATE_OFFLINE); }
- bool IsOffline() const { return (_online <= ONLINE_STATE_OFFLINE); }
- TauntState GetTauntState() const { return _taunted; }
- bool IsTaunting() const { return _taunted == TAUNT_STATE_TAUNT; }
- bool IsDetaunted() const { return _taunted == TAUNT_STATE_DETAUNT; }
+ Unit* GetOwner() const { return _owner; }
+ Unit* GetVictim() const { return _victim; }
+ float GetThreat() const { return std::max(_baseAmount + (float)_tempModifier, 0.0f); }
+ OnlineState GetOnlineState() const { return _online; }
+ bool IsOnline() const { return (_online >= ONLINE_STATE_ONLINE); }
+ bool IsAvailable() const { return (_online > ONLINE_STATE_OFFLINE); }
+ bool IsOffline() const { return (_online <= ONLINE_STATE_OFFLINE); }
+ TauntState GetTauntState() const { return _taunted; }
+ 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 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 ClearThreat(bool sendRemove = true); // dealloc's this
+ void ClearThreat(bool sendRemove = true); // 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();
- void UpdateTauntState(bool victimIsTaunting);
- Unit* const _owner;
- ThreatManager* const _mgr;
- void HeapNotifyIncreased() { _mgr->_sortedThreatList.increase(_handle); }
- void HeapNotifyDecreased() { _mgr->_sortedThreatList.decrease(_handle); }
- void HeapNotifyChanged() { _mgr->_sortedThreatList.update(_handle); }
- Unit* const _victim;
- 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;
+ 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();
+ void UpdateTauntState(bool victimIsTaunting);
+ Unit* const _owner;
+ ThreatManager* const _mgr;
+ void HeapNotifyIncreased() { _mgr->_sortedThreatList.increase(_handle); }
+ void HeapNotifyDecreased() { _mgr->_sortedThreatList.decrease(_handle); }
+ void HeapNotifyChanged() { _mgr->_sortedThreatList.update(_handle); }
+ Unit* const _victim;
+ 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;
-public:
- ThreatReference(ThreatReference const&) = delete;
- ThreatReference& operator=(ThreatReference const&) = delete;
+ public:
+ ThreatReference(ThreatReference const&) = delete;
+ ThreatReference& operator=(ThreatReference const&) = delete;
friend class ThreatManager;
friend struct CompareThreatLessThan;
};
inline bool CompareThreatLessThan::operator()(ThreatReference const* a, ThreatReference const* b) const { return ThreatManager::CompareReferencesLT(a, b, 1.0f); }
-
-#endif
+
+ #endif
diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp
index f90f2c6bd11..6cb39871f10 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -39,6 +39,7 @@ EndScriptData */
#include "PoolMgr.h"
#include "QuestPools.h"
#include "RBAC.h"
+#include "SpellMgr.h"
#include "Transport.h"
#include "WorldSession.h"
#include
@@ -78,6 +79,7 @@ public:
{
{ "setbit", rbac::RBAC_PERM_COMMAND_DEBUG_SETBIT, false, &HandleDebugSet32BitCommand, "" },
{ "threat", rbac::RBAC_PERM_COMMAND_DEBUG_THREAT, false, &HandleDebugThreatListCommand, "" },
+ { "threatinfo", rbac::RBAC_PERM_COMMAND_DEBUG_THREATINFO, false, &HandleDebugThreatInfoCommand, "" },
{ "combat", rbac::RBAC_PERM_COMMAND_DEBUG_COMBAT, false, &HandleDebugCombatListCommand, "" },
{ "anim", rbac::RBAC_PERM_COMMAND_DEBUG_ANIM, false, &HandleDebugAnimCommand, "" },
{ "arena", rbac::RBAC_PERM_COMMAND_DEBUG_ARENA, true, &HandleDebugArenaCommand, "" },
@@ -916,6 +918,79 @@ public:
return true;
}
+ static bool HandleDebugThreatInfoCommand(ChatHandler* handler, char const* /*args*/)
+ {
+ Unit* target = handler->getSelectedUnit();
+ if (!target)
+ {
+ handler->SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ handler->PSendSysMessage("Threat info for %s (%s):", target->GetName(), target->GetGUID().ToString().c_str());
+
+ ThreatManager const& mgr = target->GetThreatManager();
+
+ // _singleSchoolModifiers
+ {
+ auto& mods = mgr._singleSchoolModifiers;
+ handler->SendSysMessage(" - Single-school threat modifiers:");
+ handler->PSendSysMessage(" |-- Physical: %.2f%%", mods[SPELL_SCHOOL_NORMAL]*100.0f);
+ handler->PSendSysMessage(" |-- Holy : %.2f%%", mods[SPELL_SCHOOL_HOLY]*100.0f);
+ handler->PSendSysMessage(" |-- Fire : %.2f%%", mods[SPELL_SCHOOL_FIRE]*100.0f);
+ handler->PSendSysMessage(" |-- Nature : %.2f%%", mods[SPELL_SCHOOL_NATURE]*100.0f);
+ handler->PSendSysMessage(" |-- Frost : %.2f%%", mods[SPELL_SCHOOL_FROST]*100.0f);
+ handler->PSendSysMessage(" |-- Shadow : %.2f%%", mods[SPELL_SCHOOL_SHADOW]*100.0f);
+ handler->PSendSysMessage(" |-- Arcane : %.2f%%", mods[SPELL_SCHOOL_ARCANE]*100.0f);
+ }
+
+ // _multiSchoolModifiers
+ {
+ auto& mods = mgr._multiSchoolModifiers;
+ handler->PSendSysMessage("- Multi-school threat modifiers (%zu entries):", mods.size());
+ for (auto const& pair : mods)
+ handler->PSendSysMessage(" |-- Mask 0x%x: %.2f%%", uint32(pair.first), pair.second);
+ }
+
+ // _redirectInfo
+ {
+ auto const& redirectInfo = mgr._redirectInfo;
+ if (redirectInfo.empty())
+ handler->SendSysMessage(" - No redirects being applied");
+ else
+ {
+ handler->PSendSysMessage(" - %02zu redirects being applied:", redirectInfo.size());
+ for (auto const& pair : redirectInfo)
+ {
+ Unit* unit = ObjectAccessor::GetUnit(*target, pair.first);
+ handler->PSendSysMessage(" |-- %02u%% to %s", pair.second, unit ? unit->GetName().c_str() : pair.first.ToString().c_str());
+ }
+ }
+ }
+
+ // _redirectRegistry
+ {
+ auto const& redirectRegistry = mgr._redirectRegistry;
+ if (redirectRegistry.empty())
+ handler->SendSysMessage(" - No redirects are registered");
+ else
+ {
+ handler->PSendSysMessage(" - %02zu spells may have redirects registered", redirectRegistry.size());
+ for (auto const& outerPair : redirectRegistry) // (spellId, (guid, pct))
+ {
+ SpellInfo const* const spell = sSpellMgr->GetSpellInfo(outerPair.first);
+ handler->PSendSysMessage(" |-- #%06u %s (%zu entries):", outerPair.first, spell ? spell->SpellName[0] : "", outerPair.second.size());
+ for (auto const& innerPair : outerPair.second) // (guid, pct)
+ {
+ Unit* unit = ObjectAccessor::GetUnit(*target, innerPair.first);
+ handler->PSendSysMessage(" |-- %02u%% to %s", innerPair.second, unit ? unit->GetName().c_str() : innerPair.first.ToString().c_str());
+ }
+ }
+ }
+ }
+ }
+
static bool HandleDebugCombatListCommand(ChatHandler* handler, char const* /*args*/)
{
Unit* target = handler->getSelectedUnit();