Scripts/Commands: New command: .debug threatinfo

# Conflicts:
#	sql/base/auth_database.sql
#	sql/old/4.3.4/auth/19_2017_06_18/2018_01_09_00_auth.sql
#	sql/old/4.3.4/world/23_2017_11_15/2018_01_09_00_world.sql
#	src/server/game/Accounts/RBAC.h
#	src/server/game/Combat/ThreatManager.h
This commit is contained in:
Treeston
2018-01-09 18:09:24 +01:00
committed by Ovahlord
parent 4411a5389c
commit cbec08f2b5
3 changed files with 224 additions and 152 deletions

View File

@@ -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 */;

View File

@@ -15,13 +15,11 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_THREATMANAGER_H
#define TRINITY_THREATMANAGER_H
#include "Common.h"
#include "IteratorPair.h"
#include "ObjectGuid.h"
#include "SharedDefines.h"
#include <array>
#include <boost/heap/fibonacci_heap.hpp>
#include <unordered_map>
#include <vector>
@@ -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<ThreatReference const*, boost::heap::compare<CompareThreatLessThan>> threat_list_heap;
public:
typedef boost::heap::fibonacci_heap<ThreatReference const*, boost::heap::compare<CompareThreatLessThan>> 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<threat_list_heap::const_iterator> 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<threat_list_heap::ordered_iterator> 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<ThreatReference*> 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<threat_list_heap::const_iterator> 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<threat_list_heap::ordered_iterator> 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<ThreatReference*> 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<ObjectGuid, ThreatReference*> _myThreatListEntries;
ThreatReference const* _currentVictimRef;
ThreatReference const* ReselectVictim();
uint32 _updateClientTimer;
threat_list_heap _sortedThreatList;
std::unordered_map<ObjectGuid, ThreatReference*> _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<ObjectGuid, ThreatReference*> _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<std::underlying_type<SpellSchoolMask>::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<ObjectGuid, ThreatReference*> _threatenedByMe; // these refs are entries for myself on other units' threat lists
std::array<float, MAX_SPELL_SCHOOL> _singleSchoolModifiers; // most spells are single school - we pre-calculate these and store them
mutable std::unordered_map<std::underlying_type<SpellSchoolMask>::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<std::pair<ObjectGuid, uint32>> _redirectInfo; // current redirection targets and percentages (updated from registry in ThreatManager::UpdateRedirectInfo)
std::unordered_map<uint32, std::unordered_map<ObjectGuid, uint32>> _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<std::pair<ObjectGuid, uint32>> _redirectInfo; // current redirection targets and percentages (updated from registry in ThreatManager::UpdateRedirectInfo)
std::unordered_map<uint32, std::unordered_map<ObjectGuid, uint32>> _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<float>(_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<float>(_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

View File

@@ -39,6 +39,7 @@ EndScriptData */
#include "PoolMgr.h"
#include "QuestPools.h"
#include "RBAC.h"
#include "SpellMgr.h"
#include "Transport.h"
#include "WorldSession.h"
#include <fstream>
@@ -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] : "<unknown>", 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();