ThreatManager.h
ThreatManager --仇恨管理类
ThreatReference --仇恨列表
struct DefaultTargetSelector --目标选择迭代器
// Selection method used by SelectTarget
enum class SelectTargetMethod
{
Random, // just pick a random target
MaxThreat, // prefer targets higher in the threat list
MinThreat, // prefer targets lower in the threat list
MaxDistance, // prefer targets further from us
MinDistance // prefer targets closer to us
};
random 随机
MaxThreat 最大仇恨
MinThreat 最小仇恨
MaxDistance 最大距离
MinDistance 最小距离
void UnitAI::SelectTargetList(std::list<Unit*>& targetList, uint32 num,
SelectTargetMethod targetType, uint32 offset, float dist,
bool playerOnly, bool withTank, int32 aura)
①targetList:传出参数-获取的目标列表
②num: 需要获取的数量
③SelectTargetMethod : 选择目标的类型-例如最大仇恨往下递减找目标
④offset:点名往后偏移几位-例如点最大仇恨,往下偏移
⑤dist:距离
⑥playerOnly:只揍玩家
⑦withTank:是否包含第一仇恨
⑧aura:
这段代码用于检查生物身上是否存在特定的光环效果。如果 aura 变量为正值,则要求目标生物必须拥有具有相同 ID 的光环效果;如果为负值,则要求目标生物必须没有具有相同 ID 的光环效果。如果条件不满足,则返回 false。这样的设计可用于执行与特定光环效果相关的条件检查
1)找到最大仇恨的前两位,带主T,不限obj
std::list<Unit*> unitList;
SelectTargetList(unitList, 2, SelectTargetMethod::MaxThreat, 0, 0.0f, false);
if (unitList.size() > 1)
DoCast(SelectTarget(SelectTargetMethod::MaxThreat, 0, 100, true), SPELL_WILL_OF_HAKKAR);
2)找到倒数的5位仇恨的玩家,包括主T
std::list<Unit*> playerList;
SelectTargetList(playerList, 5, SelectTargetMethod::MaxThreat, 0, 0.0f, true);
3)取仇恨列表的玩家
①ranged:随机取25个战斗距离为5的玩家,默认有T
②melee:随机取25个战斗距离为5的玩家,默认有T
std::list<Unit*> ranged, melee;
uint32 minTargets = RAID_MODE<uint32>(3, 8, 3, 8);
SelectTargetList(ranged, 25, SelectTargetMethod::Random, 0, -5.0f, true);
SelectTargetList(melee, 25, SelectTargetMethod::Random, 0, 5.0f, true);
4)随机取3个玩家,不带主T
std::list<Unit*> targets;
SelectTargetList(targets, 3, SelectTargetMethod::Random, 0, 0.0f, true, false);
void UnitAI::SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType, uint32 offset, float dist, bool playerOnly, bool withTank, int32 aura)
{
SelectTargetList(targetList, num, targetType, offset, DefaultTargetSelector(me, dist, playerOnly, withTank, aura));
}
--------------------------------------------------
template <class PREDICATE>
void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType, uint32 offset, PREDICATE const& predicate)
{
if (!PrepareTargetListSelection(targetList, targetType, offset))
return;
// then finally filter by predicate
targetList.remove_if([&predicate](Unit* target) { return !predicate(target); });
FinalizeTargetListSelection(targetList, num, targetType);
}
①其他模块调用SelectTargetList
void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType, uint32 offset = 0, float dist = 0.0f, bool playerOnly = false, bool withTank = true, int32 aura = 0);
②UnitAI调用带模板的SelectTargetList,这个内部函数分为三部分
template <class PREDICATE>
void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType, uint32 offset, PREDICATE const& predicate)
③先预先处理一下目标列表
bool PrepareTargetListSelection(std::list<Unit*>& targetList, SelectTargetMethod targetType, uint32 offset);
④自制迭代器会根据距离、buff、是否带第一仇恨、是否是玩家过滤一遍预处理的列表
struct DefaultTargetSelector
⑤最后再过滤一遍目标列表
void FinalizeTargetListSelection(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType);
Unit* const _owner;
bool _ownerCanHaveThreatList;
--------------------------------
bool _needClientUpdate;
uint32 _updateTimer;
std::unique_ptr<Heap> _sortedThreatList;
std::unordered_map<ObjectGuid, ThreatReference*> _myThreatListEntries;
--------------------------------
std::vector<ObjectGuid> _needsAIUpdate;
--------------------------------
ThreatReference const* _currentVictimRef;
ThreatReference const* _fixateRef;
--------------------------------
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
--------------------------------
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)
class TC_GAME_API ThreatManager
{
public:
class Heap;
class ThreatListIterator;
static const uint32 THREAT_UPDATE_INTERVAL = 1000u;
static bool CanHaveThreatList(Unit const* who);
ThreatManager(Unit* owner);
~ThreatManager();
// called from ::Create methods just after construction (once all fields on owner have been populated)
// should not be called from anywhere else
void Initialize();
// called from Creature::Update (only creatures can have their own threat list)
// should not be called from anywhere else
void Update(uint32 tdiff);
// 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 current victim - this can be nullptr if owner's threat list is empty, or has only offline targets
Unit* GetCurrentVictim();
Unit* GetLastVictim() const;
// returns an arbitrary non-offline victim from owner's threat list if one exists, nullptr otherwise
Unit* GetAnyTarget() const;
// 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;
// fastest of the three threat list getters - gets the threat list in "arbitrary" order
// iterators will invalidate on adding/removing entries from the threat list; slightly less finicky than GetSorted.
Trinity::IteratorPair<ThreatListIterator, std::nullptr_t> GetUnsortedThreatList() const;
// slightly slower than GetUnsorted, but, well...sorted - only use it if you need the sorted property, of course
// this iterator pair will invalidate on any modification (even indirect) of the threat list; spell casts and similar can all induce this!
// note: current tank is NOT guaranteed to be the first entry in this list - check GetLastVictim separately if you want that!
Trinity::IteratorPair<ThreatListIterator, std::nullptr_t> GetSortedThreatList() const;
// slowest of the three threat list getters (by far), but lets you modify the threat references - this is also sorted
std::vector<ThreatReference*> GetModifiableThreatList();
// 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 its owner may now be suppressed on others' threat lists (immunity or damage-breakable CC being applied)
void EvaluateSuppressed(bool canExpire = false);
///== 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);
void ClearThreat(ThreatReference* ref);
// Removes all targets from the threat list (will cause evade in UpdateVictim if called)
void ClearAllThreat();
// Fixate on the passed target; this target will always be selected until the fixate is cleared
// (if the target is not in the threat list, does nothing)
void FixateTarget(Unit* target);
void ClearFixate() { FixateTarget(nullptr); }
Unit* GetFixateTarget() 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();
///== 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;
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 SendThreatListToClients(bool newHighest) const;
///== MY THREAT LIST ==
void PutThreatListRef(ObjectGuid const& guid, ThreatReference* ref);
void PurgeThreatListRef(ObjectGuid const& guid);
bool _needClientUpdate;
uint32 _updateTimer;
std::unique_ptr<Heap> _sortedThreatList;
std::unordered_map<ObjectGuid, ThreatReference*> _myThreatListEntries;
// AI notifies are delayed to ensure we are in a consistent state before we call out to arbitrary logic
// threat references might register themselves here when ::UpdateOffline() is called - MAKE SURE THIS IS PROCESSED JUST BEFORE YOU EXIT THREATMANAGER LOGIC
void ProcessAIUpdates();
void RegisterForAIUpdate(ObjectGuid const& guid) { _needsAIUpdate.push_back(guid); }
std::vector<ObjectGuid> _needsAIUpdate;
// picks a new victim - called from ::Update periodically
void UpdateVictim();
ThreatReference const* ReselectVictim();
ThreatReference const* _currentVictimRef;
ThreatReference const* _fixateRef;
///== 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)
public:
ThreatManager(ThreatManager const&) = delete;
ThreatManager& operator=(ThreatManager const&) = delete;
class ThreatListIterator
{
private:
std::function<ThreatReference const* ()> _generator;
ThreatReference const* _current;
friend ThreatManager;
explicit ThreatListIterator(std::function<ThreatReference const* ()>&& generator)
: _generator(std::move(generator)), _current(_generator()) {}
public:
ThreatReference const* operator*() const { return _current; }
ThreatReference const* operator->() const { return _current; }
ThreatListIterator& operator++() { _current = _generator(); return *this; }
bool operator==(ThreatListIterator const& o) const { return _current == o._current; }
bool operator!=(ThreatListIterator const& o) const { return _current != o._current; }
bool operator==(std::nullptr_t) const { return _current == nullptr; }
bool operator!=(std::nullptr_t) const { return _current != nullptr; }
};
friend class ThreatReference;
friend class ThreatReferenceImpl;
friend struct CompareThreatLessThan;
friend class debug_commandscript;
};
//在线状态
enum OnlineState {
ONLINE_STATE_ONLINE = 2, --在线
ONLINE_STATE_SUPPRESSED = 1, --被抑制状态
ONLINE_STATE_OFFLINE = 0 --离线
};
-------------------
enum TauntState : uint32 {
TAUNT_STATE_DETAUNT = 0, --减少嘲讽状态
TAUNT_STATE_NONE = 1, --普通状态
TAUNT_STATE_TAUNT = 2 --嘲讽状态
};
Creature* const _owner; //自己仇恨的列表,这个指向自己
ThreatManager& _mgr; //这个列表所属的仇恨列表管理类
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
TauntState _taunted;
class TC_GAME_API ThreatReference
{
public:
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 };
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; }
bool IsOnline() const { return (_online >= ONLINE_STATE_ONLINE); }
bool IsAvailable() const { return (_online > ONLINE_STATE_OFFLINE); }
bool IsSuppressed() const { return (_online == ONLINE_STATE_SUPPRESSED); }
bool IsOffline() const { return (_online <= ONLINE_STATE_OFFLINE); }
TauntState GetTauntState() const { return IsTaunting() ? TAUNT_STATE_TAUNT : _taunted; }
bool IsTaunting() const { return _taunted >= TAUNT_STATE_TAUNT; }
bool IsDetaunted() const { return _taunted == TAUNT_STATE_DETAUNT; }
void AddThreat(float amount);
void ScaleThreat(float factor);
void ModifyThreatByPercent(int32 percent) { if (percent) ScaleThreat(0.01f*float(100 + percent)); }
void UpdateOffline();
void ClearThreat(); // dealloc's this
protected:
static bool FlagsAllowFighting(Unit const* a, Unit const* b);
explicit ThreatReference(ThreatManager* mgr, Unit* victim) :
_owner(reinterpret_cast<Creature*>(mgr->_owner)), _mgr(*mgr), _victim(victim),
_baseAmount(0.0f), _tempModifier(0), _taunted(TAUNT_STATE_NONE)
{
_online = ONLINE_STATE_OFFLINE;
}
virtual ~ThreatReference() = default;
void UnregisterAndFree();
bool ShouldBeOffline() const;
bool ShouldBeSuppressed() const;
void UpdateTauntState(TauntState state = TAUNT_STATE_NONE);
Creature* const _owner;
ThreatManager& _mgr;
void HeapNotifyIncreased();
void HeapNotifyDecreased();
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
TauntState _taunted;
public:
ThreatReference(ThreatReference const&) = delete;
ThreatReference& operator=(ThreatReference const&) = delete;
friend class ThreatManager;
friend struct CompareThreatLessThan;
};